Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8a32faa4c | |||
| 0f09aaf285 | |||
| 45266db151 | |||
| 8f38794dd1 | |||
| b5076d5d7b | |||
| 2bacc0f1fb | |||
| 4b31169776 | |||
| f2a035c23c | |||
| 68f6aea6d7 | |||
| d6a7bdfb28 | |||
| 832df1ef65 | |||
| 0d97cc3e07 | |||
| 7671523547 | |||
| e109007a41 | |||
| e4b8f786f8 | |||
| 25c147aa7d | |||
| 31bf9bbb2b | |||
| 81c702959e | |||
| f45d89346d | |||
| 9b68685cd3 | |||
| d1c744b188 | |||
| 6f777a5f13 | |||
| a52de21d6f | |||
| b8d633d499 | |||
| 81206c63b3 | |||
| e18e622fa5 | |||
| f0c7550477 | |||
| f3a821ea95 | |||
| fd580cd2c5 | |||
| e528e2ba50 | |||
| ebb0fd83cb | |||
| 7d7d605bd2 | |||
| aa8d17268e | |||
| dca71560fe | |||
| 52bcad5911 | |||
| 6af2765508 | |||
| 2827595511 | |||
| d6730b01fc | |||
| 303eb7fb6a |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://twitter.com/SpeckleSystems) [](https://speckle.community) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
Plugin for Unreal Engine 4 to import objects from Speckle v2.
|
||||
Plugin for Unreal Engine to import objects from Speckle v2.
|
||||
|
||||
|
||||
Screencast of an example: https://user-images.githubusercontent.com/2551138/114720093-61403e00-9d40-11eb-8045-6e8ca656554d.mp4
|
||||
@@ -12,20 +12,21 @@ Screencast of an example: https://user-images.githubusercontent.com/2551138/1147
|
||||
|
||||
## NOTICE
|
||||
|
||||
* Tested on Windows, Unreal Engine v4.26 and Visual Studio Community 2019
|
||||
* Only displays meshes. Breps are converted using their display values.
|
||||
* Our Unreal Connector is in an beta stage of development. Currently we only support receiving objects, and there is no UI to fetch lists of streams/commits/branches.
|
||||
* We officially support Unreal Engine 4.26 and 4.27, and have experimental support for UE5.
|
||||
* Does not use the Speckle Kit workflow as conversions all happen in C++.
|
||||
|
||||
## How To Install
|
||||
|
||||
|
||||
1. Clone the repository or download it as a zip file.
|
||||
2. Navigate to `SpeckleUnrealProject` > `Plugins` and copy the `SpeckleUnreal` folder
|
||||
3. Paste the folder into your Unreal project under `YourUnrealProjectFolder` > `Plugins` (Create a `Plugins` folder if you don't already have one).
|
||||
4. Reopen your project.
|
||||
1. Download `speckle-unreal` repository archive from https://github.com/specklesystems/speckle-unreal/archive/refs/heads/main.zip
|
||||
1. Extract the downloaded archive into your project's `Plugins` directory (if your project directory doesn't contain a directory called Plugins, you should create it)
|
||||
1. Open your UE project (or restart the editor if you already have it opened). This will build the plugin in your environment.
|
||||
|
||||
We will eventually look to distributing the plugin officially on the Unreal Engine Marketplace but for now you'll need to install the plugin manually like this.
|
||||
|
||||
See our [docs](https://speckle.guide/user/unreal.html) for usage instructions.
|
||||
|
||||
## Credits
|
||||
Based off the original Unreal integration for Speckle v1 by Mark and Jak which can be found here: [https://github.com/mobiusnode/SpeckleUnreal](https://github.com/mobiusnode/SpeckleUnreal).
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "API/Operations/ReceiveOperation.h"
|
||||
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Transports/Transport.h"
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Mixpanel.h"
|
||||
|
||||
|
||||
UReceiveOperation* UReceiveOperation::ReceiveOperation(UObject* WorldContextObject, const FString& ObjectId, TScriptInterface<ITransport> RemoteTransport, TScriptInterface<ITransport> LocalTransport)
|
||||
{
|
||||
UReceiveOperation* Node = NewObject<UReceiveOperation>();
|
||||
Node->ObjectId = ObjectId;
|
||||
Node->RemoteTransport = RemoteTransport;
|
||||
Node->LocalTransport = LocalTransport;
|
||||
|
||||
Node->RegisterWithGameInstance(WorldContextObject);
|
||||
return Node;
|
||||
}
|
||||
|
||||
|
||||
void UReceiveOperation::Activate()
|
||||
{
|
||||
FAnalytics::TrackEvent("unknown", "unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }});
|
||||
|
||||
//Async(EAsyncExecution::Thread, [this]{Receive();});
|
||||
Receive();
|
||||
}
|
||||
|
||||
void UReceiveOperation::Receive()
|
||||
{
|
||||
check(LocalTransport != nullptr);
|
||||
|
||||
// 1. Try and get object from local transport
|
||||
auto Obj = LocalTransport->GetSpeckleObject(ObjectId);
|
||||
|
||||
if (Obj != nullptr )
|
||||
{
|
||||
HandleReceive(Obj);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Try and get object from remote transport
|
||||
if(RemoteTransport == nullptr)
|
||||
{
|
||||
FString ErrorMessage = TEXT("Could not find specified object using the local transport, and you didn't provide a fallback remote from which to pull it.");
|
||||
HandleError(ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
FTransportCopyObjectCompleteDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindUObject(this, &UReceiveOperation::HandleReceive);
|
||||
FTransportErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindUObject(this, &UReceiveOperation::HandleError);
|
||||
|
||||
RemoteTransport->CopyObjectAndChildren(ObjectId, LocalTransport, CompleteDelegate, ErrorDelegate);
|
||||
}
|
||||
|
||||
void UReceiveOperation::HandleReceive(TSharedPtr<FJsonObject> Object)
|
||||
{
|
||||
check(IsInGameThread())
|
||||
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
if(Object == nullptr)
|
||||
{
|
||||
OnError.Broadcast(nullptr, FString::Printf(TEXT("Failed to get object %s from transport"), *ObjectId));
|
||||
}
|
||||
else
|
||||
{
|
||||
UBase* Res = USpeckleSerializer::DeserializeBase(Object, LocalTransport);
|
||||
if(IsValid(Res))
|
||||
OnReceiveSuccessfully.Broadcast(Res, "");
|
||||
else
|
||||
OnError.Broadcast(nullptr, FString::Printf(TEXT("Root Speckle Object %s failed to deserialize"), *ObjectId));
|
||||
}
|
||||
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UReceiveOperation::HandleError(FString& Message)
|
||||
{
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
OnError.Broadcast(nullptr, Message);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "API/Operations/SendOperation.h"
|
||||
|
||||
#include "Mixpanel.h"
|
||||
#include "Transports/Transport.h"
|
||||
|
||||
|
||||
USendOperation* USendOperation::SendOperation(UObject* WorldContextObject, UBase* Base, TArray<TScriptInterface<ITransport>> Transports)
|
||||
{
|
||||
|
||||
USendOperation* Node = NewObject<USendOperation>();
|
||||
Node->Base = Base;
|
||||
Node->Transports = Transports;
|
||||
|
||||
Node->RegisterWithGameInstance(WorldContextObject);
|
||||
return Node;
|
||||
}
|
||||
|
||||
void USendOperation::Activate()
|
||||
{
|
||||
FAnalytics::TrackEvent("unknown", "unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }});
|
||||
Send();
|
||||
}
|
||||
|
||||
void USendOperation::Send()
|
||||
{
|
||||
check(Transports.Num() > 0);
|
||||
|
||||
for (const auto t : Transports)
|
||||
{
|
||||
//TODO
|
||||
//ITransport::
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "API/SpeckleSerializer.h"
|
||||
|
||||
#include "Objects/Base.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
#include "Objects/ObjectModelRegistry.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "Transports/Transport.h"
|
||||
#include "UObject/Package.h"
|
||||
|
||||
|
||||
UBase* USpeckleSerializer::DeserializeBase(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(Obj == nullptr) return nullptr;
|
||||
|
||||
{ // Handle Detached Objects
|
||||
TSharedPtr<FJsonObject> DetachedObject;
|
||||
if(USpeckleObjectUtils::ResolveReference(Obj, ReadTransport, DetachedObject))
|
||||
{
|
||||
return DeserializeBase(DetachedObject, ReadTransport);
|
||||
}
|
||||
}
|
||||
|
||||
FString SpeckleType;
|
||||
if (!Obj->TryGetStringField("speckle_type", SpeckleType)) return nullptr;
|
||||
FString ObjectId = "";
|
||||
Obj->TryGetStringField("id", ObjectId);
|
||||
|
||||
TSubclassOf<UBase> BaseType;
|
||||
|
||||
FString WorkingType(SpeckleType);
|
||||
|
||||
int32 Tries = 1000;
|
||||
while(ensure(Tries-- > 0))
|
||||
{
|
||||
//Try and deserialize
|
||||
if(UObjectModelRegistry::TryGetRegisteredType(WorkingType, BaseType))
|
||||
{
|
||||
UBase* Base = NewObject<UBase>(GetTransientPackage(), BaseType);
|
||||
if(Base->Parse(Obj, ReadTransport))
|
||||
return Base;
|
||||
}
|
||||
|
||||
//If we couldn't even deserialize this to a Base
|
||||
if(WorkingType == "Base" || BaseType == UBase::StaticClass())
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Skipping deserilization of %s id: %s - object could not be deserilaized to Base"), *SpeckleType, *ObjectId );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//Try the next type
|
||||
if(!UObjectModelRegistry::ParentType(WorkingType, WorkingType))
|
||||
{
|
||||
WorkingType = "Base";
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Unrecognised SpeckleType %s - Object id: %s Will be deserialized as Base"), *SpeckleType, *ObjectId );
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UBase* USpeckleSerializer::DeserializeBaseById(const FString& ObjectId,
|
||||
const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
auto Obj = ReadTransport->GetSpeckleObject(ObjectId);
|
||||
return DeserializeBase(Obj, ReadTransport);
|
||||
}
|
||||
|
||||
|
||||
UBase* TrySetProperties(const TSubclassOf<UBase> BaseType, const FString& SerialisedObject, TScriptInterface<ITransport> Transport)
|
||||
{
|
||||
UBase* Base = NewObject<UBase>(GetTransientPackage(), BaseType);
|
||||
|
||||
|
||||
TSharedRef<FJsonStringReader> Reader = FJsonStringReader::Create(SerialisedObject);
|
||||
for(auto foo : Reader.)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Base;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "API/SpeckleTypeConverter.h"
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "Mixpanel.h"
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "HttpModule.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "Launch/Resources/Version.h"
|
||||
#include "Misc/Base64.h"
|
||||
#include "Policies/CondensedJsonPrintPolicy.h"
|
||||
#include "Serialization/JsonSerializerMacros.h"
|
||||
#include "Serialization/JsonWriter.h"
|
||||
|
||||
const FString FAnalytics::MixpanelToken = TEXT("acd87c5a50b56df91a795e999812a3a4");
|
||||
const FString FAnalytics::MixpanelServer = TEXT("https://analytics.speckle.systems");
|
||||
const FString FAnalytics::VersionedApplicationName = FString::Printf(TEXT("Unreal Engine %d.%d"), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION);
|
||||
|
||||
void FAnalytics::TrackEvent(const FString& Email, const FString& Server, const FString& EventName)
|
||||
{
|
||||
const TMap<FString, FString> CustomProperties;
|
||||
TrackEvent(Email, Server, EventName, CustomProperties);
|
||||
}
|
||||
|
||||
void FAnalytics::TrackEvent(const FString& Email, const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties)
|
||||
{
|
||||
#if !SUPPRESS_SPECKLE_ANALYTICS
|
||||
|
||||
|
||||
FString HashedEmail = "@" + Hash(Email); //prepending an @ so we distinguish logged and non-logged users
|
||||
FString HashedServer = Hash(Server);
|
||||
|
||||
TMap<FString, FString> Properties
|
||||
{
|
||||
{ "distinct_id", HashedEmail },
|
||||
{ "server_id", HashedServer },
|
||||
{ "token", MixpanelToken },
|
||||
{ "hostApp", "Unreal Engine" },
|
||||
{ "hostAppVersion", VersionedApplicationName },
|
||||
{ "$os", *UGameplayStatics::GetPlatformName() },
|
||||
{ "type", "action" }
|
||||
};
|
||||
|
||||
|
||||
Properties.Append(CustomProperties);
|
||||
|
||||
FString Json;
|
||||
{
|
||||
auto Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&Json);
|
||||
FJsonSerializerWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> Serializer(Writer);
|
||||
Serializer.StartObject();
|
||||
FString EventNameString = EventName;
|
||||
Serializer.Serialize(TEXT("event"), EventNameString);
|
||||
Serializer.SerializeMap(TEXT("properties"), Properties);
|
||||
Serializer.EndObject();
|
||||
Writer->Close();
|
||||
}
|
||||
const FString Data = FBase64::Encode(Json);
|
||||
|
||||
// Create Request
|
||||
const FHttpRequestRef Request = FHttpModule::Get().CreateRequest();
|
||||
{
|
||||
Request->SetURL(MixpanelServer + "/track?ip=1&data=" + Data);
|
||||
Request->SetHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
Request->SetHeader("Accept", TEXT("text/plain"));
|
||||
Request->SetVerb("POST");
|
||||
}
|
||||
|
||||
Request->OnProcessRequestComplete().BindLambda([](FHttpRequestPtr, FHttpResponsePtr Response, bool)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Recieved Mixpanel resonse %d"), Response->GetResponseCode());
|
||||
});
|
||||
|
||||
const bool RequestSent = Request->ProcessRequest();
|
||||
if(!RequestSent)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Failed to send Mixpanel event to %s"), *MixpanelServer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FString FAnalytics::Hash(const FString& Input)
|
||||
{
|
||||
return FMD5::HashAnsiString(*Input.ToLower());
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Components/SpeckleOperationsComponent.h"
|
||||
|
||||
|
||||
// Sets default values for this component's properties
|
||||
USpeckleOperationsComponent::USpeckleOperationsComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "Conversion/Converters/AggregateConverter.h"
|
||||
|
||||
#include "LogSpeckle.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
|
||||
|
||||
void UAggregateConverter::OnConvertersChangeHandler()
|
||||
{
|
||||
SpeckleTypeMap.Empty();
|
||||
|
||||
for(int i = 0; i < SpeckleConverters.Num(); i++)
|
||||
{
|
||||
const UObject* Converter = SpeckleConverters[i];
|
||||
if(Converter != nullptr && !Converter->GetClass()->ImplementsInterface(USpeckleConverter::StaticClass()))
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface %s"), *Converter->GetClass()->GetName(), *USpeckleConverter::StaticClass()->GetName())
|
||||
SpeckleConverters.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UAggregateConverter::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UAggregateConverter, SpeckleConverters))
|
||||
{
|
||||
OnConvertersChangeHandler();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void UAggregateConverter::ConvertToSpeckle_Implementation(const UObject* Object, UBase* SpeckleObject)
|
||||
{
|
||||
//TODO implement ToSpeckle
|
||||
unimplemented();
|
||||
}
|
||||
|
||||
|
||||
UObject* UAggregateConverter::ConvertToNative_Implementation(const UBase* Object, UWorld* World, TScriptInterface<ISpeckleConverter>& )
|
||||
{
|
||||
return ConvertToNativeInternal(Object, World);
|
||||
}
|
||||
|
||||
UObject* UAggregateConverter::ConvertToNativeInternal(const UBase* Object, UWorld* World)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
if(!IsValid(Object)) return nullptr;
|
||||
|
||||
const TSubclassOf<UBase> Type = Object->GetClass();
|
||||
UObject* Converter = GetConverter(Type).GetObject();
|
||||
if(!IsValid(Converter))
|
||||
{
|
||||
if(Type != UBase::StaticClass())
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Skipping Object %s: No conversion functions exist for %s"), *Object->Id, *Type->GetName());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Converting object of type: %s id: %s"), *Type->GetName(), *Object->Id);
|
||||
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
|
||||
TScriptInterface<ISpeckleConverter> MainConverter = this;
|
||||
|
||||
check(Converter->IsValidLowLevel());
|
||||
return Execute_ConvertToNative(Converter, Object, World, MainConverter);
|
||||
|
||||
}
|
||||
|
||||
bool UAggregateConverter::CanConvertToNative_Implementation(TSubclassOf<UBase> BaseType)
|
||||
{
|
||||
return GetConverter(BaseType).GetInterface() != nullptr;
|
||||
}
|
||||
|
||||
|
||||
TScriptInterface<ISpeckleConverter> UAggregateConverter::GetConverter(const TSubclassOf<UBase> BaseType)
|
||||
{
|
||||
// Check if this SpeckleType has a known converter.
|
||||
if(SpeckleTypeMap.Contains(BaseType))
|
||||
{
|
||||
return SpeckleTypeMap[BaseType];
|
||||
}
|
||||
|
||||
// Try and find one that can convert this SpeckleType.
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
for(UObject* Converter : SpeckleConverters)
|
||||
{
|
||||
if(!CheckValidConverter(Converter)) continue;
|
||||
|
||||
if(Execute_CanConvertToNative(Converter, BaseType))
|
||||
{
|
||||
//Found a Converter! Save this mapping for next time.
|
||||
SpeckleTypeMap.Add(BaseType, Converter);
|
||||
return Converter;
|
||||
}
|
||||
}
|
||||
|
||||
// SpeckleType has no conversions.
|
||||
SpeckleTypeMap.Add(BaseType, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UAggregateConverter::FinishConversion_Implementation()
|
||||
{
|
||||
FinishConversion_Internal();
|
||||
}
|
||||
|
||||
void UAggregateConverter::FinishConversion_Internal()
|
||||
{
|
||||
for (UObject* Converter : SpeckleConverters)
|
||||
{
|
||||
if(!CheckValidConverter(Converter)) continue;
|
||||
|
||||
Execute_FinishConversion(Converter);
|
||||
}
|
||||
OnConvertersChangeHandler();
|
||||
}
|
||||
|
||||
bool UAggregateConverter::CheckValidConverter(const UObject* Converter, bool LogWarning)
|
||||
{
|
||||
if(Converter == nullptr) return false;
|
||||
|
||||
if(Converter->Implements<USpeckleConverter>()) return true;
|
||||
|
||||
if(LogWarning)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface {%s}"), *Converter->GetClass()->GetName(), *USpeckleConverter::StaticClass()->GetName())
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "Conversion/Converters/BlockConverter.h"
|
||||
|
||||
#include "Objects/Other/BlockInstance.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
UBlockConverter::UBlockConverter()
|
||||
{
|
||||
SpeckleTypes.Add(UBlockInstance::StaticClass());
|
||||
|
||||
BlockInstanceActorType = AActor::StaticClass();
|
||||
ActorMobility = EComponentMobility::Static;
|
||||
}
|
||||
|
||||
UObject* UBlockConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>&)
|
||||
{
|
||||
const UBlockInstance* Block = Cast<UBlockInstance>(SpeckleBase);
|
||||
if(Block == nullptr) return nullptr;
|
||||
|
||||
return BlockToNative(Block, World);
|
||||
}
|
||||
|
||||
AActor* UBlockConverter::BlockToNative(const UBlockInstance* Block, UWorld* World)
|
||||
{
|
||||
AActor* BlockActor = CreateEmptyActor(World, USpeckleObjectUtils::CreateTransform(Block->Transform));
|
||||
//Return the block actor as is,
|
||||
//Other converter logic will convert child geometries because UBlockInstance intentionally left them as dynamic properties
|
||||
return BlockActor;
|
||||
}
|
||||
|
||||
AActor* UBlockConverter::CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
|
||||
{
|
||||
AActor* Actor = World->SpawnActor<AActor>(BlockInstanceActorType, Transform, SpawnParameters);
|
||||
|
||||
if(!Actor->HasValidRootComponent())
|
||||
{
|
||||
USceneComponent* Scene = NewObject<USceneComponent>(Actor, "Root");
|
||||
Scene->SetRelativeTransform(Transform);
|
||||
Actor->SetRootComponent(Scene);
|
||||
Scene->RegisterComponent();
|
||||
}
|
||||
USceneComponent* RootComponent = Actor->GetRootComponent();
|
||||
|
||||
RootComponent->SetMobility(ActorMobility);
|
||||
|
||||
return Actor;
|
||||
}
|
||||
|
||||
UBase* UBlockConverter::ConvertToSpeckle_Implementation(const UObject* Object)
|
||||
{
|
||||
unimplemented();
|
||||
return nullptr; //TODO implement
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Conversion/Converters/MaterialConverter.h"
|
||||
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "Objects/Other/RenderMaterial.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
|
||||
UMaterialConverter::UMaterialConverter()
|
||||
{
|
||||
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleMaterial(TEXT("Material'/SpeckleUnreal/SpeckleMaterial.SpeckleMaterial'"));
|
||||
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleGlassMaterial(TEXT("Material'/SpeckleUnreal/SpeckleGlassMaterial.SpeckleGlassMaterial'"));
|
||||
|
||||
DefaultMeshMaterial = SpeckleMaterial.Object;
|
||||
BaseMeshOpaqueMaterial = SpeckleMaterial.Object;
|
||||
BaseMeshTransparentMaterial = SpeckleGlassMaterial.Object;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UseConstMaterials = NotPlay;
|
||||
#endif
|
||||
|
||||
SpeckleTypes.Add(URenderMaterial::StaticClass());
|
||||
}
|
||||
|
||||
UObject* UMaterialConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld*, TScriptInterface<ISpeckleConverter>&)
|
||||
{
|
||||
const URenderMaterial* m = Cast<URenderMaterial>(SpeckleBase);
|
||||
|
||||
if(m == nullptr) return DefaultMeshMaterial;
|
||||
|
||||
return GetMaterial(m);
|
||||
}
|
||||
|
||||
UMaterialInterface* UMaterialConverter::GetMaterial(const URenderMaterial* SpeckleMaterial)
|
||||
{
|
||||
if(SpeckleMaterial == nullptr || SpeckleMaterial->Id == "") return DefaultMeshMaterial; //Material is invalid
|
||||
|
||||
// 1. Check Overrides
|
||||
UMaterialInterface* NativeMaterial;
|
||||
if(TryGetOverride(SpeckleMaterial, NativeMaterial))
|
||||
return NativeMaterial;
|
||||
|
||||
// 2. Check transient cache
|
||||
if(ConvertedMaterials.Contains(SpeckleMaterial->Id))
|
||||
{
|
||||
return ConvertedMaterials[SpeckleMaterial->Id];
|
||||
}
|
||||
|
||||
// 3. Check Assets
|
||||
UPackage* Package = GetPackage(SpeckleMaterial->Id);
|
||||
|
||||
NativeMaterial = Cast<UMaterialInterface>(Package->FindAssetInPackage());
|
||||
if(IsValid(NativeMaterial))
|
||||
{
|
||||
return NativeMaterial;
|
||||
}
|
||||
|
||||
// 4. Convert
|
||||
return RenderMaterialToNative(SpeckleMaterial, Package);
|
||||
}
|
||||
|
||||
bool UMaterialConverter::TryGetOverride(const URenderMaterial* SpeckleMaterial, UMaterialInterface*& OutMaterial) const
|
||||
{
|
||||
const auto MaterialID = SpeckleMaterial->Id;
|
||||
|
||||
|
||||
//Override by id
|
||||
if(MaterialOverridesById.Contains(MaterialID))
|
||||
{
|
||||
OutMaterial = MaterialOverridesById[MaterialID];
|
||||
return true;
|
||||
}
|
||||
//Override by name
|
||||
const FString Name = SpeckleMaterial->Name;
|
||||
for (const UMaterialInterface* Mat : MaterialOverridesByName)
|
||||
{
|
||||
if(ensureAlways(IsValid(Mat)) && Mat->GetName() == Name)
|
||||
{
|
||||
OutMaterial = MaterialOverridesById[MaterialID];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FString UMaterialConverter::RemoveInvalidFileChars(const FString& InString) const
|
||||
{
|
||||
return FPaths::MakeValidFileName(InString.Replace(TEXT("."), TEXT("_"), ESearchCase::CaseSensitive));
|
||||
}
|
||||
|
||||
UMaterialInterface* UMaterialConverter::RenderMaterialToNative(const URenderMaterial* SpeckleMaterial, UPackage* Package)
|
||||
{
|
||||
UMaterialInterface* MaterialBase = SpeckleMaterial->Opacity >= 1
|
||||
? BaseMeshOpaqueMaterial
|
||||
: BaseMeshTransparentMaterial;
|
||||
|
||||
UMaterialInstance* MaterialInstance;
|
||||
#if WITH_EDITOR
|
||||
if (ShouldCreateConstMaterial(UseConstMaterials))
|
||||
{
|
||||
const FName Name = MakeUniqueObjectName(Package, UMaterialInstanceConstant::StaticClass(), *RemoveInvalidFileChars(SpeckleMaterial->Name));
|
||||
|
||||
//TStrongObjectPtr< UMaterialInstanceConstantFactoryNew > MaterialFact( NewObject< UMaterialInstanceConstantFactoryNew >() );
|
||||
//MaterialFact->InitialParent = MaterialBase;
|
||||
//UMaterialInstanceConstant* ConstMaterial = Cast< UMaterialInstanceConstant >( MaterialFact->FactoryCreateNew( UMaterialInstanceConstant::StaticClass(), Package, Name, RF_Public, nullptr, GWarn ) );
|
||||
UMaterialInstanceConstant* ConstMaterial = NewObject<UMaterialInstanceConstant>(Package, Name, RF_Public | RF_Standalone);
|
||||
|
||||
MaterialInstance = ConstMaterial;
|
||||
ConstMaterial->SetParentEditorOnly(MaterialBase);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Opacity"), SpeckleMaterial->Opacity);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Metallic"), SpeckleMaterial->Metalness);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Roughness"), SpeckleMaterial->Roughness);
|
||||
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("BaseColor"), SpeckleMaterial->Diffuse);
|
||||
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("EmissiveColor"), SpeckleMaterial->Emissive);
|
||||
|
||||
//ConstMaterial->InitStaticPermutation();
|
||||
|
||||
ConstMaterial->MarkPackageDirty();
|
||||
|
||||
FAssetRegistryModule::AssetCreated(MaterialInstance);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(MaterialBase, Package, FName(SpeckleMaterial->Name));
|
||||
MaterialInstance = DynMaterial;
|
||||
|
||||
DynMaterial->SetScalarParameterValue("Opacity", SpeckleMaterial->Opacity);
|
||||
DynMaterial->SetScalarParameterValue("Metallic", SpeckleMaterial->Metalness);
|
||||
DynMaterial->SetScalarParameterValue("Roughness", SpeckleMaterial->Roughness);
|
||||
DynMaterial->SetVectorParameterValue("BaseColor", SpeckleMaterial->Diffuse);
|
||||
DynMaterial->SetVectorParameterValue("EmissiveColor", SpeckleMaterial->Emissive);
|
||||
|
||||
DynMaterial->SetFlags(RF_Public);
|
||||
}
|
||||
|
||||
ConvertedMaterials.Add(SpeckleMaterial->Id, MaterialInstance);
|
||||
|
||||
return MaterialInstance;
|
||||
|
||||
}
|
||||
|
||||
void UMaterialConverter::FinishConversion_Implementation()
|
||||
{
|
||||
ConvertedMaterials.Empty();
|
||||
}
|
||||
|
||||
UPackage* UMaterialConverter::GetPackage(const FString& ObjectID ) const
|
||||
{
|
||||
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle/Materials"), ObjectID);
|
||||
return CreatePackage(*PackagePath);
|
||||
}
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
bool UMaterialConverter::ShouldCreateConstMaterial(TEnumAsByte<EConstMaterialOptions> Options)
|
||||
{
|
||||
if(!GIsEditor) return false;
|
||||
|
||||
switch(Options)
|
||||
{
|
||||
case Never:
|
||||
return false;
|
||||
case NotPlay:
|
||||
return !FApp::IsGame();
|
||||
case Always:
|
||||
return true;
|
||||
default:
|
||||
unimplemented();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -5,28 +5,29 @@
|
||||
|
||||
#include "LidarPointCloudActor.h"
|
||||
#include "LidarPointCloudComponent.h"
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "Objects/PointCloud.h"
|
||||
#include "Objects/Geometry/PointCloud.h"
|
||||
|
||||
|
||||
UPointCloudConverter::UPointCloudConverter()
|
||||
{
|
||||
SpeckleTypes.Add(UPointCloud::StaticClass());
|
||||
|
||||
PointCloudActorType = ALidarPointCloudActor::StaticClass();
|
||||
ActorMobility = EComponentMobility::Static;
|
||||
}
|
||||
|
||||
|
||||
AActor* UPointCloudConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
|
||||
UObject* UPointCloudConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>&)
|
||||
{
|
||||
const UPointCloud* P = Cast<UPointCloud>(SpeckleBase);
|
||||
const UPointCloud* p = Cast<UPointCloud>(SpeckleBase);
|
||||
|
||||
if(P == nullptr) return nullptr;
|
||||
if(p == nullptr) return nullptr;
|
||||
|
||||
return PointCloudToNative(P, Manager);
|
||||
return PointCloudToNative(p, World);
|
||||
}
|
||||
|
||||
|
||||
ALidarPointCloudActor* UPointCloudConverter::PointCloudToNative(const UPointCloud* SpecklePointCloud, ASpeckleUnrealManager* Manager)
|
||||
ALidarPointCloudActor* UPointCloudConverter::PointCloudToNative(const UPointCloud* SpecklePointCloud, UWorld* World)
|
||||
{
|
||||
TArray<FLidarPointCloudPoint> LidarPoints;
|
||||
|
||||
@@ -35,7 +36,13 @@ ALidarPointCloudActor* UPointCloudConverter::PointCloudToNative(const UPointClou
|
||||
for(int i = 0; i < SpecklePointCloud->Points.Num(); i++)
|
||||
{
|
||||
FColor c = SpecklePointCloud->Colors.Num() > i? SpecklePointCloud->Colors[i] : FColor::White;
|
||||
FLidarPointCloudPoint p = FLidarPointCloudPoint(SpecklePointCloud->Points[i], c, true, 0);
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
FVector3f Point = FVector3f(SpecklePointCloud->Points[i]);
|
||||
#else
|
||||
FVector Point = SpecklePointCloud->Points[i];
|
||||
#endif
|
||||
|
||||
FLidarPointCloudPoint p(Point, c, true, 0);
|
||||
LidarPoints.Add(p);
|
||||
}
|
||||
|
||||
@@ -48,20 +55,21 @@ ALidarPointCloudActor* UPointCloudConverter::PointCloudToNative(const UPointClou
|
||||
PointCloud->CenterPoints();
|
||||
PointCloud->RefreshBounds();
|
||||
|
||||
return CreateActor(Manager, PointCloud);
|
||||
return CreateActor(World, PointCloud);
|
||||
|
||||
}
|
||||
|
||||
|
||||
ALidarPointCloudActor* UPointCloudConverter::CreateActor(const ASpeckleUnrealManager* Manager, ULidarPointCloud* PointCloudData)
|
||||
ALidarPointCloudActor* UPointCloudConverter::CreateActor(UWorld* World, ULidarPointCloud* PointCloudData)
|
||||
{
|
||||
ALidarPointCloudActor* Actor = Manager->GetWorld()->SpawnActor<ALidarPointCloudActor>(PointCloudActorType);
|
||||
ALidarPointCloudActor* Actor = World->SpawnActor<ALidarPointCloudActor>(PointCloudActorType);
|
||||
Actor->SetPointCloud(PointCloudData);
|
||||
Actor->GetRootComponent()->SetMobility(ActorMobility);
|
||||
return Actor;
|
||||
}
|
||||
|
||||
|
||||
UBase* UPointCloudConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
|
||||
UBase* UPointCloudConverter::ConvertToSpeckle_Implementation(const UObject* Object)
|
||||
{
|
||||
const ULidarPointCloudComponent* P = Cast<ULidarPointCloudComponent>(Object);
|
||||
|
||||
@@ -75,11 +83,11 @@ UBase* UPointCloudConverter::ConvertToSpeckle_Implementation(const UObject* Obje
|
||||
}
|
||||
if(P == nullptr) return nullptr;
|
||||
|
||||
return PointCloudToSpeckle(P, Manager);
|
||||
return PointCloudToSpeckle(P);
|
||||
}
|
||||
|
||||
|
||||
UPointCloud* UPointCloudConverter::PointCloudToSpeckle(const ULidarPointCloudComponent* Object, ASpeckleUnrealManager* Manager)
|
||||
UPointCloud* UPointCloudConverter::PointCloudToSpeckle(const ULidarPointCloudComponent* Object)
|
||||
{
|
||||
return nullptr; //TODO implement ToSpeckle function
|
||||
}
|
||||
|
||||
@@ -3,29 +3,53 @@
|
||||
#include "Conversion/Converters/ProceduralMeshConverter.h"
|
||||
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "StaticMeshDescription.h"
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "Objects/Mesh.h"
|
||||
#include "Objects/RenderMaterial.h"
|
||||
#include "Conversion/Converters/MaterialConverter.h"
|
||||
#include "Materials/MaterialInstance.h"
|
||||
#include "Objects/DisplayValueElement.h"
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
#include "Objects/Other/RenderMaterial.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
UProceduralMeshConverter::UProceduralMeshConverter()
|
||||
{
|
||||
SpeckleTypes.Add(UMesh::StaticClass());
|
||||
SpeckleTypes.Add(UDisplayValueElement::StaticClass());
|
||||
|
||||
MeshActorType = AActor::StaticClass();
|
||||
ActorMobility = EComponentMobility::Static;
|
||||
}
|
||||
|
||||
AActor* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
|
||||
UObject* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters )
|
||||
{
|
||||
const UMesh* P = Cast<UMesh>(SpeckleBase);
|
||||
|
||||
if(P == nullptr) return nullptr;
|
||||
|
||||
return MeshToNative(P, Manager);
|
||||
const UMesh* m = Cast<UMesh>(SpeckleBase);
|
||||
if(m != nullptr)
|
||||
return MeshToNative(m, World, AvailableConverters);
|
||||
|
||||
const UDisplayValueElement* d = Cast<UDisplayValueElement>(SpeckleBase);
|
||||
if(d)
|
||||
{
|
||||
AActor* Parent = CreateEmptyActor(World, FTransform());
|
||||
for(const UMesh* Child : d->DisplayValue)
|
||||
{
|
||||
AActor* ChildActor = MeshToNative(Child, World, AvailableConverters);
|
||||
if(IsValid(ChildActor))
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
ChildActor->SetActorLabel(FString::Printf(TEXT("%s - %s"), *Child->SpeckleType, *Child->Id));
|
||||
#endif
|
||||
ChildActor->GetRootComponent()->SetMobility(ActorMobility);
|
||||
ChildActor->AttachToActor(Parent, FAttachmentTransformRules::KeepRelativeTransform);
|
||||
ChildActor->SetOwner(Parent);
|
||||
}
|
||||
}
|
||||
return Parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager)
|
||||
AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
{
|
||||
AActor* MeshActor = CreateActor(Manager, FTransform(SpeckleMesh->Transform));
|
||||
AActor* MeshActor = CreateEmptyActor(World, USpeckleObjectUtils::CreateTransform(SpeckleMesh->Transform));
|
||||
UProceduralMeshComponent* MeshComponent = NewObject<UProceduralMeshComponent>(MeshActor, FName("SpeckleMeshComponent"));
|
||||
MeshComponent->SetupAttachment(MeshActor->GetRootComponent());
|
||||
MeshComponent->RegisterComponent();
|
||||
@@ -74,49 +98,27 @@ AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh, ASpeckl
|
||||
SpeckleMesh->VertexColors,
|
||||
Tangents,
|
||||
true);
|
||||
|
||||
UMaterialInterface* Material = Cast<UMaterialInstance>(Execute_ConvertToNative(MaterialConverter.GetObject(), SpeckleMesh->RenderMaterial, World, MaterialConverter));
|
||||
ensure(Material != nullptr);
|
||||
|
||||
MeshComponent->SetMaterial(0, GetMaterial(SpeckleMesh->RenderMaterial, Manager));
|
||||
MeshComponent->SetMaterial(0, Material);
|
||||
|
||||
return MeshActor;
|
||||
}
|
||||
|
||||
AActor* UProceduralMeshConverter::CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
|
||||
AActor* UProceduralMeshConverter::CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
|
||||
{
|
||||
AActor* Actor = Manager->GetWorld()->SpawnActor<AActor>(MeshActorType, Transform, SpawnParameters);
|
||||
AActor* Actor = World->SpawnActor<AActor>(MeshActorType, Transform, SpawnParameters);
|
||||
USceneComponent* Scene = NewObject<USceneComponent>(Actor, "Root");
|
||||
Actor->SetRootComponent(Scene);
|
||||
Scene->RegisterComponent();
|
||||
Scene->SetMobility(ActorMobility);
|
||||
return Actor;
|
||||
}
|
||||
|
||||
|
||||
UMaterialInterface* UProceduralMeshConverter::GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager)
|
||||
{
|
||||
UMaterialInterface* ExistingMaterial;
|
||||
if(Manager->TryGetMaterial(SpeckleMaterial, true, ExistingMaterial))
|
||||
return ExistingMaterial; //Return existing material
|
||||
|
||||
UMaterialInterface* MaterialBase = SpeckleMaterial->Opacity >= 1
|
||||
? Manager->BaseMeshOpaqueMaterial
|
||||
: Manager->BaseMeshTransparentMaterial;
|
||||
|
||||
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(MaterialBase, Manager, FName(SpeckleMaterial->Name));
|
||||
|
||||
DynMaterial->SetFlags(RF_Public);
|
||||
|
||||
DynMaterial->SetScalarParameterValue("Opacity", SpeckleMaterial->Opacity);
|
||||
DynMaterial->SetScalarParameterValue("Metallic", SpeckleMaterial->Metalness);
|
||||
DynMaterial->SetScalarParameterValue("Roughness", SpeckleMaterial->Roughness);
|
||||
DynMaterial->SetVectorParameterValue("BaseColor", SpeckleMaterial->Diffuse);
|
||||
DynMaterial->SetVectorParameterValue("EmissiveColor", SpeckleMaterial->Emissive);
|
||||
|
||||
Manager->ConvertedMaterials.Add(SpeckleMaterial->Id, DynMaterial);
|
||||
|
||||
return DynMaterial;
|
||||
}
|
||||
|
||||
|
||||
UBase* UProceduralMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
|
||||
UBase* UProceduralMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object)
|
||||
{
|
||||
const UProceduralMeshComponent* M = Cast<UProceduralMeshComponent>(Object);
|
||||
|
||||
@@ -130,11 +132,11 @@ UBase* UProceduralMeshConverter::ConvertToSpeckle_Implementation(const UObject*
|
||||
}
|
||||
if(M == nullptr) return nullptr;
|
||||
|
||||
return MeshToSpeckle(M, Manager);
|
||||
return MeshToSpeckle(M);
|
||||
}
|
||||
|
||||
|
||||
UMesh* UProceduralMeshConverter::MeshToSpeckle(const UProceduralMeshComponent* Object, ASpeckleUnrealManager* Manager)
|
||||
UMesh* UProceduralMeshConverter::MeshToSpeckle(const UProceduralMeshComponent* Object)
|
||||
{
|
||||
return nullptr; //TODO implement ToSpeckle function
|
||||
}
|
||||
|
||||
@@ -5,39 +5,84 @@
|
||||
#include "MeshDescriptionBase.h"
|
||||
#include "StaticMeshDescription.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "StaticMeshOperations.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Engine/StaticMeshActor.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Objects/Mesh.h"
|
||||
#include "Objects/RenderMaterial.h"
|
||||
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Conversion/Converters/MaterialConverter.h"
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
#include "Objects/DisplayValueElement.h"
|
||||
#include "Objects/Other/RenderMaterial.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
|
||||
UStaticMeshConverter::UStaticMeshConverter()
|
||||
{
|
||||
Transient = false;
|
||||
UseFullBuild = true;
|
||||
BuildSimpleCollision = true;
|
||||
|
||||
MeshActorType = AStaticMeshActor::StaticClass();
|
||||
SpeckleTypes.Add(UMesh::StaticClass());
|
||||
SpeckleTypes.Add(UDisplayValueElement::StaticClass());
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UseFullBuild = true;
|
||||
DisplayBuildProgressBar = true;
|
||||
AllowCancelBuild = false;
|
||||
#endif
|
||||
Transient = false;
|
||||
BuildSimpleCollision = true;
|
||||
|
||||
BuildReversedIndexBuffer = true;
|
||||
UseFullPrecisionUVs = false;
|
||||
RemoveDegeneratesOnBuild = true;
|
||||
MinLightmapResolution = 64;
|
||||
|
||||
MeshActorType = AStaticMeshActor::StaticClass();
|
||||
ActorMobility = EComponentMobility::Static;
|
||||
}
|
||||
|
||||
AActor* UStaticMeshConverter::CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
|
||||
AActor* UStaticMeshConverter::CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
|
||||
{
|
||||
AActor* Actor = Manager->GetWorld()->SpawnActor<AActor>(AStaticMeshActor::StaticClass(), Transform, SpawnParameters);
|
||||
AActor* Actor = World->SpawnActor<AActor>(MeshActorType, Transform, SpawnParameters);
|
||||
if(Actor->HasValidRootComponent())
|
||||
Actor->GetRootComponent()->SetMobility(EComponentMobility::Movable); //Create actor as movable for now, we will change it later to the desired mobility
|
||||
return Actor;
|
||||
}
|
||||
|
||||
AActor* UStaticMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
|
||||
UObject* UStaticMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters)
|
||||
{
|
||||
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle"), Manager->StreamID, TEXT("Geometry"), SpeckleBase->Id);
|
||||
UPackage* Package = CreatePackage(*PackagePath);
|
||||
const UMesh* m = Cast<UMesh>(SpeckleBase);
|
||||
if(m != nullptr)
|
||||
{
|
||||
//Handle Single Mesh
|
||||
return MeshToNativeActor(m, World, AvailableConverters);
|
||||
}
|
||||
|
||||
const UDisplayValueElement* d = Cast<UDisplayValueElement>(SpeckleBase);
|
||||
if(d != nullptr)
|
||||
{
|
||||
//Handle Element with Display Values
|
||||
return MeshesToNativeActor(d, d->DisplayValue, World, AvailableConverters);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const UMesh* SpeckleMesh = Cast<UMesh>(SpeckleBase);
|
||||
if(SpeckleMesh == nullptr) return nullptr;
|
||||
|
||||
AActor* UStaticMeshConverter::MeshToNativeActor(const UMesh* SpeckleMesh, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
{
|
||||
TArray<UMesh*> Meshes = {const_cast<UMesh*>(SpeckleMesh)};
|
||||
return MeshesToNativeActor(SpeckleMesh, Meshes, World, MaterialConverter);
|
||||
}
|
||||
|
||||
AActor* UStaticMeshConverter::MeshesToNativeActor(const UBase* Parent, const TArray<UMesh*>& SpeckleMeshes, UWorld* World, TScriptInterface<ISpeckleConverter>& RenderMaterialConverter)
|
||||
{
|
||||
check(RenderMaterialConverter.GetInterface() != nullptr);
|
||||
ensureMsgf(Execute_CanConvertToNative(RenderMaterialConverter.GetObject(), URenderMaterial::StaticClass()), TEXT("StaticMeshConverter expects a valid RenderMaterial converter to be avaiable"));
|
||||
|
||||
|
||||
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle/Geometry"), Parent->Id);
|
||||
UPackage* Package = CreatePackage(*PackagePath);
|
||||
|
||||
//Find existing mesh
|
||||
UStaticMesh* Mesh = Cast<UStaticMesh>(Package->FindAssetInPackage());
|
||||
@@ -45,10 +90,18 @@ AActor* UStaticMeshConverter::ConvertToNative_Implementation(const UBase* Speckl
|
||||
if(!IsValid(Mesh))
|
||||
{
|
||||
//No existing mesh was found, try and convert SpeckleMesh
|
||||
Mesh = MeshToNative(Package, SpeckleMesh, Manager);
|
||||
Mesh = MeshesToNativeMesh(Package, Parent, SpeckleMeshes, RenderMaterialConverter);
|
||||
}
|
||||
|
||||
FMatrix Transform = FMatrix::Identity;
|
||||
// For single mesh, we check for transform
|
||||
//TODO figure out how to handle DisplayValueElement with transform. Maybe we just grab transform from parent unconditionally? How does this affect blocks?
|
||||
if(Parent == SpeckleMeshes[0])
|
||||
{
|
||||
Transform = SpeckleMeshes[0]->Transform;
|
||||
}
|
||||
|
||||
AActor* Actor = CreateActor(Manager, FTransform(SpeckleMesh->Transform));
|
||||
AActor* Actor = CreateEmptyActor(World, USpeckleObjectUtils::CreateTransform(Transform));
|
||||
TInlineComponentArray<UStaticMeshComponent*> Components;
|
||||
Actor->GetComponents<UStaticMeshComponent>(Components);
|
||||
|
||||
@@ -61,19 +114,70 @@ AActor* UStaticMeshConverter::ConvertToNative_Implementation(const UBase* Speckl
|
||||
MeshComponent->SetupAttachment(Actor->GetRootComponent());
|
||||
MeshComponent->RegisterComponent();
|
||||
}
|
||||
|
||||
|
||||
MeshComponent->SetStaticMesh(Mesh);
|
||||
MeshComponent->SetMaterial(0, GetMaterial(SpeckleMesh->RenderMaterial, Manager));
|
||||
|
||||
int i = 0;
|
||||
for(const UMesh* DisplayMesh : SpeckleMeshes)
|
||||
{
|
||||
URenderMaterial* MaterialToConvert = DisplayMesh->RenderMaterial;
|
||||
if(!MaterialToConvert)
|
||||
{
|
||||
//Try and grab a material from the parent
|
||||
const auto* MaterialProperty = Parent->DynamicProperties.Find("renderMaterial");
|
||||
const TSharedPtr<FJsonObject>* MaterialObject;
|
||||
if(MaterialProperty && (*MaterialProperty)->TryGetObject(MaterialObject))
|
||||
{
|
||||
//TODO giving a nullptr transport is pretty unsafe and relies on the deserializer not to have to dereference a detached property.
|
||||
MaterialToConvert = Cast<URenderMaterial>(USpeckleSerializer::DeserializeBase(*MaterialObject, nullptr));
|
||||
}
|
||||
}
|
||||
UMaterialInterface* Material = GetMaterial(MaterialToConvert, World, RenderMaterialConverter);
|
||||
|
||||
ensure(IsValid(Material));
|
||||
MeshComponent->SetMaterial(i, Material);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(Actor->HasValidRootComponent())
|
||||
Actor->GetRootComponent()->SetMobility(ActorMobility);
|
||||
|
||||
return Actor;
|
||||
}
|
||||
|
||||
UStaticMesh* UStaticMeshConverter::MeshToNative(UObject* Outer, const UMesh* SpeckleMesh,
|
||||
ASpeckleUnrealManager* Manager)
|
||||
|
||||
UMaterialInterface* UStaticMeshConverter::GetMaterial(const URenderMaterial* SpeckleMaterial, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter) const
|
||||
{
|
||||
if(!SpeckleMaterial)
|
||||
{
|
||||
// Create a fake render material, (since nullptr doesn't have a type, speckle converters don't know how to convert it)
|
||||
// If the converter wants to handle this specially, The material has an empty Id
|
||||
SpeckleMaterial = NewObject<URenderMaterial>(GetTransientPackage(), "NullSpeckleMaterial");
|
||||
}
|
||||
return Cast<UMaterialInterface>(Execute_ConvertToNative(MaterialConverter.GetObject(), SpeckleMaterial, World, MaterialConverter));
|
||||
}
|
||||
|
||||
|
||||
UStaticMesh* UStaticMeshConverter::MeshToNativeMesh(UObject* Outer, const UMesh* SpeckleMesh, TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
{
|
||||
TArray<UMesh*> Meshes;
|
||||
Meshes.Add(const_cast<UMesh*>(SpeckleMesh));
|
||||
return MeshesToNativeMesh(Outer, SpeckleMesh, Meshes, MaterialConverter);
|
||||
}
|
||||
|
||||
|
||||
UStaticMesh* UStaticMeshConverter::MeshesToNativeMesh(UObject* Outer, const UBase* Parent, const TArray<UMesh*>& SpeckleMeshes, TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
{
|
||||
if(SpeckleMeshes.Num() == 0) return nullptr;
|
||||
|
||||
const EObjectFlags ObjectFags = Transient? RF_Transient | RF_Public : RF_Public;
|
||||
UStaticMesh* Mesh = NewObject<UStaticMesh>(Outer, FName(SpeckleMesh->Id), ObjectFags);
|
||||
EObjectFlags ObjectFags = Transient? RF_Transient | RF_Public : RF_Public;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
ObjectFags |= RF_Standalone; //TODO maybe all versions should have RF_Standalone?
|
||||
#endif
|
||||
|
||||
UStaticMesh* Mesh = NewObject<UStaticMesh>(Outer, FName(Parent->Id), ObjectFags);
|
||||
|
||||
Mesh->InitResources();
|
||||
Mesh->SetLightingGuid();
|
||||
@@ -85,56 +189,68 @@ UStaticMesh* UStaticMeshConverter::MeshToNative(UObject* Outer, const UMesh* Spe
|
||||
#if WITH_EDITOR
|
||||
{
|
||||
FStaticMeshSourceModel& SrcModel = Mesh->AddSourceModel();
|
||||
SrcModel.BuildSettings.bRecomputeNormals = false;
|
||||
SrcModel.BuildSettings.bRecomputeTangents = false;
|
||||
SrcModel.BuildSettings.bRemoveDegenerates = false;
|
||||
SrcModel.BuildSettings.bRecomputeNormals = true;
|
||||
SrcModel.BuildSettings.bRecomputeTangents = true;
|
||||
SrcModel.BuildSettings.bRemoveDegenerates = RemoveDegeneratesOnBuild;
|
||||
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
|
||||
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
|
||||
SrcModel.BuildSettings.bBuildReversedIndexBuffer = BuildReversedIndexBuffer;
|
||||
SrcModel.BuildSettings.bUseFullPrecisionUVs = UseFullPrecisionUVs;
|
||||
SrcModel.BuildSettings.bGenerateLightmapUVs = GenerateLightmapUV;
|
||||
SrcModel.BuildSettings.SrcLightmapIndex = 0;
|
||||
SrcModel.BuildSettings.DstLightmapIndex = 1;
|
||||
SrcModel.BuildSettings.MinLightmapResolution = MinLightmapResolution;
|
||||
}
|
||||
#endif
|
||||
|
||||
UStaticMesh::FBuildMeshDescriptionsParams MeshParams;
|
||||
MeshParams.bBuildSimpleCollision = BuildSimpleCollision;
|
||||
MeshParams.bCommitMeshDescription = true;
|
||||
MeshParams.bMarkPackageDirty = true;
|
||||
MeshParams.bUseHashAsGuid = false;
|
||||
|
||||
//Set Mesh Data
|
||||
UMaterialInterface* Material = GetMaterial(SpeckleMesh->RenderMaterial, Manager);
|
||||
|
||||
const FName MaterialSlotName = Mesh->AddMaterial(Material);;
|
||||
BaseMeshDescription.PolygonGroupAttributes().RegisterAttribute<FName>(MeshAttribute::PolygonGroup::ImportedMaterialSlotName, 1, MaterialSlotName, EMeshAttributeFlags::None);
|
||||
GenerateMeshParams(MeshParams);
|
||||
|
||||
for(const UMesh* SpeckleMesh : SpeckleMeshes)
|
||||
{
|
||||
const size_t NumberOfVertices = SpeckleMesh->Vertices.Num();
|
||||
const size_t NumberOfFacesIndices = SpeckleMesh->Faces.Num();
|
||||
|
||||
// Convert Vertices
|
||||
if(NumberOfVertices == 0 || NumberOfFacesIndices == 0) continue;
|
||||
|
||||
StaticMeshDescription->ReserveNewVertices(NumberOfVertices);
|
||||
|
||||
|
||||
TArray<FVertexID> Vertices;
|
||||
Vertices.Reserve(NumberOfVertices);
|
||||
|
||||
|
||||
for(const FVector VertexPosition : SpeckleMesh->Vertices)
|
||||
{
|
||||
const FVertexID VertID = StaticMeshDescription->CreateVertex();
|
||||
StaticMeshDescription->SetVertexPosition(VertID, VertexPosition);
|
||||
Vertices.Add(VertID);
|
||||
}
|
||||
|
||||
// Convert Material
|
||||
UMaterialInterface* Material = GetMaterial(SpeckleMesh->RenderMaterial, GetWorld(), MaterialConverter);
|
||||
|
||||
//Convert Faces
|
||||
const FName MaterialSlotName = Mesh->AddMaterial(Material);;
|
||||
BaseMeshDescription.PolygonGroupAttributes().RegisterAttribute<FName>(MeshAttribute::PolygonGroup::ImportedMaterialSlotName, 1, MaterialSlotName, EMeshAttributeFlags::None);
|
||||
|
||||
// Convert Faces
|
||||
const FPolygonGroupID PolygonGroupID = StaticMeshDescription->CreatePolygonGroup();
|
||||
|
||||
StaticMeshDescription->SetPolygonGroupMaterialSlotName(PolygonGroupID, MaterialSlotName);
|
||||
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
StaticMeshDescription->VertexInstanceAttributes().RegisterAttribute<FVector2f>(MeshAttribute::VertexInstance::TextureCoordinate, 2, FVector2f::ZeroVector, EMeshAttributeFlags::None);
|
||||
#else
|
||||
StaticMeshDescription->VertexInstanceAttributes().RegisterAttribute<FVector2D>(MeshAttribute::VertexInstance::TextureCoordinate, 2, FVector2D::ZeroVector, EMeshAttributeFlags::None);
|
||||
|
||||
|
||||
StaticMeshDescription->ReserveNewTriangles(SpeckleMesh->Faces.Num() * 3); //Reserve space assuming faces will all be triangles
|
||||
StaticMeshDescription->ReserveNewPolygons(SpeckleMesh->Faces.Num());
|
||||
StaticMeshDescription->ReserveNewVertexInstances(SpeckleMesh->Faces.Num() * 3); //Reserve space assuming faces will all be triangles
|
||||
#endif
|
||||
{
|
||||
// Reserve space assuming faces will all be triangles //TODO (maybe it's better to assume something higher?)
|
||||
const int32 EstimatedNumberOfFaces = SpeckleMesh->Faces.Num() / 4 * 3;
|
||||
StaticMeshDescription->ReserveNewTriangles(EstimatedNumberOfFaces);
|
||||
StaticMeshDescription->ReserveNewPolygons(EstimatedNumberOfFaces);
|
||||
StaticMeshDescription->ReserveNewVertexInstances(FGenericPlatformMath::Max(EstimatedNumberOfFaces * 3, SpeckleMesh->Vertices.Num()));
|
||||
}
|
||||
|
||||
int32 i = 0;
|
||||
while (i < SpeckleMesh->Faces.Num())
|
||||
while (i < NumberOfFacesIndices)
|
||||
{
|
||||
int32 n = SpeckleMesh->Faces[i];
|
||||
if(n < 3) n += 3; // 0 -> 3, 1 -> 4
|
||||
@@ -145,74 +261,88 @@ UStaticMesh* UStaticMeshConverter::MeshToNative(UObject* Outer, const UMesh* Spe
|
||||
Verts.Reserve(n);
|
||||
for(int j = 0; j < n; j ++)
|
||||
{
|
||||
int32 VertIndex = SpeckleMesh->Faces[i + n - j];
|
||||
int32 VertIndex = SpeckleMesh->Faces[i + 1 + j];
|
||||
FVertexID Vert = Vertices[VertIndex];
|
||||
bool AlreadyInSet;
|
||||
Verts.Add(Vert, &AlreadyInSet);
|
||||
|
||||
|
||||
if(AlreadyInSet)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Invalid Polygon while creating mesh %s - vertex at index %d appears more than once in a face, duplicate vertices will be ignored"), *SpeckleMesh->Id, VertIndex);
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Invalid Polygon while creating mesh %s - vertex at index %d appears more than once in a face, duplicate vertices will be ignored"), *SpeckleMesh->Id, VertIndex);
|
||||
continue;
|
||||
}
|
||||
FVertexInstanceID VertexInstance = StaticMeshDescription->CreateVertexInstance(Vert);
|
||||
|
||||
VertexInstances.Add(VertexInstance);
|
||||
|
||||
VertexInstances.Add(VertexInstance);
|
||||
|
||||
if(SpeckleMesh->TextureCoordinates.Num() > VertIndex)
|
||||
StaticMeshDescription->SetVertexInstanceUV(VertexInstance, SpeckleMesh->TextureCoordinates[VertIndex]);
|
||||
|
||||
|
||||
//if(SpeckleMesh->VertexColors.Num() > VertIndex)
|
||||
// //TODO set vertex colors
|
||||
}
|
||||
|
||||
i += n + 1;
|
||||
if(VertexInstances.Num() < 3)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Invalid Polygon while creating mesh %s - face has fewer than 3 verts, this face will be ignored"), *SpeckleMesh->Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TArray<FEdgeID> Edges;
|
||||
Edges.Reserve(n);
|
||||
|
||||
|
||||
const FPolygonID PolygonID = StaticMeshDescription->CreatePolygon(PolygonGroupID, VertexInstances, Edges);
|
||||
|
||||
for (const FEdgeID EdgeID : Edges)
|
||||
{
|
||||
StaticMeshDescription->GetEdgeHardnesses()[EdgeID] = true;
|
||||
}
|
||||
|
||||
StaticMeshDescription->ComputePolygonTriangulation(PolygonID);
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION <= 4
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Normal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Tangent, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Binormal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Center, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
FStaticMeshOperations::ComputePolygonTangentsAndNormals(BaseMeshDescription);
|
||||
}
|
||||
|
||||
if(StaticMeshDescription->Vertices().Num() == 0
|
||||
|| StaticMeshDescription->VertexInstances().Num() == 0
|
||||
|| StaticMeshDescription->Triangles().Num() == 0)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Skipping %s $s, converted mesh is empty!"), *Parent->SpeckleType, *Parent->Id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BaseMeshDescription.TriangulateMesh();
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
FStaticMeshOperations::ComputeTriangleTangentsAndNormals(BaseMeshDescription);
|
||||
#else
|
||||
FStaticMeshOperations::ComputeTriangleTangentsAndNormals(BaseMeshDescription);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Normal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Tangent, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Binormal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Center, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
|
||||
FStaticMeshOperations::ComputePolygonTangentsAndNormals(BaseMeshDescription);
|
||||
#endif
|
||||
|
||||
FStaticMeshOperations::ComputeTangentsAndNormals(BaseMeshDescription, EComputeNTBsFlags::Normals | EComputeNTBsFlags::Tangents);
|
||||
}
|
||||
|
||||
FStaticMeshOperations::ComputeTangentsAndNormals(BaseMeshDescription, EComputeNTBsFlags::Normals | EComputeNTBsFlags::Tangents);
|
||||
|
||||
|
||||
//Mesh->PreEditChange(nullptr);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION <= 4
|
||||
Mesh->LightMapCoordinateIndex = 1;
|
||||
#else
|
||||
#if ENGINE_MAJOR_VERSION >= 5 || ( ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 27 )
|
||||
Mesh->SetLightMapCoordinateIndex(1);
|
||||
#else
|
||||
Mesh->LightMapCoordinateIndex = 1;
|
||||
#endif
|
||||
|
||||
Mesh->BuildFromMeshDescriptions(TArray<const FMeshDescription*>{&BaseMeshDescription}, MeshParams);
|
||||
|
||||
#if WITH_EDITOR
|
||||
if(UseFullBuild) Mesh->Build(true); //This makes conversion time much slower, but is needed for generating lightmap UVs
|
||||
if(UseFullBuild)
|
||||
{
|
||||
FScopeLock Lock(&Lock_StaticMeshesToBuild);
|
||||
StaticMeshesToBuild.Add(Mesh);
|
||||
}
|
||||
|
||||
if (GIsEditor && !GWorld->HasBegunPlay())
|
||||
if (!FApp::IsGame())
|
||||
{
|
||||
Mesh->MarkPackageDirty();
|
||||
FAssetRegistryModule::AssetCreated(Mesh);
|
||||
@@ -223,72 +353,20 @@ UStaticMesh* UStaticMeshConverter::MeshToNative(UObject* Outer, const UMesh* Spe
|
||||
return Mesh;
|
||||
}
|
||||
|
||||
|
||||
UMaterialInterface* UStaticMeshConverter::GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager)
|
||||
void UStaticMeshConverter::GenerateMeshParams(UStaticMesh::FBuildMeshDescriptionsParams& MeshParams) const
|
||||
{
|
||||
if(SpeckleMaterial == nullptr || SpeckleMaterial->Id == "") return Manager->DefaultMeshMaterial; //Material is invalid
|
||||
|
||||
UMaterialInterface* ExistingMaterial;
|
||||
if(Manager->TryGetMaterial(SpeckleMaterial, true, ExistingMaterial))
|
||||
return ExistingMaterial; //Return existing material
|
||||
MeshParams.bBuildSimpleCollision = BuildSimpleCollision;
|
||||
MeshParams.bCommitMeshDescription = true;
|
||||
MeshParams.bMarkPackageDirty = true;
|
||||
MeshParams.bUseHashAsGuid = false;
|
||||
|
||||
|
||||
UMaterialInterface* MaterialBase = SpeckleMaterial->Opacity >= 1
|
||||
? Manager->BaseMeshOpaqueMaterial
|
||||
: Manager->BaseMeshTransparentMaterial;
|
||||
|
||||
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle"), Manager->StreamID, TEXT("Materials"), SpeckleMaterial->Id);
|
||||
UPackage* Package = CreatePackage(*PackagePath);
|
||||
|
||||
|
||||
UMaterialInstance* MaterialInstance;
|
||||
#if WITH_EDITOR
|
||||
if (GIsEditor && !GWorld->HasBegunPlay())
|
||||
{
|
||||
const FName Name = MakeUniqueObjectName(Package, UMaterialInstanceConstant::StaticClass(), FName(SpeckleMaterial->Name));
|
||||
|
||||
//TStrongObjectPtr< UMaterialInstanceConstantFactoryNew > MaterialFact( NewObject< UMaterialInstanceConstantFactoryNew >() );
|
||||
//MaterialFact->InitialParent = MaterialBase;
|
||||
//UMaterialInstanceConstant* ConstMaterial = Cast< UMaterialInstanceConstant >( MaterialFact->FactoryCreateNew( UMaterialInstanceConstant::StaticClass(), Package, Name, RF_Public, nullptr, GWarn ) );
|
||||
UMaterialInstanceConstant* ConstMaterial = NewObject<UMaterialInstanceConstant>(Package, Name, RF_Public);
|
||||
|
||||
MaterialInstance = ConstMaterial;
|
||||
ConstMaterial->SetParentEditorOnly(MaterialBase);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Opacity"), SpeckleMaterial->Opacity);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Metallic"), SpeckleMaterial->Metalness);
|
||||
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Roughness"), SpeckleMaterial->Roughness);
|
||||
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("BaseColor"), SpeckleMaterial->Diffuse);
|
||||
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("EmissiveColor"), SpeckleMaterial->Emissive);
|
||||
|
||||
//ConstMaterial->InitStaticPermutation();
|
||||
|
||||
ConstMaterial->MarkPackageDirty();
|
||||
|
||||
FAssetRegistryModule::AssetCreated(MaterialInstance);
|
||||
}
|
||||
else
|
||||
#if !WITH_EDITOR && ENGINE_MAJOR_VERSION >= 5
|
||||
MeshParams.bFastBuild = true;
|
||||
#endif
|
||||
{
|
||||
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(MaterialBase, Package, FName(SpeckleMaterial->Name));
|
||||
MaterialInstance = DynMaterial;
|
||||
|
||||
DynMaterial->SetScalarParameterValue("Opacity", SpeckleMaterial->Opacity);
|
||||
DynMaterial->SetScalarParameterValue("Metallic", SpeckleMaterial->Metalness);
|
||||
DynMaterial->SetScalarParameterValue("Roughness", SpeckleMaterial->Roughness);
|
||||
DynMaterial->SetVectorParameterValue("BaseColor", SpeckleMaterial->Diffuse);
|
||||
DynMaterial->SetVectorParameterValue("EmissiveColor", SpeckleMaterial->Emissive);
|
||||
|
||||
DynMaterial->SetFlags(RF_Public);
|
||||
}
|
||||
|
||||
Manager->ConvertedMaterials.Add(SpeckleMaterial->Id, MaterialInstance);
|
||||
|
||||
return MaterialInstance;
|
||||
|
||||
}
|
||||
|
||||
|
||||
UBase* UStaticMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
|
||||
void UStaticMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object, UBase* SpeckleObject)
|
||||
{
|
||||
const UStaticMeshComponent* M = Cast<UStaticMeshComponent>(Object);
|
||||
|
||||
@@ -300,13 +378,94 @@ UBase* UStaticMeshConverter::ConvertToSpeckle_Implementation(const UObject* Obje
|
||||
M = A->FindComponentByClass<UStaticMeshComponent>();
|
||||
}
|
||||
}
|
||||
if(M == nullptr) return nullptr;
|
||||
if(M == nullptr) return;
|
||||
|
||||
//SpeckleObject->DynamicProperties.Add(TEXT("@displayValue"), MeshToSpeckle(M));
|
||||
|
||||
return MeshToSpeckle(M, Manager);
|
||||
}
|
||||
|
||||
|
||||
UMesh* UStaticMeshConverter::MeshToSpeckle(const UStaticMeshComponent* Object, ASpeckleUnrealManager* Manager)
|
||||
TArray<UMesh*> UStaticMeshConverter::MeshToSpeckle(const UStaticMeshComponent* MeshComponent)
|
||||
{
|
||||
return nullptr; //TODO implement ToSpeckle function
|
||||
|
||||
UStaticMeshDescription* StaticMeshDescription = NewObject<UStaticMeshDescription>(GetTransientPackage(), NAME_None, RF_Transient);
|
||||
const FMeshDescription* MeshDescription = MeshComponent->GetStaticMesh()->GetMeshDescription(0);;
|
||||
StaticMeshDescription->SetMeshDescription(*MeshDescription);
|
||||
StaticMeshDescription->RegisterAttributes();
|
||||
|
||||
const FVertexArray& nVertices = StaticMeshDescription->Vertices();
|
||||
TArray<FVector> sVertices;
|
||||
sVertices.Reserve(nVertices.Num()); // * 3));
|
||||
|
||||
TMap<FVertexID, int32> VertexIdToIndex;
|
||||
VertexIdToIndex.Reserve(nVertices.Num() * 3);
|
||||
|
||||
int32 i = 0;
|
||||
for (const FVertexID& VertexId : nVertices.GetElementIDs())
|
||||
{
|
||||
const FVector Vert = StaticMeshDescription->GetVertexPosition(VertexId);
|
||||
sVertices.Add(Vert);
|
||||
|
||||
VertexIdToIndex.Add(VertexId, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
FTriangleArray& nTriangles = StaticMeshDescription->Triangles();
|
||||
TArray<int32> sFaces;
|
||||
sFaces.Reserve(nTriangles.Num());
|
||||
|
||||
for (const FTriangleID& TriangleId : nTriangles.GetElementIDs())
|
||||
{
|
||||
TArray<FVertexID> Tri;
|
||||
StaticMeshDescription->GetTriangleVertices(TriangleId, Tri);
|
||||
|
||||
sFaces.Add(3);
|
||||
sFaces.Add(VertexIdToIndex[Tri[0]]);
|
||||
sFaces.Add(VertexIdToIndex[Tri[1]]);
|
||||
sFaces.Add(VertexIdToIndex[Tri[2]]);
|
||||
}
|
||||
|
||||
//TODO colors
|
||||
|
||||
//TODO tex-coords
|
||||
|
||||
//TODO split mesh by material
|
||||
UMesh* Mesh = NewObject<UMesh>(GetTransientPackage(), NAME_None, RF_Transient);
|
||||
|
||||
Mesh->Vertices = sVertices;
|
||||
Mesh->Faces = sFaces;
|
||||
//Mesh->Colors = sColors;
|
||||
//Mesh->TextureCoordinates = sTexCoords;
|
||||
Mesh->Units = "cm";
|
||||
|
||||
return TArray<UMesh*>{Mesh};
|
||||
}
|
||||
|
||||
|
||||
void UStaticMeshConverter::FinishConversion_Implementation()
|
||||
{
|
||||
|
||||
FScopeLock Lock(&Lock_StaticMeshesToBuild);
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
FFormatNamedArguments Args;
|
||||
Args.Add( TEXT("Path"), FText::FromString( GetPathName() ) );
|
||||
const FText StatusUpdate = FText::Format( LOCTEXT("BeginStaticMeshBuildingTask", "({Path}) Building"), Args );
|
||||
FScopedSlowTask Progress(StaticMeshesToBuild.Num(), StatusUpdate, DisplayBuildProgressBar);
|
||||
|
||||
Progress.MakeDialog(AllowCancelBuild);
|
||||
auto ProgressAction = [&Progress](UStaticMesh*) -> bool
|
||||
{
|
||||
Progress.EnterProgressFrame(1);
|
||||
return !Progress.ShouldCancel();
|
||||
};
|
||||
|
||||
UStaticMesh::BatchBuild(StaticMeshesToBuild, !DisplayBuildProgressBar, ProgressAction);
|
||||
#endif
|
||||
|
||||
StaticMeshesToBuild.Empty();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,179 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
|
||||
#include "ActorEditorUtils.h"
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Conversion/Converters/AggregateConverter.h"
|
||||
#include "Conversion/Converters/BlockConverter.h"
|
||||
#include "Conversion/Converters/PointCloudConverter.h"
|
||||
#include "Conversion/Converters/StaticMeshConverter.h"
|
||||
#include "Conversion/Converters/MaterialConverter.h"
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
USpeckleConverterComponent::USpeckleConverterComponent()
|
||||
{
|
||||
//TODO consider using an object library for default converters
|
||||
static ConstructorHelpers::FObjectFinder<UStaticMeshConverter> MeshConverter(TEXT("StaticMeshConverter'/SpeckleUnreal/Converters/DefaultStaticMeshConverter.DefaultStaticMeshConverter'"));
|
||||
static ConstructorHelpers::FObjectFinder<UPointCloudConverter> PointCloudConverter(TEXT("PointCloudConverter'/SpeckleUnreal/Converters/DefaultPointCloudConverter.DefaultPointCloudConverter'"));
|
||||
static ConstructorHelpers::FObjectFinder<UBlockConverter> BlockConverter(TEXT("BlockConverter'/SpeckleUnreal/Converters/DefaultBlockConverter.DefaultBlockConverter'"));
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialConverter> MaterialConverter(TEXT("MaterialConverter'/SpeckleUnreal/Converters/DefaultMaterialConverter.DefaultMaterialConverter'"));
|
||||
static ConstructorHelpers::FObjectFinder<UObject> CameraConverter(TEXT("CameraConverter'/SpeckleUnreal/Converters/DefaultCameraConverter.DefaultCameraConverter'"));
|
||||
//static ConstructorHelpers::FObjectFinder<ULightConverter> LightConverter(TEXT("LightConverter'/SpeckleUnreal/Converters/DefaultLightConverter.DefaultLightConverter'"));
|
||||
|
||||
SpeckleConverter = CreateDefaultSubobject<UAggregateConverter>(TEXT("Objects Converter"));
|
||||
|
||||
SpeckleConverter->SpeckleConverters.Add(MeshConverter.Object);
|
||||
SpeckleConverter->SpeckleConverters.Add(PointCloudConverter.Object);
|
||||
SpeckleConverter->SpeckleConverters.Add(BlockConverter.Object);
|
||||
SpeckleConverter->SpeckleConverters.Add(MaterialConverter.Object);
|
||||
SpeckleConverter->SpeckleConverters.Add(CameraConverter.Object);
|
||||
//SpeckleConverter->SpeckleConverters.Add(LightConverter.Object);
|
||||
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
AActor* USpeckleConverterComponent::RecursivelyConvertToNative(AActor* AOwner, const UBase* Base,
|
||||
const TScriptInterface<ITransport>& LocalTransport, bool DisplayProgressBar, TArray<AActor*>& OutActors)
|
||||
{
|
||||
float ObjectsToConvert{};
|
||||
Base->TryGetDynamicNumber("totalChildrenCount", ObjectsToConvert);
|
||||
FScopedSlowTask Progress(ObjectsToConvert + 2, LOCTEXT("SpeckleConvertoNative","Converting Speckle Objects to Native"), DisplayProgressBar);
|
||||
#if WITH_EDITOR
|
||||
Progress.MakeDialog(true, false);
|
||||
#endif
|
||||
|
||||
AActor* RootActor = RecursivelyConvertToNative_Internal(AOwner, Base, LocalTransport, &Progress, OutActors);
|
||||
|
||||
FinishConversion();
|
||||
return RootActor;
|
||||
}
|
||||
|
||||
AActor* USpeckleConverterComponent::RecursivelyConvertToNative_Internal(AActor* AOwner, const UBase* Base,
|
||||
const TScriptInterface<ITransport>& LocalTransport,
|
||||
FSlowTask* Task,
|
||||
TArray<AActor*>& OutActors)
|
||||
{
|
||||
check(IsValid(AOwner));
|
||||
if(!IsValid(Base)) return nullptr;
|
||||
|
||||
// Convert Speckle Object
|
||||
UObject* Converted = SpeckleConverter->ConvertToNativeInternal(Base, AOwner->GetWorld());
|
||||
AttachConvertedToOwner(AOwner, Base, Converted);
|
||||
|
||||
// Handle new actors
|
||||
AActor* ConvertedAsActor = Cast<AActor>(Converted);
|
||||
AActor* NextOwner = IsValid(ConvertedAsActor) ? ConvertedAsActor : AOwner;
|
||||
if(NextOwner != AOwner)
|
||||
{
|
||||
OutActors.Add(NextOwner);
|
||||
OutActors.Append(NextOwner->Children);
|
||||
}
|
||||
|
||||
Task->EnterProgressFrame(1);
|
||||
if(Task->ShouldCancel()) return AOwner;
|
||||
|
||||
//Convert Children
|
||||
TMap<FString, TSharedPtr<FJsonValue>> PotentialChildren = Base->DynamicProperties;
|
||||
|
||||
for (const auto& Kvp : PotentialChildren)
|
||||
{
|
||||
if(Task->ShouldCancel()) break;
|
||||
|
||||
ConvertChild(Kvp.Value, AOwner, LocalTransport, Task, OutActors);
|
||||
}
|
||||
return AOwner;
|
||||
}
|
||||
|
||||
void USpeckleConverterComponent::ConvertChild(const TSharedPtr<FJsonValue> Object, AActor* AOwner,
|
||||
const TScriptInterface<ITransport>& LocalTransport, FSlowTask* Task,
|
||||
TArray<AActor*>& OutActors)
|
||||
{
|
||||
//Handle child object
|
||||
const TSharedPtr<FJsonObject>* ChildObj;
|
||||
if (Object->TryGetObject(ChildObj))
|
||||
{
|
||||
const UBase* Child = USpeckleSerializer::DeserializeBase(*ChildObj, LocalTransport);
|
||||
RecursivelyConvertToNative_Internal(AOwner, Child, LocalTransport, Task, OutActors);
|
||||
return;
|
||||
}
|
||||
|
||||
//Handle child array object
|
||||
const TArray<TSharedPtr<FJsonValue>>* ChildArr;
|
||||
if (Object->TryGetArray(ChildArr))
|
||||
{
|
||||
for (const auto& v : *ChildArr)
|
||||
{
|
||||
ConvertChild(v, AOwner, LocalTransport, Task, OutActors);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void USpeckleConverterComponent::AttachConvertedToOwner(AActor* AOwner, const UBase* Base, UObject* Converted)
|
||||
{
|
||||
|
||||
// Case Actor
|
||||
{
|
||||
AActor* NativeActor = Cast<AActor>(Converted);
|
||||
if(IsValid(NativeActor))
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
{
|
||||
FString Name;
|
||||
FText _Discard;
|
||||
if( !(Base->TryGetDynamicString("name", Name) && FActorEditorUtils::ValidateActorName(FText::FromString(Name), _Discard)) )
|
||||
{
|
||||
Name = FString::Printf(TEXT("%s - %s"), *Base->SpeckleType, *Base->Id);
|
||||
}
|
||||
NativeActor->SetActorLabel(Name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure actor has a valid mobility for its owner
|
||||
if(NativeActor->HasValidRootComponent())
|
||||
{
|
||||
uint8 CurrentMobility = NativeActor->GetRootComponent()->Mobility;
|
||||
uint8 OwnerMobility = AOwner->GetRootComponent()->Mobility;
|
||||
|
||||
if(CurrentMobility < OwnerMobility)
|
||||
{
|
||||
NativeActor->GetRootComponent()->SetMobility(AOwner->GetRootComponent()->Mobility);
|
||||
}
|
||||
}
|
||||
|
||||
NativeActor->AttachToActor(AOwner, FAttachmentTransformRules::KeepRelativeTransform);
|
||||
NativeActor->SetOwner(AOwner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Case ActorComponent
|
||||
{
|
||||
UActorComponent* NativeComponent = Cast<UActorComponent>(Converted);
|
||||
if(IsValid(NativeComponent))
|
||||
{
|
||||
if(!AOwner->HasValidRootComponent()) AOwner->SetRootComponent(NewObject<USceneComponent>(AOwner));
|
||||
|
||||
USceneComponent* SceneComponent = Cast<USceneComponent>(Converted);
|
||||
if(IsValid(SceneComponent)) SceneComponent->SetupAttachment(AOwner->GetRootComponent());
|
||||
|
||||
NativeComponent->RegisterComponent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void USpeckleConverterComponent::FinishConversion()
|
||||
{
|
||||
SpeckleConverter->FinishConversion_Internal();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,60 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
#include "Conversion/Converters/AggregateConverter.h"
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
|
||||
|
||||
UBase* USpeckleConverterComponent::RecursivelyConvertToSpeckle(const TArray<AActor*>& RootActors, FActorPredicate& Predicate)
|
||||
{
|
||||
TArray<UBase*> ConvertedRootActors;
|
||||
for(const AActor* RootActor : RootActors)
|
||||
{
|
||||
RecurseTreeToSpeckle(RootActor, Predicate, ConvertedRootActors);
|
||||
}
|
||||
|
||||
UBase* Wrapper = NewObject<UBase>(GetTransientPackage(), NAME_None, RF_Transient);
|
||||
|
||||
//Converted->DynamicProperties["objects"] = ConvertedRootActors; //TODO set children
|
||||
|
||||
return Wrapper;
|
||||
}
|
||||
|
||||
void USpeckleConverterComponent::RecurseTreeToSpeckle(const AActor* RootActor, FActorPredicate& Predicate, TArray<UBase*>& OutConverted)
|
||||
{
|
||||
// Convert children first
|
||||
TArray<UBase*> ConvertedChildren;
|
||||
ConvertedChildren.Reserve(RootActor->Children.Num());
|
||||
for (const AActor* Child : RootActor->Children)
|
||||
{
|
||||
RecurseTreeToSpeckle(Child, Predicate, ConvertedChildren);
|
||||
}
|
||||
|
||||
bool ShouldConvert;
|
||||
Predicate.Execute(RootActor, ShouldConvert);
|
||||
if(ISpeckleConverter::Execute_CanConvertToSpeckle(SpeckleConverter, RootActor) && ShouldConvert)
|
||||
{
|
||||
// Convert and output
|
||||
UBase* Converted = NewObject<UBase>(GetTransientPackage(), NAME_None, RF_Transient);
|
||||
//Converted->DynamicProperties["name"] = RootActor->GetName(),
|
||||
//["transform"] = TransformToSpeckle(go.Transform), //TODO set common props
|
||||
//Converted->DynamicProperties["tag"] = go.tag,
|
||||
//Converted->DynamicProperties["layer"] = go.layer,
|
||||
//Converted->DynamicProperties["isStatic"] = go.isStatic,
|
||||
|
||||
ISpeckleConverter::Execute_ConvertToSpeckle(SpeckleConverter, RootActor, Converted);
|
||||
//Converted->DynamicProperties["objects"] = ConvertedChildren; //TODO set children
|
||||
OutConverted.Add(Converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip this object, and output any children
|
||||
OutConverted.Append(ConvertedChildren);
|
||||
}
|
||||
|
||||
}
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -1,122 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "Conversion/Converters/PointCloudConverter.h"
|
||||
#include "Conversion/Converters/StaticMeshConverter.h"
|
||||
#include "Objects/Mesh.h"
|
||||
#include "Objects/PointCloud.h"
|
||||
|
||||
|
||||
// Sets default values for this component's properties
|
||||
USpeckleConverterComponent::USpeckleConverterComponent()
|
||||
{
|
||||
//TODO consider using an object library for default converters
|
||||
static ConstructorHelpers::FObjectFinder<UStaticMeshConverter> MeshConverter(TEXT("StaticMeshConverter'/SpeckleUnreal/Converters/DefaultStaticMeshConverter.DefaultStaticMeshConverter'"));
|
||||
static ConstructorHelpers::FObjectFinder<UPointCloudConverter> PointCloudConverter(TEXT("PointCloudConverter'/SpeckleUnreal/Converters/DefaultPointCloudConverter.DefaultPointCloudConverter'"));
|
||||
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
|
||||
|
||||
|
||||
//Mesh
|
||||
//Point
|
||||
//Light
|
||||
//BlockDef
|
||||
//BlockInst
|
||||
//Wall
|
||||
//Element
|
||||
|
||||
SpeckleConverters.Add(MeshConverter.Object);
|
||||
SpeckleConverters.Add(PointCloudConverter.Object);
|
||||
//SpeckleConverter.Add(FLightConverter);
|
||||
}
|
||||
|
||||
void USpeckleConverterComponent::OnConvertersChangeHandler()
|
||||
{
|
||||
SpeckleTypeMap.Empty();
|
||||
|
||||
for(int i = 0; i < SpeckleConverters.Num(); i++)
|
||||
{
|
||||
const UObject* Converter = SpeckleConverters[i];
|
||||
if(Converter != nullptr && !Converter->GetClass()->ImplementsInterface(USpeckleConverter::StaticClass()))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface %s"), *Converter->GetClass()->GetName(), *USpeckleConverter::StaticClass()->GetName())
|
||||
SpeckleConverters.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void USpeckleConverterComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(USpeckleConverterComponent, SpeckleConverters))
|
||||
{
|
||||
OnConvertersChangeHandler();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UBase* USpeckleConverterComponent::ConvertToSpeckle(UObject* Object)
|
||||
{
|
||||
//TODO implement ToSpeckle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
AActor* USpeckleConverterComponent::ConvertToNative(const UBase* Object, ASpeckleUnrealManager* Manager)
|
||||
{
|
||||
check(Object != nullptr);
|
||||
const TSubclassOf<UBase> Type = Object->GetClass();
|
||||
UObject* Converter = GetConverter(Type).GetObject();
|
||||
if(Converter == nullptr)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Skipping Object %s - No conversion functions exist for %s"), *Object->Id, *Type->GetName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Converting object of type: %s id: %s "), *Object->Id, *Type->GetName());
|
||||
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
return ISpeckleConverter::Execute_ConvertToNative(Converter, Object, Manager);
|
||||
|
||||
}
|
||||
|
||||
TScriptInterface<ISpeckleConverter> USpeckleConverterComponent::GetConverter(const TSubclassOf<UBase> BaseType)
|
||||
{
|
||||
// Check if this SpeckleType has a known converter.
|
||||
if(SpeckleTypeMap.Contains(BaseType))
|
||||
{
|
||||
return SpeckleTypeMap[BaseType];
|
||||
}
|
||||
|
||||
// Try and find one that can convert this SpeckleType.
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
for(UObject* Converter : SpeckleConverters)
|
||||
{
|
||||
if(Converter == nullptr) continue;
|
||||
|
||||
if(!Converter->GetClass()->ImplementsInterface(USpeckleConverter::StaticClass()))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface {%s}"), *Converter->GetClass()->GetName(), *USpeckleConverter::StaticClass()->GetName())
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ISpeckleConverter::Execute_CanConvertToNative(Converter, BaseType))
|
||||
{
|
||||
//Found a Converter! Save this mapping for next time.
|
||||
SpeckleTypeMap.Add(BaseType, Converter);
|
||||
return Converter;
|
||||
}
|
||||
}
|
||||
|
||||
// SpeckleType has no conversions.
|
||||
SpeckleTypeMap.Add(BaseType, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "LogSpeckle.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogSpeckle);
|
||||
@@ -1,49 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/BuiltElement.h"
|
||||
|
||||
#include "SpeckleUnrealManager.h"
|
||||
|
||||
|
||||
TArray<FString> UBuiltElement::DisplayValueAliasStrings = {
|
||||
"displayValue",
|
||||
"@displayValue",
|
||||
"displayMesh"
|
||||
"@displayMesh"
|
||||
};
|
||||
|
||||
|
||||
void UBuiltElement::AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
|
||||
{
|
||||
UBase* v = Manager->DeserializeBase(Obj);
|
||||
if(v != nullptr) this->DisplayValue.Add(v);
|
||||
}
|
||||
|
||||
void UBuiltElement::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
|
||||
{
|
||||
Super::Parse(Obj, Manager);
|
||||
|
||||
//Find display values
|
||||
for(const FString& Alias : DisplayValueAliasStrings)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* SubObjectPtr;
|
||||
if (Obj->TryGetObjectField(Alias, SubObjectPtr))
|
||||
{
|
||||
AddDisplayValue(*SubObjectPtr, Manager);
|
||||
}
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* SubArrayPtr;
|
||||
if (Obj->TryGetArrayField(Alias, SubArrayPtr))
|
||||
{
|
||||
for (const auto& ArrayElement : *SubArrayPtr)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ArraySubObjPtr;
|
||||
if (ArrayElement->TryGetObject(ArraySubObjPtr))
|
||||
{
|
||||
AddDisplayValue(*SubObjectPtr, Manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/DisplayValueElement.h"
|
||||
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
|
||||
|
||||
TArray<FString> UDisplayValueElement::DisplayValueAliasStrings = {
|
||||
"displayValue",
|
||||
"@displayValue",
|
||||
"displayMesh"
|
||||
"@displayMesh"
|
||||
};
|
||||
|
||||
|
||||
bool UDisplayValueElement::AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
UMesh* DisplayMesh = Cast<UMesh>(USpeckleSerializer::DeserializeBase(Obj, ReadTransport));
|
||||
const bool Valid = IsValid(DisplayMesh);
|
||||
if(Valid)
|
||||
this->DisplayValue.Add(DisplayMesh);
|
||||
return Valid;
|
||||
}
|
||||
|
||||
bool UDisplayValueElement::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
|
||||
//Find display values
|
||||
for(const FString& Alias : DisplayValueAliasStrings)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* SubObjectPtr;
|
||||
if (Obj->TryGetObjectField(Alias, SubObjectPtr))
|
||||
{
|
||||
AddDisplayValue(*SubObjectPtr, ReadTransport);
|
||||
DynamicProperties.Remove(Alias);
|
||||
continue;
|
||||
}
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* SubArrayPtr;
|
||||
if (Obj->TryGetArrayField(Alias, SubArrayPtr))
|
||||
{
|
||||
for (const auto& ArrayElement : *SubArrayPtr)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ArraySubObjPtr;
|
||||
if (ArrayElement->TryGetObject(ArraySubObjPtr))
|
||||
{
|
||||
AddDisplayValue(*ArraySubObjPtr, ReadTransport);
|
||||
}
|
||||
}
|
||||
DynamicProperties.Remove(Alias);
|
||||
}
|
||||
}
|
||||
|
||||
return DisplayValue.Num() > 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright AEC Systems Ltd
|
||||
|
||||
#include "Objects/DynamicBase.h"
|
||||
#include "Misc/Variant.h"
|
||||
|
||||
template <typename T>
|
||||
bool UDynamicBase::TryGetDynamicProperty(const FString& Key, T& OutValue) const
|
||||
{
|
||||
const auto Wrapper = DynamicProperties.FindRef(Key);
|
||||
if(Wrapper.IsValid()) return false;
|
||||
|
||||
if(Wrapper->GetType() != TVariantTraits<T>::GetType()) return false;
|
||||
|
||||
OutValue = Wrapper->GetValue<T>();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void UDynamicBase::SetDynamicProperty(const FString& Key, T Value)
|
||||
{
|
||||
DynamicProperties.Add(Key, MakeShareable(new FVariant(Value)));
|
||||
}
|
||||
|
||||
int32 UDynamicBase::RemoveDynamicProperty(const FString& Key)
|
||||
{
|
||||
return DynamicProperties.Remove(Key);
|
||||
}
|
||||
|
||||
//-------------------
|
||||
|
||||
bool UDynamicBase::TryGetDynamicStringProperty(const FString& Key, FString& OutValue) const
|
||||
{
|
||||
return TryGetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
bool UDynamicBase::TryGetDynamicIntProperty(const FString& Key, int32& OutValue) const
|
||||
{
|
||||
return TryGetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
bool UDynamicBase::TryGetDynamicFloatProperty(const FString& Key, float& OutValue) const
|
||||
{
|
||||
return TryGetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
bool UDynamicBase::TryGetDynamicBoolProperty(const FString& Key, bool& OutValue) const
|
||||
{
|
||||
return TryGetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
bool UDynamicBase::TryGetDynamicBaseProperty(const FString& Key, UBase*& OutValue) const
|
||||
{
|
||||
return TryGetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
void UDynamicBase::SetDynamicStringProperty(const FString& Key, FString& OutValue)
|
||||
{
|
||||
SetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
void UDynamicBase::SetDynamicIntProperty(const FString& Key, int32& OutValue)
|
||||
{
|
||||
SetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
void UDynamicBase::SetDynamicFloatProperty(const FString& Key, float& OutValue)
|
||||
{
|
||||
SetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
void UDynamicBase::SetDynamicBoolProperty(const FString& Key, bool& OutValue)
|
||||
{
|
||||
SetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
void UDynamicBase::SetDynamicBaseProperty(const FString& Key, UBase*& OutValue)
|
||||
{
|
||||
SetDynamicProperty(Key, OutValue);
|
||||
}
|
||||
|
||||
+86
-27
@@ -1,37 +1,32 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/Mesh.h"
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "Objects/RenderMaterial.h"
|
||||
#include "Objects/Other/RenderMaterial.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
#include "Transports/Transport.h"
|
||||
|
||||
void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
|
||||
bool UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
Super::Parse(Obj, Manager);
|
||||
|
||||
const float ScaleFactor = Manager->ParseScaleFactor(Units);
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
const float ScaleFactor = USpeckleObjectUtils::ParseScaleFactor(Units);
|
||||
|
||||
//Parse optional Transform
|
||||
if(USpeckleObjectUtils::TryParseTransform(Obj, Transform))
|
||||
{
|
||||
Transform.ScaleTranslation(FVector(ScaleFactor));
|
||||
DynamicProperties.Remove("transform");
|
||||
}
|
||||
else
|
||||
{
|
||||
Transform = FMatrix::Identity;
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* TransformData = nullptr;
|
||||
if(Obj->HasField("properties") && Obj->GetObjectField("properties")->TryGetArrayField("transform", TransformData))
|
||||
{
|
||||
for(int32 Row = 0; Row < 4; Row++)
|
||||
for(int32 Col = 0; Col < 4; Col++)
|
||||
{
|
||||
Transform.M[Row][Col] = TransformData->operator[](Row * 4 + Col)->AsNumber();
|
||||
}
|
||||
Transform = Transform.GetTransposed();
|
||||
Transform.ScaleTranslation(FVector(ScaleFactor));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Parse Vertices
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectVertices = Manager->CombineChunks(Obj->GetArrayField("vertices"));
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectVertices = USpeckleObjectUtils::CombineChunks(Obj->GetArrayField("vertices"), ReadTransport);
|
||||
const int32 NumberOfVertices = ObjectVertices.Num() / 3;
|
||||
|
||||
Vertices.Reserve(NumberOfVertices);
|
||||
@@ -41,20 +36,22 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
Vertices.Add(Transform.InverseTransformPosition(FVector
|
||||
(
|
||||
ObjectVertices[j].Get()->AsNumber(),
|
||||
ObjectVertices[j + 1].Get()->AsNumber(),
|
||||
-ObjectVertices[j + 1].Get()->AsNumber(),
|
||||
ObjectVertices[j + 2].Get()->AsNumber()
|
||||
) * ScaleFactor ));
|
||||
}
|
||||
DynamicProperties.Remove("vertices");
|
||||
}
|
||||
|
||||
//Parse Faces
|
||||
{
|
||||
const TArray<TSharedPtr<FJsonValue>> FaceVertices = Manager->CombineChunks(Obj->GetArrayField("faces"));
|
||||
const TArray<TSharedPtr<FJsonValue>> FaceVertices = USpeckleObjectUtils::CombineChunks(Obj->GetArrayField("faces"), ReadTransport);
|
||||
Faces.Reserve(FaceVertices.Num());
|
||||
for(const auto VertIndex : FaceVertices)
|
||||
for(const auto& VertIndex : FaceVertices)
|
||||
{
|
||||
Faces.Add(VertIndex->AsNumber());
|
||||
}
|
||||
DynamicProperties.Remove("faces");
|
||||
}
|
||||
|
||||
//Parse TextureCoords
|
||||
@@ -62,7 +59,7 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
const TArray<TSharedPtr<FJsonValue>>* TextCoordArray;
|
||||
if(Obj->TryGetArrayField("textureCoordinates", TextCoordArray))
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> TexCoords = Manager->CombineChunks(*TextCoordArray);
|
||||
TArray<TSharedPtr<FJsonValue>> TexCoords = USpeckleObjectUtils::CombineChunks(*TextCoordArray, ReadTransport);
|
||||
|
||||
TextureCoordinates.Reserve(TexCoords.Num() / 2);
|
||||
|
||||
@@ -74,6 +71,7 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
TexCoords[i + 1].Get()->AsNumber()
|
||||
));
|
||||
}
|
||||
DynamicProperties.Remove("textureCoordinates");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +80,7 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
const TArray<TSharedPtr<FJsonValue>>* ColorArray;
|
||||
if(Obj->TryGetArrayField("colors", ColorArray))
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> Colors = Manager->CombineChunks(*ColorArray);
|
||||
TArray<TSharedPtr<FJsonValue>> Colors = USpeckleObjectUtils::CombineChunks(*ColorArray, ReadTransport);
|
||||
|
||||
VertexColors.Reserve(Colors.Num());
|
||||
|
||||
@@ -90,6 +88,7 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
{
|
||||
VertexColors.Add(FColor(Colors[i].Get()->AsNumber()));
|
||||
}
|
||||
DynamicProperties.Remove("colors");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +96,71 @@ void UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager
|
||||
if (Obj->HasField("renderMaterial"))
|
||||
{
|
||||
RenderMaterial = NewObject<URenderMaterial>();
|
||||
RenderMaterial->Parse(Obj->GetObjectField("renderMaterial"), Manager);
|
||||
RenderMaterial->Parse(Obj->GetObjectField("renderMaterial"), ReadTransport);
|
||||
DynamicProperties.Remove("renderMaterial");
|
||||
}
|
||||
|
||||
|
||||
AlignVerticesWithTexCoordsByIndex();
|
||||
|
||||
return Vertices.Num() > 0 && Faces.Num() > 0;
|
||||
}
|
||||
|
||||
void UMesh::ToJson(TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> Writer)
|
||||
{
|
||||
Super::ToJson(Writer);
|
||||
|
||||
// Serialize Transform
|
||||
// TODO todo transform
|
||||
|
||||
// Serialize Vertices
|
||||
{
|
||||
TArray<double> FlattenedVertices;
|
||||
FlattenedVertices.Reserve(Vertices.Num() * 3);
|
||||
for(size_t i = 0, j = 0; i < Vertices.Num(); i++)
|
||||
{
|
||||
const FVector& v = Vertices[i];
|
||||
FlattenedVertices[j++] = v.X;
|
||||
FlattenedVertices[j++] = -v.Y;
|
||||
FlattenedVertices[j++] = v.Z;
|
||||
}
|
||||
Writer.WriteValue(TEXT("vertices"), FlattenedVertices);
|
||||
}
|
||||
|
||||
// Serialize Faces
|
||||
Writer.WriteValue(TEXT("faces"), Faces);
|
||||
|
||||
|
||||
//Parse TextureCoords
|
||||
{
|
||||
TArray<double> FlattenedTexCoords;
|
||||
FlattenedTexCoords.Reserve(TextureCoordinates.Num() * 2);
|
||||
for(size_t i = 0, j = 0; i < TextureCoordinates.Num(); i++)
|
||||
{
|
||||
const FVector2D& c = TextureCoordinates[i];
|
||||
FlattenedTexCoords[j++] = c.X;
|
||||
FlattenedTexCoords[j++] = -c.Y;
|
||||
}
|
||||
Writer.WriteValue(TEXT("textureCoordinates"), FlattenedTexCoords);
|
||||
}
|
||||
|
||||
//Parse VertexColors
|
||||
{
|
||||
TArray<int32> ArgbColors;
|
||||
ArgbColors.Reserve(VertexColors.Num());
|
||||
for(size_t i = 0; i < VertexColors.Num(); i++)
|
||||
{
|
||||
ArgbColors[i] = VertexColors[i].ToPackedARGB();
|
||||
}
|
||||
Writer.WriteValue(TEXT("colors"), ArgbColors);
|
||||
}
|
||||
|
||||
|
||||
// Parse Optional RenderMaterial
|
||||
//if (RenderMaterial != nullptr)
|
||||
{
|
||||
//TODO renderMaterial
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/Geometry/PointCloud.h"
|
||||
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
#include "Transports/Transport.h"
|
||||
|
||||
bool UPointCloud::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
|
||||
const float ScaleFactor = USpeckleObjectUtils::ParseScaleFactor(Units);
|
||||
|
||||
//Parse Points
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectPoints = USpeckleObjectUtils::CombineChunks(Obj->GetArrayField("points"), ReadTransport);
|
||||
|
||||
Points.Reserve(ObjectPoints.Num() / 3);
|
||||
for (int32 i = 2; i < ObjectPoints.Num(); i += 3)
|
||||
{
|
||||
Points.Add(FVector
|
||||
(
|
||||
ObjectPoints[i - 2].Get()->AsNumber(),
|
||||
ObjectPoints[i - 1].Get()->AsNumber(),
|
||||
ObjectPoints[i].Get()->AsNumber()
|
||||
) * ScaleFactor);
|
||||
}
|
||||
DynamicProperties.Remove("points");
|
||||
}
|
||||
|
||||
|
||||
//Parse Colors
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectColors = USpeckleObjectUtils::CombineChunks(Obj->GetArrayField("colors"), ReadTransport);
|
||||
|
||||
Colors.Reserve(ObjectColors.Num());
|
||||
for (int32 i = 0; i < ObjectColors.Num(); i += 1)
|
||||
{
|
||||
Colors.Add( FColor(ObjectColors[i].Get()->AsNumber()) );
|
||||
}
|
||||
DynamicProperties.Remove("colors");
|
||||
}
|
||||
|
||||
//Parse Sizes
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectSizes = USpeckleObjectUtils::CombineChunks(Obj->GetArrayField("sizes"), ReadTransport);
|
||||
|
||||
Sizes.Reserve(ObjectSizes.Num());
|
||||
for (int32 i = 0; i < ObjectSizes.Num(); i += 1)
|
||||
{
|
||||
Sizes.Add( ObjectSizes[i].Get()->AsNumber() * ScaleFactor);
|
||||
}
|
||||
DynamicProperties.Remove("sizes");
|
||||
}
|
||||
|
||||
return Points.Num() >= 0;
|
||||
}
|
||||
|
||||
+23
-13
@@ -1,11 +1,12 @@
|
||||
#include "Objects/RegisteringBase.h"
|
||||
#include "Objects/ObjectModelRegistry.h"
|
||||
|
||||
#include "Objects/Base.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
|
||||
//TOptional<TMap<FString, TSubclassOf<UBase>>> URegisteringBase::TypeRegistry;
|
||||
TMap<FString, TSubclassOf<UBase>> URegisteringBase::TypeRegistry;
|
||||
TMap<FString, TSubclassOf<UBase>> UObjectModelRegistry::TypeRegistry;
|
||||
|
||||
void URegisteringBase::GenerateTypeRegistry()
|
||||
void UObjectModelRegistry::GenerateTypeRegistry()
|
||||
{
|
||||
//TypeRegistry.Reset();
|
||||
TypeRegistry.Empty();
|
||||
@@ -34,33 +35,42 @@ void URegisteringBase::GenerateTypeRegistry()
|
||||
|
||||
|
||||
|
||||
TSubclassOf<UBase> URegisteringBase::FindClosestType(const FString& SpeckleType)
|
||||
TSubclassOf<UBase> UObjectModelRegistry::FindClosestType(const FString& SpeckleType)
|
||||
{
|
||||
FString TypeString(SpeckleType);
|
||||
TSubclassOf<UBase> Type = nullptr;
|
||||
|
||||
while(!TryGetRegisteredType(TypeString, Type))
|
||||
{
|
||||
int32 SplitIndex;
|
||||
if(TypeString.FindLastChar('.', SplitIndex))
|
||||
{
|
||||
TypeString = TypeString.Left(SplitIndex);
|
||||
}
|
||||
else return nullptr;
|
||||
if(!ParentType(TypeString, TypeString)) return nullptr;
|
||||
}
|
||||
|
||||
return Type;
|
||||
|
||||
}
|
||||
|
||||
TSubclassOf<UBase> URegisteringBase::GetRegisteredType(const FString& SpeckleType)
|
||||
bool UObjectModelRegistry::ParentType(const FString& Type, FString& NextType)
|
||||
{
|
||||
int32 DotSplitIndex;
|
||||
Type.FindLastChar('.', DotSplitIndex);
|
||||
int32 ColonSplitIndex;
|
||||
Type.FindLastChar(':', ColonSplitIndex);
|
||||
const int32 SplitIndex = FGenericPlatformMath::Max(DotSplitIndex, ColonSplitIndex);
|
||||
|
||||
if(SplitIndex <= 0) return false;
|
||||
|
||||
NextType = Type.Left(SplitIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
TSubclassOf<UBase> UObjectModelRegistry::GetRegisteredType(const FString& SpeckleType)
|
||||
{
|
||||
TSubclassOf<UBase> Type = nullptr;
|
||||
TryGetRegisteredType(SpeckleType, Type);
|
||||
return Type;
|
||||
}
|
||||
|
||||
bool URegisteringBase::TryGetRegisteredType(const FString& SpeckleType, TSubclassOf<UBase>& OutType)
|
||||
bool UObjectModelRegistry::TryGetRegisteredType(const FString& SpeckleType, TSubclassOf<UBase>& OutType)
|
||||
{
|
||||
if(TypeRegistry.Num() == 0) GenerateTypeRegistry();
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/Other/BlockInstance.h"
|
||||
#include "LogSpeckle.h"
|
||||
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
#include "Transports/Transport.h"
|
||||
|
||||
bool UBlockInstance::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
|
||||
const float ScaleFactor = USpeckleObjectUtils::ParseScaleFactor(Units);
|
||||
|
||||
//Transform
|
||||
if(!USpeckleObjectUtils::TryParseTransform(Obj, Transform)) return false;
|
||||
Transform.ScaleTranslation(FVector(ScaleFactor));
|
||||
DynamicProperties.Remove("Transform");
|
||||
|
||||
|
||||
//Geometries
|
||||
const TSharedPtr<FJsonObject>* BlockDefinitionPtr;
|
||||
if(!Obj->TryGetObjectField("blockDefinition", BlockDefinitionPtr)) return false;
|
||||
|
||||
const FString RefID = BlockDefinitionPtr->operator->()->GetStringField("referencedId");
|
||||
const TSharedPtr<FJsonObject> BlockDefinition = ReadTransport->GetSpeckleObject(RefID);
|
||||
|
||||
if(BlockDefinition->TryGetStringField("name", Name)) DynamicProperties.Remove("Name");
|
||||
|
||||
const auto Geometries = BlockDefinition->GetArrayField("geometry");
|
||||
|
||||
if(Geometries.Num() <= 0)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Block definition has no geometry. id: %s"), *RefID)
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& Geo : Geometries)
|
||||
{
|
||||
const TSharedPtr<FJsonObject> MeshReference = Geo->AsObject();
|
||||
const FString ChildId = MeshReference->GetStringField("referencedId");
|
||||
|
||||
if(ReadTransport->HasObject(ChildId))
|
||||
{
|
||||
UBase* Child = USpeckleSerializer::DeserializeBase(ReadTransport->GetSpeckleObject(ChildId), ReadTransport);
|
||||
if(IsValid(Child))
|
||||
Geometry.Add(Child);
|
||||
}
|
||||
else UE_LOG(LogSpeckle, Warning, TEXT("Block definition references an unknown object id: %s"), *ChildId)
|
||||
}
|
||||
DynamicProperties.Remove("geometry");
|
||||
|
||||
// Intentionally don't remove blockDefinition from dynamic properties,
|
||||
// because we want the converter to create the child geometries for us
|
||||
//DynamicProperties.Remove("blockDefinition");
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright AEC Systems Ltd
|
||||
|
||||
|
||||
#include "Objects/Other/View3D.h"
|
||||
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
bool UView3D::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
|
||||
const float ScaleFactor = USpeckleObjectUtils::ParseScaleFactor(Units);
|
||||
|
||||
// Parse optional Name property
|
||||
if(Obj->TryGetStringField("name", Name)) DynamicProperties.Remove("name");
|
||||
|
||||
// Parse Origin
|
||||
if(!USpeckleObjectUtils::ParseVectorProperty(Obj, "origin", ReadTransport, Origin)) return false;
|
||||
Origin *= ScaleFactor;
|
||||
DynamicProperties.Remove("origin");
|
||||
|
||||
// Parse UpDirection
|
||||
if(!USpeckleObjectUtils::ParseVectorProperty(Obj, "upDirection", ReadTransport, UpDirection)) return false;
|
||||
UpDirection *= ScaleFactor;
|
||||
DynamicProperties.Remove("upDirection");
|
||||
|
||||
// Parse ForwardDirection
|
||||
if(!USpeckleObjectUtils::ParseVectorProperty(Obj, "forwardDirection", ReadTransport, ForwardDirection)) return false;
|
||||
ForwardDirection *= ScaleFactor;
|
||||
DynamicProperties.Remove("forwardDirection");
|
||||
|
||||
// Parse IsOrthogonal
|
||||
if(!Obj->TryGetBoolField("isOrthogonal", IsOrthogonal)) return false;
|
||||
DynamicProperties.Remove("isOrthogonal");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/PointCloud.h"
|
||||
|
||||
#include "SpeckleUnrealManager.h"
|
||||
|
||||
void UPointCloud::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
|
||||
{
|
||||
Super::Parse(Obj, Manager);
|
||||
|
||||
const float ScaleFactor = Manager->ParseScaleFactor(Units);
|
||||
|
||||
//Parse Points
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectPoints = Manager->CombineChunks(Obj->GetArrayField("points"));
|
||||
|
||||
Points.Reserve(ObjectPoints.Num() / 3);
|
||||
for (int32 i = 2; i < ObjectPoints.Num(); i += 3)
|
||||
{
|
||||
Points.Add(FVector
|
||||
(
|
||||
-ObjectPoints[i - 2].Get()->AsNumber(),
|
||||
ObjectPoints[i - 1].Get()->AsNumber(),
|
||||
ObjectPoints[i].Get()->AsNumber()
|
||||
) * ScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Parse Colors
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectColors = Manager->CombineChunks(Obj->GetArrayField("colors"));
|
||||
|
||||
Colors.Reserve(ObjectColors.Num());
|
||||
for (int32 i = 0; i < ObjectColors.Num(); i += 1)
|
||||
{
|
||||
Colors.Add( FColor(ObjectColors[i].Get()->AsNumber()) );
|
||||
}
|
||||
}
|
||||
|
||||
//Parse Sizes
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectSizes = Manager->CombineChunks(Obj->GetArrayField("sizes"));
|
||||
|
||||
Sizes.Reserve(ObjectSizes.Num());
|
||||
for (int32 i = 0; i < ObjectSizes.Num(); i += 1)
|
||||
{
|
||||
Sizes.Add( ObjectSizes[i].Get()->AsNumber() * ScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
#include "Transports/Transport.h"
|
||||
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> USpeckleObjectUtils::CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField, const TScriptInterface<ITransport> Transport)
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectPoints;
|
||||
|
||||
for(int32 i = 0; i < ArrayField.Num(); i++)
|
||||
{
|
||||
FString Index;
|
||||
if(ArrayField[i]->AsObject()->TryGetStringField("referencedId", Index))
|
||||
{
|
||||
if(!ensureAlwaysMsgf(Transport->HasObject(Index), TEXT("Failed to Dechunk array, Could not find chunk %s in transport"), *Index))
|
||||
continue;
|
||||
|
||||
const auto Chunk = Transport->GetSpeckleObject(Index)->GetArrayField("data");;
|
||||
ObjectPoints.Append(Chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ArrayField; //Array was never chunked to begin with
|
||||
}
|
||||
}
|
||||
return ObjectPoints;
|
||||
}
|
||||
|
||||
bool USpeckleObjectUtils::ResolveReference(const TSharedPtr<FJsonObject> Object, const TScriptInterface<ITransport> Transport, TSharedPtr<FJsonObject>& OutObject)
|
||||
{
|
||||
FString SpeckleType;
|
||||
FString ReferenceID;
|
||||
|
||||
if (Object->TryGetStringField("speckle_type", SpeckleType)
|
||||
&& SpeckleType == "reference"
|
||||
&& Object->TryGetStringField("referencedId",ReferenceID))
|
||||
{
|
||||
check(Transport != nullptr && Transport.GetObject() != nullptr)
|
||||
|
||||
OutObject = Transport->GetSpeckleObject(ReferenceID);
|
||||
return OutObject != nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float USpeckleObjectUtils::ParseScaleFactor(const FString& UnitsString)
|
||||
{
|
||||
static const auto ParseUnits = [](const FString& LUnits) -> float
|
||||
{
|
||||
if (LUnits == "millimeters" || LUnits == "millimeter" || LUnits == "millimetres" || LUnits == "millimetre" || LUnits == "mm")
|
||||
return 0.1;
|
||||
if (LUnits == "centimeters" || LUnits == "centimeter" ||LUnits == "centimetres" || LUnits == "centimetre" || LUnits == "cm")
|
||||
return 1;
|
||||
if (LUnits == "meters" || LUnits == "meter" || LUnits == "metres" || LUnits == "metre" || LUnits == "m")
|
||||
return 100;
|
||||
if (LUnits == "kilometers" || LUnits == "kilometres" || LUnits == "km")
|
||||
return 100000;
|
||||
|
||||
if (LUnits == "inches" || LUnits == "inch" || LUnits == "in")
|
||||
return 2.54;
|
||||
if (LUnits == "feet" || LUnits == "foot" || LUnits == "ft")
|
||||
return 30.48;
|
||||
if (LUnits == "yards" || LUnits == "yard"|| LUnits == "yd")
|
||||
return 91.44;
|
||||
if (LUnits == "miles" || LUnits == "mile" || LUnits == "mi")
|
||||
return 160934.4;
|
||||
|
||||
return 100;
|
||||
};
|
||||
|
||||
return ParseUnits(UnitsString.ToLower()); // * WorldToCentimeters; //TODO take into account world units
|
||||
}
|
||||
|
||||
FTransform USpeckleObjectUtils::CreateTransform(UPARAM(ref) const FMatrix& TransformMatrix)
|
||||
{
|
||||
FTransform Transform(TransformMatrix);
|
||||
Transform.ScaleTranslation(FVector(1,-1,1));
|
||||
FVector Rot = Transform.GetRotation().Euler();
|
||||
FVector NewRot(-Rot.X, Rot.Y, -Rot.Z);
|
||||
Transform.SetRotation(FQuat::MakeFromEuler(NewRot));
|
||||
return Transform;
|
||||
}
|
||||
|
||||
bool USpeckleObjectUtils::TryParseTransform(const TSharedPtr<FJsonObject> SpeckleObject, FMatrix& OutMatrix)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* TransformObject;
|
||||
const TArray<TSharedPtr<FJsonValue>>* TransformData;
|
||||
|
||||
if(SpeckleObject->TryGetArrayField("transform", TransformData)) //Handle transform as array
|
||||
{ }
|
||||
else if(SpeckleObject->TryGetObjectField("transform", TransformObject)
|
||||
&& (*TransformObject)->TryGetArrayField("value", TransformData)) //Handle transform as object
|
||||
{ }
|
||||
else return false;
|
||||
|
||||
FMatrix TransformMatrix;
|
||||
for(int32 Row = 0; Row < 4; Row++)
|
||||
for(int32 Col = 0; Col < 4; Col++)
|
||||
{
|
||||
TransformMatrix.M[Row][Col] = TransformData->operator[](Row * 4 + Col)->AsNumber();
|
||||
}
|
||||
OutMatrix = TransformMatrix.GetTransposed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool USpeckleObjectUtils::ParseVectorProperty(const TSharedPtr<FJsonObject> Base, const FString& PropertyName,
|
||||
const TScriptInterface<ITransport> ReadTransport, FVector& OutObject)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* OriginObject;
|
||||
if(Base->TryGetObjectField(PropertyName, OriginObject)
|
||||
&& ParseVector(*OriginObject, ReadTransport, OutObject))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool USpeckleObjectUtils::ParseVector(const TSharedPtr<FJsonObject> Object,
|
||||
const TScriptInterface<ITransport> Transport, FVector& OutObject)
|
||||
{
|
||||
if(!ensure(Object != nullptr)) return false;
|
||||
|
||||
TSharedPtr<FJsonObject> Obj;
|
||||
if(!ResolveReference(Object, Transport, Obj)) Obj = Object;
|
||||
|
||||
double x = 0, y = 0, z = 0;
|
||||
|
||||
if(!(Obj->TryGetNumberField("x", x)
|
||||
&& Obj->TryGetNumberField("y", y)
|
||||
&& Obj->TryGetNumberField("z", z))) return false;
|
||||
|
||||
OutObject = FVector(x,y,z);
|
||||
//return true;
|
||||
|
||||
UMesh* Mesh;
|
||||
return ParseSpeckleObject<UMesh>(Obj, Transport, Mesh);
|
||||
}
|
||||
|
||||
template <typename TBase>
|
||||
bool USpeckleObjectUtils::ParseSpeckleObject(const TSharedPtr<FJsonObject> Object,
|
||||
const TScriptInterface<ITransport> Transport, TBase*& OutObject)
|
||||
{
|
||||
static_assert(TIsDerivedFrom<TBase, UBase>::IsDerived, "Type TBase must inherit UBase");
|
||||
|
||||
TSharedPtr<FJsonObject> Obj;
|
||||
if(!ResolveReference(Object, Transport, Obj)) Obj = Object;
|
||||
|
||||
UBase* b = USpeckleSerializer::DeserializeBase(Object, Transport);
|
||||
OutObject = Cast<TBase>(b);
|
||||
return OutObject == nullptr;
|
||||
}
|
||||
|
||||
|
||||
AActor* USpeckleObjectUtils::SpawnActorInWorld(const TSubclassOf<AActor> Class, UWorld* World, const FTransform& Transform)
|
||||
{
|
||||
return World->SpawnActor(Class, &Transform, FActorSpawnParameters());
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SpeckleUnrealActor.h"
|
||||
|
||||
// Sets default values
|
||||
ASpeckleUnrealActor::ASpeckleUnrealActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
Scene = CreateDefaultSubobject<USceneComponent>("Root");
|
||||
RootComponent = Scene;
|
||||
Scene->SetMobility(EComponentMobility::Static);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SpeckleUnrealLayer.h"
|
||||
|
||||
USpeckleUnrealLayer::USpeckleUnrealLayer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void USpeckleUnrealLayer::Init(FString NewLayerName, int32 NewStartIndex, int32 NewObjectCount)
|
||||
{
|
||||
LayerName = NewLayerName;
|
||||
LayerColor = FLinearColor(FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), 1);
|
||||
StartIndex = NewStartIndex;
|
||||
ObjectCount = NewObjectCount;
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
#include "SpeckleUnrealActor.h"
|
||||
#include "SpeckleUnrealManager.h"
|
||||
#include "Objects/Mesh.h"
|
||||
|
||||
#include "Objects/RenderMaterial.h"
|
||||
|
||||
|
||||
void ASpeckleUnrealManager::ImportObjectFromCache(AActor* AOwner, const TSharedPtr<FJsonObject> SpeckleObject)
|
||||
{
|
||||
{ // Handle Detached Objects
|
||||
TSharedPtr<FJsonObject> DetachedObject;
|
||||
if(ResolveReference(SpeckleObject, DetachedObject))
|
||||
{
|
||||
return ImportObjectFromCache(AOwner, DetachedObject);
|
||||
}
|
||||
}
|
||||
|
||||
const UBase* Base = DeserializeBase(SpeckleObject);
|
||||
if(Base == nullptr)
|
||||
return;
|
||||
|
||||
AActor* Native = Converter->ConvertToNative(Base, this);
|
||||
|
||||
if(IsValid(Native))
|
||||
{
|
||||
|
||||
#if WITH_EDITOR
|
||||
Native->SetActorLabel(FString::Printf(TEXT("%s - %s"), *Base->SpeckleType, *Base->Id));
|
||||
#endif
|
||||
|
||||
Native->AttachToActor(AOwner, FAttachmentTransformRules::KeepRelativeTransform);
|
||||
Native->SetOwner(AOwner);
|
||||
|
||||
InProgressObjectsCache.Add(Native);
|
||||
}
|
||||
else
|
||||
{
|
||||
Native = AOwner;
|
||||
}
|
||||
|
||||
|
||||
//Convert Children
|
||||
for (const auto& Kv : SpeckleObject->Values)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* SubObjectPtr;
|
||||
if (Kv.Value->TryGetObject(SubObjectPtr))
|
||||
{
|
||||
ImportObjectFromCache(Native, *SubObjectPtr);
|
||||
continue;
|
||||
}
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* SubArrayPtr;
|
||||
if (Kv.Value->TryGetArray(SubArrayPtr))
|
||||
{
|
||||
for (const auto ArrayElement : *SubArrayPtr)
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* ArraySubObjPtr;
|
||||
if (!ArrayElement->TryGetObject(ArraySubObjPtr))
|
||||
continue;
|
||||
ImportObjectFromCache(Native, *ArraySubObjPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ASpeckleUnrealManager::TryGetMaterial(const URenderMaterial* SpeckleMaterial, const bool AcceptMaterialOverride, UMaterialInterface*& OutMaterial)
|
||||
{
|
||||
const auto MaterialID = SpeckleMaterial->Id;
|
||||
|
||||
if(AcceptMaterialOverride)
|
||||
{
|
||||
//Override by id
|
||||
if(MaterialOverridesById.Contains(MaterialID))
|
||||
{
|
||||
OutMaterial = MaterialOverridesById[MaterialID];
|
||||
return true;
|
||||
}
|
||||
//Override by name
|
||||
const FString Name = SpeckleMaterial->Name;
|
||||
for (const UMaterialInterface* Mat : MaterialOverridesByName)
|
||||
{
|
||||
if(Mat->GetName() == Name)
|
||||
{
|
||||
OutMaterial = MaterialOverridesById[MaterialID];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(ConvertedMaterials.Contains(MaterialID))
|
||||
{
|
||||
OutMaterial = ConvertedMaterials[MaterialID];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
UBase* ASpeckleUnrealManager::DeserializeBase(const TSharedPtr<FJsonObject> Obj) const
|
||||
{
|
||||
{ // Handle Detached Objects
|
||||
TSharedPtr<FJsonObject> DetachedObject;
|
||||
if(ResolveReference(Obj, DetachedObject))
|
||||
{
|
||||
return DeserializeBase(DetachedObject);
|
||||
}
|
||||
}
|
||||
|
||||
FString SpeckleType;
|
||||
if (!Obj->TryGetStringField("speckle_type", SpeckleType)) return nullptr;
|
||||
FString ObjectId = "";
|
||||
Obj->TryGetStringField("id", ObjectId);
|
||||
|
||||
const TSubclassOf<UBase> BaseType = UBase::FindClosestType(SpeckleType);
|
||||
|
||||
if(BaseType == nullptr)
|
||||
{
|
||||
UE_LOG(LogTemp, Verbose, TEXT("SpeckleType: %s is unknown,%t object: %s will be ignored"), *SpeckleType, *ObjectId );
|
||||
return nullptr; //BaseType = UBase::StaticClass();
|
||||
}
|
||||
|
||||
|
||||
UBase* Base = NewObject<UBase>(GetTransientPackage(), BaseType);
|
||||
Base->Parse(Obj, this);
|
||||
|
||||
return Base;
|
||||
}
|
||||
|
||||
bool ASpeckleUnrealManager::HasObject(const FString& Id) const
|
||||
{
|
||||
return SpeckleObjects.Contains(Id);
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject, ESPMode::Fast> ASpeckleUnrealManager::GetSpeckleObject(const FString& Id) const
|
||||
{
|
||||
return *SpeckleObjects.Find(Id);
|
||||
}
|
||||
|
||||
bool ASpeckleUnrealManager::ResolveReference(const TSharedPtr<FJsonObject> Object, TSharedPtr<FJsonObject>& OutObject) const
|
||||
{
|
||||
FString SpeckleType;
|
||||
FString ReferenceID;
|
||||
|
||||
if (Object->TryGetStringField("speckle_type", SpeckleType)
|
||||
&& SpeckleType == "reference"
|
||||
&& Object->TryGetStringField("referencedId",ReferenceID))
|
||||
{
|
||||
OutObject = GetSpeckleObject(ReferenceID);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> ASpeckleUnrealManager::CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField) const
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ObjectPoints;
|
||||
|
||||
for(int32 i = 0; i < ArrayField.Num(); i++)
|
||||
{
|
||||
FString Index;
|
||||
if(ArrayField[i]->AsObject()->TryGetStringField("referencedId", Index))
|
||||
{
|
||||
const auto Chunk = SpeckleObjects[Index]->GetArrayField("data");
|
||||
ObjectPoints.Append(Chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ArrayField; //Array was never chunked to begin with
|
||||
}
|
||||
}
|
||||
return ObjectPoints;
|
||||
}
|
||||
|
||||
float ASpeckleUnrealManager::ParseScaleFactor(const FString& Units) const
|
||||
{
|
||||
static const auto ParseUnits = [](const FString& LUnits) -> float
|
||||
{
|
||||
if (LUnits == "millimeters" || LUnits == "millimeter" || LUnits == "millimetres" || LUnits == "millimetre" || LUnits == "mm")
|
||||
return 0.1;
|
||||
if (LUnits == "centimeters" || LUnits == "centimeter" ||LUnits == "centimetres" || LUnits == "centimetre" || LUnits == "cm")
|
||||
return 1;
|
||||
if (LUnits == "meters" || LUnits == "meter" || LUnits == "metres" || LUnits == "metre" || LUnits == "m")
|
||||
return 100;
|
||||
if (LUnits == "kilometers" || LUnits == "kilometres" || LUnits == "km")
|
||||
return 100000;
|
||||
|
||||
if (LUnits == "inches" || LUnits == "inch" || LUnits == "in")
|
||||
return 2.54;
|
||||
if (LUnits == "feet" || LUnits == "foot" || LUnits == "ft")
|
||||
return 30.48;
|
||||
if (LUnits == "yards" || LUnits == "yard"|| LUnits == "yd")
|
||||
return 91.44;
|
||||
if (LUnits == "miles" || LUnits == "mile" || LUnits == "mi")
|
||||
return 160934.4;
|
||||
|
||||
return 100;
|
||||
};
|
||||
|
||||
return ParseUnits(Units.ToLower()) * WorldToCentimeters;
|
||||
}
|
||||
|
||||
|
||||
AActor* ASpeckleUnrealManager::CreateBlockInstance(const TSharedPtr<FJsonObject> Obj)
|
||||
{
|
||||
//Transform
|
||||
const FString Units = Obj->GetStringField("units");
|
||||
const float ScaleFactor = ParseScaleFactor(Units);
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* TransformData;
|
||||
if(!Obj->TryGetArrayField("transform", TransformData)) return nullptr;
|
||||
|
||||
|
||||
FMatrix TransformMatrix;
|
||||
for(int32 Row = 0; Row < 4; Row++)
|
||||
for(int32 Col = 0; Col < 4; Col++)
|
||||
{
|
||||
TransformMatrix.M[Row][Col] = TransformData->operator[](Row * 4 + Col)->AsNumber();
|
||||
}
|
||||
TransformMatrix = TransformMatrix.GetTransposed();
|
||||
TransformMatrix.ScaleTranslation(FVector(ScaleFactor));
|
||||
|
||||
//Block Instance
|
||||
const FString ObjectId = Obj->GetStringField("id"), SpeckleType = Obj->GetStringField("speckle_type");
|
||||
|
||||
ASpeckleUnrealActor* BlockInstance = World->SpawnActor<ASpeckleUnrealActor>(ASpeckleUnrealActor::StaticClass(), FTransform(TransformMatrix));
|
||||
|
||||
|
||||
//Block Definition
|
||||
const TSharedPtr<FJsonObject>* BlockDefinitionReference;
|
||||
if(!Obj->TryGetObjectField("blockDefinition", BlockDefinitionReference)) return nullptr;
|
||||
|
||||
const FString RefID = BlockDefinitionReference->operator->()->GetStringField("referencedId");
|
||||
|
||||
const TSharedPtr<FJsonObject> BlockDefinition = SpeckleObjects[RefID];
|
||||
|
||||
//For now just recreate mesh, eventually we should use instanced static mesh
|
||||
const auto Geometries = BlockDefinition->GetArrayField("geometry");
|
||||
|
||||
for(const auto Child : Geometries)
|
||||
{
|
||||
const TSharedPtr<FJsonObject> MeshReference = Child->AsObject();
|
||||
const FString MeshID = MeshReference->GetStringField("referencedId");
|
||||
|
||||
//It is important that ParentObject is the BlockInstance not the BlockDefinition to keep NativeIDs of meshes unique between Block Instances
|
||||
ImportObjectFromCache(BlockInstance, SpeckleObjects[MeshID]);
|
||||
}
|
||||
|
||||
|
||||
return BlockInstance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,196 +1,132 @@
|
||||
#include "SpeckleUnrealManager.h"
|
||||
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "API/Operations/ReceiveOperation.h"
|
||||
#include "Transports/MemoryTransport.h"
|
||||
#include "Transports/ServerTransport.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "API/SpeckleSerializer.h"
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
|
||||
|
||||
// Sets default values
|
||||
ASpeckleUnrealManager::ASpeckleUnrealManager()
|
||||
{
|
||||
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleMaterial(TEXT("Material'/SpeckleUnreal/SpeckleMaterial.SpeckleMaterial'"));
|
||||
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleGlassMaterial(TEXT("Material'/SpeckleUnreal/SpeckleGlassMaterial.SpeckleGlassMaterial'"));
|
||||
|
||||
//When the object is constructed, Get the HTTP module
|
||||
Http = &FHttpModule::Get();
|
||||
|
||||
SetRootComponent(CreateDefaultSubobject<USceneComponent>("Root"));
|
||||
RootComponent->SetRelativeScale3D(FVector(-1,1,1));
|
||||
SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Root")));
|
||||
RootComponent->SetRelativeScale3D(FVector(1,1,1));
|
||||
RootComponent->SetMobility(EComponentMobility::Static);
|
||||
|
||||
Converter = CreateDefaultSubobject<USpeckleConverterComponent>(FName("Converter"));
|
||||
|
||||
DefaultMeshMaterial = SpeckleMaterial.Object;
|
||||
BaseMeshOpaqueMaterial = SpeckleMaterial.Object;
|
||||
BaseMeshTransparentMaterial = SpeckleGlassMaterial.Object;
|
||||
KeepCache = true;
|
||||
DisplayProgressBar = true;
|
||||
ServerUrl = "https://speckle.xyz";
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ASpeckleUnrealManager::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (ImportAtRuntime)
|
||||
ImportSpeckleObject();
|
||||
|
||||
|
||||
if(ImportAtRuntime) Receive();
|
||||
}
|
||||
|
||||
/*Import the Speckle object*/
|
||||
void ASpeckleUnrealManager::ImportSpeckleObject()
|
||||
void ASpeckleUnrealManager::Receive()
|
||||
{
|
||||
const FString UserAgent = FString::Printf(TEXT("Unreal Engine (%s) / %d.%d.%d"), *UGameplayStatics::GetPlatformName(), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ENGINE_PATCH_VERSION);
|
||||
DeleteObjects();
|
||||
|
||||
#if !SUPPRESS_SPECKLE_ANALYTICS
|
||||
|
||||
// Trim parameters
|
||||
ServerUrl.TrimEndInline();
|
||||
while(ServerUrl.RemoveFromEnd("/")) { }
|
||||
StreamID.TrimEndInline();
|
||||
ObjectID.TrimEndInline();
|
||||
AuthToken.TrimEndInline();
|
||||
|
||||
const FString HostApplication = FString::Printf(TEXT("Unreal%%20Engine%%20%d"), ENGINE_MAJOR_VERSION);
|
||||
const FString Action = "receive/manual";
|
||||
FString SpeckleUserID = "No%20SUUID";
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
const FString UserPath = UKismetSystemLibrary::GetPlatformUserDir().LeftChop(10); //remove "Documents/"
|
||||
const FString Dir = FString::Printf(TEXT("%sAppData/Roaming/Speckle/suuid"), *UserPath);
|
||||
FFileHelper::LoadFileToString(SpeckleUserID, *Dir);
|
||||
#endif
|
||||
//TODO MACOS
|
||||
|
||||
//Track page view
|
||||
const FString ViewURL = FString::Printf(
|
||||
TEXT("https://speckle.matomo.cloud/matomo.php?idsite=2&rec=1&apiv=1&uid=%s&action_name=%s&url=http://connectors/%s/%s&urlref=http://connectors/%s/%s&_cvar=%%7B%%22hostApplication%%22:%%20%%22%s%%22%%7D"),
|
||||
*SpeckleUserID,
|
||||
*Action,
|
||||
*HostApplication,
|
||||
*Action,
|
||||
*HostApplication,
|
||||
*Action,
|
||||
*HostApplication
|
||||
);
|
||||
FAnalytics::TrackEvent("unknown", "unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }, {"worldType", FString::FromInt(GetWorld()->WorldType)}});
|
||||
|
||||
const FHttpRequestRef ViewTrackingRequest = Http->CreateRequest();
|
||||
ViewTrackingRequest->SetVerb("POST");
|
||||
ViewTrackingRequest->SetURL(ViewURL);
|
||||
ViewTrackingRequest->SetHeader("User-Agent", UserAgent);
|
||||
ViewTrackingRequest->ProcessRequest();
|
||||
if(!KeepCache && LocalObjectCache.GetObjectRef() != nullptr)
|
||||
{
|
||||
LocalObjectCache.GetObjectRef()->ConditionalBeginDestroy();
|
||||
LocalObjectCache = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
}
|
||||
if(LocalObjectCache.GetObjectRef() == nullptr)
|
||||
{
|
||||
LocalObjectCache = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
}
|
||||
|
||||
//Track receive action
|
||||
const FString EventURL = FString::Printf(
|
||||
TEXT("https://speckle.matomo.cloud/matomo.php?idsite=2&rec=1&apiv=1&uid=%s&_cvar=%%7B%%22hostApplication%%22:%%20%%22%s%%22%%7D&e_c=%s&e_a=%s"),
|
||||
*SpeckleUserID,
|
||||
*HostApplication,
|
||||
*HostApplication,
|
||||
*Action
|
||||
);
|
||||
|
||||
const FHttpRequestRef EventTrackingRequest = Http->CreateRequest();
|
||||
EventTrackingRequest->SetVerb("POST");
|
||||
EventTrackingRequest->SetURL(EventURL);
|
||||
EventTrackingRequest->SetHeader("User-Agent", UserAgent);
|
||||
|
||||
EventTrackingRequest->ProcessRequest();
|
||||
|
||||
#endif
|
||||
UServerTransport* ServerTransport = UServerTransport::CreateServerTransport(ServerUrl,StreamID,AuthToken);
|
||||
|
||||
|
||||
const FString url = ServerUrl + "/objects/" + StreamID + "/" + ObjectID;
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "[Speckle] Downloading: " + url);
|
||||
|
||||
const FHttpRequestRef Request = Http->CreateRequest();
|
||||
|
||||
Request->SetVerb("GET");
|
||||
Request->SetHeader("Accept", TEXT("text/plain"));
|
||||
Request->SetHeader("Authorization", "Bearer " + AuthToken);
|
||||
FString Message = FString::Printf(TEXT("Fetching Objects from Speckle Server: %s"), *ServerUrl);
|
||||
PrintMessage(Message);
|
||||
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamTextResponseReceived);
|
||||
Request->SetURL(url);
|
||||
Request->SetHeader("User-Agent", UserAgent);
|
||||
Request->ProcessRequest();
|
||||
FTransportErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindUObject(this, &ASpeckleUnrealManager::HandleError);
|
||||
FTransportCopyObjectCompleteDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindUObject(this, &ASpeckleUnrealManager::HandleReceive, DisplayProgressBar);
|
||||
|
||||
|
||||
//Receive
|
||||
ServerTransport->CopyObjectAndChildren(ObjectID, LocalObjectCache, CompleteDelegate, ErrorDelegate);
|
||||
}
|
||||
|
||||
void ASpeckleUnrealManager::OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
|
||||
|
||||
|
||||
void ASpeckleUnrealManager::HandleReceive(TSharedPtr<FJsonObject> RootObject, bool DisplayProgress)
|
||||
{
|
||||
if (!bWasSuccessful)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Stream Request failed: " + Response->GetContentAsString());
|
||||
return;
|
||||
}
|
||||
auto responseCode = Response->GetResponseCode();
|
||||
if (responseCode != 200)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, FString::Printf(TEXT("Error response. Response code %d"), responseCode));
|
||||
return;
|
||||
}
|
||||
|
||||
FString response = Response->GetContentAsString();
|
||||
if(RootObject == nullptr) return;
|
||||
|
||||
// ParseIntoArray is very inneficient for large strings.
|
||||
// https://docs.unrealengine.com/en-US/API/Runtime/Core/Containers/FString/ParseIntoArrayLines/index.html
|
||||
// https://answers.unrealengine.com/questions/81697/reading-text-file-line-by-line.html
|
||||
// Can be fixed by setting the size of the array
|
||||
int lineCount = 0;
|
||||
for (const TCHAR* ptr = *response; *ptr; ptr++)
|
||||
if (*ptr == '\n')
|
||||
lineCount++;
|
||||
TArray<FString> lines;
|
||||
lines.Reserve(lineCount);
|
||||
response.ParseIntoArray(lines, TEXT("\n"), true);
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Parsing %d downloaded objects..."), lineCount));
|
||||
|
||||
|
||||
for (const auto& line : lines)
|
||||
const UBase* Res = USpeckleSerializer::DeserializeBase(RootObject, LocalObjectCache);
|
||||
if(IsValid(Res))
|
||||
{
|
||||
FString objectId, objectJson;
|
||||
if (!line.Split("\t", &objectId, &objectJson))
|
||||
continue;
|
||||
TSharedPtr<FJsonObject> jsonObject;
|
||||
TSharedRef<TJsonReader<>> jsonReader = TJsonReaderFactory<>::Create(objectJson);
|
||||
if (!FJsonSerializer::Deserialize(jsonReader, jsonObject))
|
||||
continue;
|
||||
|
||||
SpeckleObjects.Add(objectId, jsonObject);
|
||||
Converter->RecursivelyConvertToNative(this, Res, LocalObjectCache, DisplayProgress, Actors);
|
||||
|
||||
FString Message = FString::Printf(TEXT("Converted %d Actors"), Actors.Num());
|
||||
PrintMessage(Message);
|
||||
}
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Converting %d objects..."), lineCount));
|
||||
|
||||
//World Units setup
|
||||
WorldToCentimeters = 1; //Default value of 1uu = 1cm
|
||||
|
||||
AWorldSettings* WorldSettings;
|
||||
if(IsValid(World = GetWorld() )
|
||||
&& IsValid(WorldSettings = World->GetWorldSettings()) )
|
||||
else
|
||||
{
|
||||
WorldToCentimeters = WorldSettings->WorldToMeters / 100;
|
||||
FString Id;
|
||||
RootObject->TryGetStringField("id", Id);
|
||||
FString Message = FString::Printf(TEXT("Failed to deserialise root object: %s"), *Id);
|
||||
HandleError(Message);
|
||||
}
|
||||
|
||||
|
||||
ImportObjectFromCache(this, SpeckleObjects[ObjectID]);
|
||||
|
||||
for (const auto& m : CreatedObjectsCache)
|
||||
{
|
||||
if(AActor* a = Cast<AActor>(m))
|
||||
a->Destroy();
|
||||
else
|
||||
m->ConditionalBeginDestroy();
|
||||
}
|
||||
|
||||
CreatedObjectsCache = InProgressObjectsCache;
|
||||
InProgressObjectsCache.Empty();
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Objects imported successfully. Created %d Actors"), CreatedObjectsCache.Num()));
|
||||
|
||||
}
|
||||
|
||||
void ASpeckleUnrealManager::HandleError(FString& Message)
|
||||
{
|
||||
PrintMessage(Message, true);
|
||||
}
|
||||
|
||||
void ASpeckleUnrealManager::PrintMessage(FString& Message, bool IsError) const
|
||||
{
|
||||
if(IsError)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Error, TEXT("%s"), *Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogSpeckle, Log, TEXT("%s"), *Message);
|
||||
}
|
||||
|
||||
FColor Color = IsError? FColor::Red : FColor::Green;
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, Color, Message);
|
||||
}
|
||||
|
||||
|
||||
void ASpeckleUnrealManager::DeleteObjects()
|
||||
{
|
||||
ConvertedMaterials.Empty();
|
||||
Converter->FinishConversion();
|
||||
|
||||
for (const auto& m : CreatedObjectsCache)
|
||||
for (AActor* a : Actors)
|
||||
{
|
||||
if(AActor* a = Cast<AActor>(m))
|
||||
a->Destroy();
|
||||
else
|
||||
m->ConditionalBeginDestroy();
|
||||
|
||||
if(IsValid(a)) a->Destroy();
|
||||
}
|
||||
|
||||
CreatedObjectsCache.Empty();
|
||||
Actors.Empty();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Transports/MemoryTransport.h"
|
||||
|
||||
|
||||
#if WITH_DEV_AUTOMATION_TESTS
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMemoryTransportTest, "SpeckleUnreal.Transports.MemoryTransport", EAutomationTestFlags::EditorContext | EAutomationTestFlags::SmokeFilter)
|
||||
|
||||
|
||||
bool FMemoryTransportTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UMemoryTransport* Transport = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
// Test Construction
|
||||
{
|
||||
TestNotNull(TEXT("Constructed object"), Transport);
|
||||
TestTrue(TEXT("Constructed object is valid"), IsValid(Transport));
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> MockObj = MakeShareable(new FJsonObject());
|
||||
FString TestId = TEXT("testidlalalala");
|
||||
FString TestPayloadName = TEXT("Playload");
|
||||
FString TestPayload = TEXT("MyPayloadValue!");
|
||||
MockObj->SetStringField(TestPayloadName, TestPayload);
|
||||
|
||||
// Test Save
|
||||
{
|
||||
Transport->SaveObject(TestId, MockObj);
|
||||
TestTrue(TEXT("Transport with save object to HasObject"), Transport->HasObject(TestId));
|
||||
|
||||
auto Value = Transport->GetSpeckleObject(TestId);
|
||||
TestNotNull(TEXT("Return of getting saved object"), Value.Get());
|
||||
TestEqual(TEXT("Return of getting saved object"), Value, MockObj);
|
||||
TestTrue(TEXT("Returned object to have payload"), Value->HasField(TestPayloadName));
|
||||
TestEqual(TEXT("Returned object's playload"), Value->GetStringField(TestPayloadName), TestPayload);
|
||||
}
|
||||
|
||||
// Test Ids are missing
|
||||
{
|
||||
TArray<FString> MissingIds = {
|
||||
TEXT("NoObjectsWithThisId"),
|
||||
TEXT("testidla"),
|
||||
TEXT("testidlalalalaextrala"),
|
||||
TEXT("testidrararara"),
|
||||
TEXT(""),
|
||||
};
|
||||
|
||||
for(const FString& Id : MissingIds)
|
||||
{
|
||||
TestFalse(TEXT("transport has unknown id -") + Id, Transport->HasObject(Id));
|
||||
auto ValueM = Transport->GetSpeckleObject(Id);
|
||||
TestNull(TEXT("return of unknown id -") + Id, ValueM.Get());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Transports/ServerTransport.h"
|
||||
|
||||
|
||||
#if WITH_DEV_AUTOMATION_TESTS
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FServerTransportTest, "SpeckleUnreal.Transports.ServerTransport", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter)
|
||||
|
||||
|
||||
bool FServerTransportTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
FString ServerUrl = TEXT("https://example.com");
|
||||
FString StreamId = TEXT("1234");
|
||||
FString Token = TEXT("MyAuthToken");
|
||||
auto Transport = UServerTransport::CreateServerTransport(ServerUrl, StreamId, Token);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "FBatchSender.h"
|
||||
|
||||
FBatchSender::FBatchSender(FString& ServerUrl, FString& StreamId, FString& AuthToken, int32 MaxBatchSizeMb,
|
||||
int32 MaxBufferLength, int32 ThreadCount)
|
||||
: ServerUrl(ServerUrl)
|
||||
, StreamId(StreamId)
|
||||
, AuthToken(AuthToken)
|
||||
, MaxBatchSizeMb(MaxBatchSizeMb)
|
||||
, MaxBufferLength(MaxBufferLength)
|
||||
, ThreadCount(ThreadCount)
|
||||
{
|
||||
SendingThread = FRunnableThread::Create(this, TEXT("Speckle Transport Send Thread"), 0);
|
||||
ensure(SendingThread != nullptr);
|
||||
}
|
||||
|
||||
FBatchSender::~FBatchSender()
|
||||
{
|
||||
if (SendingThread)
|
||||
{
|
||||
SendingThread->Kill();
|
||||
delete SendingThread;
|
||||
SendingThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32 FBatchSender::Run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(!ShouldSendThreadRun) return 0;
|
||||
|
||||
TArray<TTuple<FString, FString>> Buffer;
|
||||
FScopeLock Lock(&Lock_SendBuffer);
|
||||
if(SendBuffer.Num() > 0)
|
||||
{
|
||||
Buffer.Append(SendBuffer);
|
||||
SendBuffer.Empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsWriteComplete = true;
|
||||
}
|
||||
|
||||
if(Buffer.Num() <= 0)
|
||||
{
|
||||
FPlatformProcess::Sleep(0.1);
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<FString> ObjectIds;
|
||||
ObjectIds.Reserve(Buffer.Num());
|
||||
for(const auto& Item: Buffer)
|
||||
{
|
||||
ObjectIds.Add(Item.Key);
|
||||
}
|
||||
|
||||
//TODO only send objects that aren't already on the server.
|
||||
//TMap<FString, bool> HasObjects = API.HasObjects(StreamId, ObjectIds);
|
||||
|
||||
//TODO api upload
|
||||
//API.UploadObjects(StreamId, Buffer);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void FBatchSender::Stop()
|
||||
{
|
||||
ShouldSendThreadRun = false;
|
||||
}
|
||||
|
||||
void FBatchSender::EnqueueSend(const FString& Id, const FString& Object)
|
||||
{
|
||||
FScopeLock Lock(&Lock_SendBuffer);
|
||||
SendBuffer.Emplace(Id, Object);
|
||||
IsWriteComplete = false;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SPECKLEUNREAL_API FBatchSender : public FRunnable
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
FString ServerUrl;
|
||||
FString StreamId;
|
||||
FString AuthToken;
|
||||
int32 MaxBatchSizeMb;
|
||||
int32 MaxBufferLength;
|
||||
int32 ThreadCount;
|
||||
|
||||
bool ShouldSendThreadRun = false;
|
||||
bool IsWriteComplete = false;
|
||||
FRunnableThread* SendingThread;
|
||||
FCriticalSection Lock_SendBuffer;
|
||||
TArray<TTuple<const FString, const FString>> SendBuffer;
|
||||
|
||||
public:
|
||||
|
||||
FBatchSender(FString& ServerUrl, FString& StreamId, FString& AuthToken, int32 MaxBatchSizeMb=1, int32 MaxBufferLength = 10, int32 ThreadCount = 4);
|
||||
|
||||
virtual ~FBatchSender() override;
|
||||
|
||||
virtual uint32 Run() override;
|
||||
virtual void Stop() override;
|
||||
|
||||
|
||||
virtual void EnqueueSend(const FString& Id, const FString& Object);
|
||||
virtual void Flush();
|
||||
|
||||
protected:
|
||||
|
||||
virtual void SendingThreadMain();
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Transports/MemoryTransport.h"
|
||||
|
||||
bool UMemoryTransport::HasObject(const FString& ObjectId) const
|
||||
{
|
||||
return SpeckleObjects.Contains(ObjectId);
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> UMemoryTransport::GetSpeckleObject(const FString& ObjectId) const
|
||||
{
|
||||
return SpeckleObjects.FindRef(ObjectId);
|
||||
}
|
||||
|
||||
void UMemoryTransport::SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject)
|
||||
{
|
||||
SpeckleObjects.Add(ObjectId, SerializedObject);
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Transports/ServerTransport.h"
|
||||
|
||||
#include "FBatchSender.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "HttpModule.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Interfaces/IHttpRequest.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "Policies/CondensedJsonPrintPolicy.h"
|
||||
|
||||
|
||||
|
||||
|
||||
TSharedPtr<FJsonObject> UServerTransport::GetSpeckleObject(const FString& ObjectId) const
|
||||
{
|
||||
unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UServerTransport::SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> Object)
|
||||
{
|
||||
FString SerializedObject;
|
||||
{
|
||||
auto Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&SerializedObject);
|
||||
FJsonSerializerWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> SerializerWriter(Writer);
|
||||
FJsonSerializer::Serialize(Object.ToSharedRef(), Writer);
|
||||
}
|
||||
|
||||
BatchSender.EnqueueSend(ObjectId, SerializedObject);
|
||||
}
|
||||
|
||||
void UServerTransport::BeginWrite()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UServerTransport::EndWrite()
|
||||
{
|
||||
BatchSender.Flush();
|
||||
}
|
||||
|
||||
bool UServerTransport::HasObject(const FString& ObjectId) const
|
||||
{
|
||||
unimplemented();
|
||||
return false;
|
||||
}
|
||||
|
||||
void UServerTransport::CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction)
|
||||
{
|
||||
this->OnComplete = OnCompleteAction;
|
||||
this->OnError = OnErrorAction;
|
||||
|
||||
// Create Request for Root Object
|
||||
const FHttpRequestRef Request = FHttpModule::Get().CreateRequest();
|
||||
const FString Endpoint = FString::Printf(TEXT("%s/objects/%s/%s/single"), *ServerUrl, *StreamId, *ObjectId);
|
||||
Request->SetVerb("GET");
|
||||
Request->SetURL(Endpoint);
|
||||
Request->SetHeader("Accept", TEXT("text/plain"));
|
||||
Request->SetHeader("Authorization", "Bearer " + AuthToken);
|
||||
|
||||
|
||||
// Response Callback
|
||||
auto ResponseHandler = [=](FHttpRequestPtr, FHttpResponsePtr Response, bool bWasSuccessful) mutable
|
||||
{
|
||||
if(!bWasSuccessful)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for root object at %s was unsuccessful: %s"), *Response->GetURL(), *Response->GetContentAsString());
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 ResponseCode = Response->GetResponseCode();
|
||||
if (ResponseCode != 200)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for root object at %s failed with HTTP response %d"), *Response->GetURL(), ResponseCode);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
HandleRootObjectResponse(Response->GetContentAsString(), TargetTransport, ObjectId);
|
||||
|
||||
};
|
||||
|
||||
Request->OnProcessRequestComplete().BindLambda(ResponseHandler);
|
||||
|
||||
// Send request
|
||||
const bool RequestSent = Request->ProcessRequest();
|
||||
|
||||
if(!RequestSent)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for root object at %s failed: \nHTTP request failed to start"), *Endpoint);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("GET Request sent for root object at %s, awaiting response"), *Endpoint );
|
||||
FAnalytics::TrackEvent("unknown", ServerUrl, "Receive");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTransport, const FString& ObjectId, const TArray<FString>& ChildrenIds, int32 CStart) const
|
||||
{
|
||||
// Check if all children have been fetched
|
||||
if(ChildrenIds.Num() <= CStart)
|
||||
{
|
||||
ensureAlwaysMsgf(this->OnComplete.ExecuteIfBound(TargetTransport->GetSpeckleObject(ObjectId)), TEXT("Complete handler was not bound properly"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Assemble list of ids to ask for in this request
|
||||
// We want to avoid making requests too large
|
||||
const int32 CEnd = FGenericPlatformMath::Min(ChildrenIds.Num(), CStart + MaxNumberOfObjectsPerRequest);
|
||||
FString ChildrenIdsString;
|
||||
{
|
||||
auto Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&ChildrenIdsString);
|
||||
Writer->WriteArrayStart();
|
||||
for (int32 i = CStart; i < CEnd; i++)
|
||||
{
|
||||
Writer->WriteValue(ChildrenIds[i]);
|
||||
}
|
||||
Writer->WriteArrayEnd();
|
||||
Writer->Close();
|
||||
}
|
||||
|
||||
FString Body;
|
||||
{
|
||||
auto Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&Body);
|
||||
Writer->WriteObjectStart();
|
||||
Writer->WriteValue(TEXT("objects"), ChildrenIdsString);
|
||||
Writer->WriteObjectEnd();
|
||||
Writer->Close();
|
||||
}
|
||||
|
||||
// Create Request
|
||||
const FHttpRequestRef Request = FHttpModule::Get().CreateRequest();
|
||||
{
|
||||
const FString EndPoint = FString::Printf(TEXT("%s/api/getobjects/%s"), *ServerUrl, *StreamId);
|
||||
Request->SetVerb("POST");
|
||||
Request->SetURL(EndPoint);
|
||||
Request->SetHeader("Accept", TEXT("text/plain"));
|
||||
Request->SetHeader("Authorization", "Bearer " + AuthToken);
|
||||
Request->SetHeader("Content-Type", "application/json");
|
||||
Request->SetContentAsString(Body);
|
||||
}
|
||||
|
||||
// Response Callback
|
||||
auto ResponseHandler = [=](FHttpRequestPtr, FHttpResponsePtr Response, bool bWasSuccessful) mutable
|
||||
{
|
||||
if(!bWasSuccessful)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for children of root object %s/%s failed: %s"), *StreamId, *ObjectId, *Response->GetContentAsString());
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 ResponseCode = Response->GetResponseCode();
|
||||
if (ResponseCode != 200)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for children of root object %s/%s failed:\nHTTP response %d"), *StreamId, *ObjectId, ResponseCode);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FString> Lines;
|
||||
const int32 LineCount = SplitLines(Response->GetContentAsString(), Lines);
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Parsing %d downloaded objects..."), LineCount)
|
||||
if(LineCount != CEnd - CStart)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Requested %d objects, but recieved %d"), CEnd - CStart, LineCount);
|
||||
}
|
||||
|
||||
for (const FString& Line : Lines)
|
||||
{
|
||||
FString Id, ObjectJson;
|
||||
if (!Line.Split("\t", &Id, &ObjectJson))
|
||||
continue;
|
||||
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
if(!LoadJson(ObjectJson, JsonObject)) continue;
|
||||
|
||||
|
||||
TargetTransport->SaveObject(Id, JsonObject);
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Processed %d/%d Child objects"), CEnd, ChildrenIds.Num())
|
||||
|
||||
//Iterate again for any missing children
|
||||
FetchChildren(TargetTransport, ObjectId, ChildrenIds , CEnd);
|
||||
};
|
||||
|
||||
Request->OnProcessRequestComplete().BindLambda(ResponseHandler);
|
||||
|
||||
// Send request for children
|
||||
const bool RequestSent = Request->ProcessRequest();
|
||||
|
||||
if(!RequestSent)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Failed to fetch children of root object %s/%s:\nHTTP request failed to start"), *StreamId, *ObjectId);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Requesting %d child objects"), CEnd - CStart);
|
||||
}
|
||||
|
||||
void UServerTransport::HandleRootObjectResponse(const FString& RootObjSerialized, TScriptInterface<ITransport> TargetTransport, const FString& ObjectId) const
|
||||
{
|
||||
TSharedPtr<FJsonObject> RootObj;
|
||||
if(!LoadJson(RootObjSerialized, RootObj))
|
||||
{
|
||||
FString Message = FString::Printf( TEXT("A Root Object %s was recieved but was invalid and could not be deserialied"), *ObjectId);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
TargetTransport->SaveObject(ObjectId, RootObj);
|
||||
|
||||
// Find children are not already in the target transport
|
||||
const auto Closures = RootObj->GetObjectField("__closure")->Values;
|
||||
|
||||
TArray<FString> ChildrenIds;
|
||||
Closures.GetKeys(ChildrenIds);
|
||||
TArray<FString> NewChildrenIds;
|
||||
for(const FString& Id : ChildrenIds)
|
||||
{
|
||||
if(TargetTransport->HasObject(Id)) continue;
|
||||
|
||||
NewChildrenIds.Add(Id);
|
||||
}
|
||||
|
||||
FetchChildren(TargetTransport, ObjectId, NewChildrenIds);
|
||||
}
|
||||
|
||||
int32 UServerTransport::SplitLines(const FString& Content, TArray<FString>& OutLines)
|
||||
{
|
||||
int32 LineCount = 0;
|
||||
for (const TCHAR* ptr = *Content; *ptr; ptr++)
|
||||
if (*ptr == '\n')
|
||||
LineCount++;
|
||||
OutLines.Reserve(LineCount);
|
||||
Content.ParseIntoArray(OutLines, TEXT("\n"), true);
|
||||
return LineCount;
|
||||
}
|
||||
|
||||
bool UServerTransport::LoadJson(const FString& ObjectJson, TSharedPtr<FJsonObject>& OutJsonObject)
|
||||
{
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ObjectJson);
|
||||
return FJsonSerializer::Deserialize(Reader, OutJsonObject);
|
||||
}
|
||||
|
||||
void UServerTransport::InvokeOnError(FString& Message) const
|
||||
{
|
||||
ensureAlwaysMsgf(this->OnError.ExecuteIfBound(Message), TEXT("ServerTransport: Unhandled error - %s"), *Message);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
//#pragma once
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
//
|
||||
// static int DefaultBranchLimit = 10;
|
||||
//
|
||||
// //UFUNCTION(BlueprintPure, Category="Speckle")
|
||||
// //UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
// //void GetAccounts();
|
||||
//
|
||||
// //UFUNCTION(BlueprintPure, Category="Speckle")
|
||||
// //UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
// //Branch StreamGetBranches(FString StreamID, int BranchesLimit = DefaultBranchLimit, int CommitLimit = DefaultBranchLimit);
|
||||
//
|
||||
//
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintAsyncActionBase.h"
|
||||
|
||||
#include "ReceiveOperation.generated.h"
|
||||
|
||||
class ITransport;
|
||||
class UBase;
|
||||
class FJsonObject;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRecieveOperationHandler, UBase*, RootBase, FString, ErrorMessage);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UReceiveOperation : public UBlueprintAsyncActionBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FRecieveOperationHandler OnReceiveSuccessfully;
|
||||
|
||||
/// Called when the total number of children is known
|
||||
//UPROPERTY(BlueprintAssignable)
|
||||
//FRecieveOperationHandler OnChildrenCountKnown;
|
||||
|
||||
/// Called when some deserilization progress is made and TotalConverted has changed
|
||||
//UPROPERTY(BlueprintAssignable)
|
||||
//FRecieveOperationHandler OnProgress;
|
||||
|
||||
/// Called when receive operation has aborted due to some error
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FRecieveOperationHandler OnError;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Speckle|Operations", meta = (WorldContext = "WorldContextObject"))
|
||||
static UReceiveOperation* ReceiveOperation(UObject* WorldContextObject, const FString& ObjectId, TScriptInterface<ITransport> RemoteTransport, TScriptInterface<ITransport> LocalTransport);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
|
||||
protected:
|
||||
void Receive();
|
||||
|
||||
FString ObjectId;
|
||||
TScriptInterface<ITransport> RemoteTransport;
|
||||
TScriptInterface<ITransport> LocalTransport;
|
||||
|
||||
void HandleReceive(TSharedPtr<FJsonObject> Object);
|
||||
|
||||
void HandleError(FString& Message);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintAsyncActionBase.h"
|
||||
|
||||
#include "SendOperation.generated.h"
|
||||
|
||||
|
||||
class ITransport;
|
||||
class UBase;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSendOperationHandler, const FString&, Id, const FString&, ErrorMessage);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API USendOperation : public UBlueprintAsyncActionBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSendOperationHandler OnSendSuccessfully;
|
||||
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSendOperationHandler OnErrorAction;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Speckle|Operations", meta = (WorldContext = "WorldContextObject"))
|
||||
static USendOperation* SendOperation(UObject* WorldContextObject, UBase* Base, TArray<TScriptInterface<ITransport>> Transports);
|
||||
|
||||
virtual void Activate() override;
|
||||
void Send();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
TWeakObjectPtr<UBase> Base;
|
||||
|
||||
TArray<TScriptInterface<ITransport>> Transports;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "SpeckleSerializer.generated.h"
|
||||
|
||||
class UBase;
|
||||
class ITransport;
|
||||
class FJsonObject;
|
||||
|
||||
UCLASS()
|
||||
class USpeckleSerializer : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="Speckle|Serialization")
|
||||
static UBase* DeserializeBaseById(const FString& ObjectId, const TScriptInterface<ITransport> ReadTransport);
|
||||
|
||||
static UBase* DeserializeBase(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport);
|
||||
|
||||
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class FSpeckleTypeConverter
|
||||
{
|
||||
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "SpeckleOperationsComponent.generated.h"
|
||||
|
||||
|
||||
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
||||
class SPECKLEUNREAL_API USpeckleOperationsComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
USpeckleOperationsComponent();
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
|
||||
#include "AggregateConverter.generated.h"
|
||||
|
||||
/**
|
||||
* An Aggregate Converter stores multiple ISpeckleConverter instances.
|
||||
* This allows you to use many converters as one
|
||||
*/
|
||||
UCLASS()
|
||||
|
||||
class SPECKLEUNREAL_API UAggregateConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
// A lazily initialised mapping of SpeckleType -> converters.
|
||||
TMap<TSubclassOf<UBase>, TScriptInterface<ISpeckleConverter>> SpeckleTypeMap;
|
||||
|
||||
public:
|
||||
|
||||
// Array of converters, must be of type ISpeckleConverter
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle|Conversion")
|
||||
TArray<UObject*> SpeckleConverters;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
virtual UObject* ConvertToNativeInternal(const UBase* Object, UWorld* World);
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>&) override;
|
||||
|
||||
virtual bool CanConvertToNative_Implementation(TSubclassOf<UBase> BaseType) override;
|
||||
|
||||
|
||||
virtual void ConvertToSpeckle_Implementation(const UObject* Object, UBase* SpeckleObject) override;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
virtual TScriptInterface<ISpeckleConverter> GetConverter(const TSubclassOf<UBase> BaseType);
|
||||
|
||||
// Validates a given object is a valid ISpeckleConverter
|
||||
static bool CheckValidConverter(const UObject* Converter, bool LogWarning = true);
|
||||
|
||||
void FinishConversion_Internal();
|
||||
virtual void FinishConversion_Implementation() override;
|
||||
|
||||
// Validates changes to SpeckleConverters property, Should be called after modifying SpeckleConverters
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
virtual void OnConvertersChangeHandler();
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
#include "BlockConverter.generated.h"
|
||||
|
||||
class UBlockInstance;
|
||||
|
||||
/**
|
||||
* Converts Speckle Block Instance objects empty native actors with transform.
|
||||
* The Block Definition can then be converted by other converters
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UBlockConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
CONVERTS_SPECKLE_TYPES()
|
||||
public:
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TSubclassOf<AActor> BlockInstanceActorType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TEnumAsByte<EComponentMobility::Type> ActorMobility;
|
||||
|
||||
UBlockConverter();
|
||||
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters) override;
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual AActor* BlockToNative(const UBlockInstance* Block, UWorld* World);
|
||||
|
||||
protected:
|
||||
virtual AActor* CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "UObject/Object.h"
|
||||
|
||||
#include "MaterialConverter.generated.h"
|
||||
|
||||
class URenderMaterial;
|
||||
class UMaterialInterface;
|
||||
|
||||
UENUM()
|
||||
enum EConstMaterialOptions
|
||||
{
|
||||
Never UMETA(DisplayName = "Never"),
|
||||
NotPlay UMETA(DisplayName = "Editor Not Playing"),
|
||||
Always UMETA(DisplayName = "Editor And PIE"),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts Speckle RenderMaterial objects into native Materials
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class SPECKLEUNREAL_API UMaterialConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
CONVERTS_SPECKLE_TYPES()
|
||||
|
||||
public:
|
||||
|
||||
/// Material to be applied to meshes when no RenderMaterial can be converted
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
UMaterialInterface* DefaultMeshMaterial;
|
||||
|
||||
/// Material Parent for converted opaque materials*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
UMaterialInterface* BaseMeshOpaqueMaterial;
|
||||
|
||||
/// Material Parent for converted materials with an opacity less than one
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
UMaterialInterface* BaseMeshTransparentMaterial;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/// Specify when to create Constant materials that can only be created with Editor.
|
||||
/// Otherwise will create dynamic materials
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TEnumAsByte<EConstMaterialOptions> UseConstMaterials;
|
||||
#endif
|
||||
|
||||
/// When generating meshes, materials in this TMap will be used
|
||||
/// instead of converted ones if the key matches the ID of the Object's RenderMaterial. (Takes priority over name matching)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ToNative|Overrides", DisplayName = "By Speckle ID")
|
||||
TMap<FString, UMaterialInterface*> MaterialOverridesById;
|
||||
|
||||
/// When generating meshes, materials in this TSet will be used instead of converted ones if the material name matches the name of the Object's RenderMaterial
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ToNative|Overrides", DisplayName = "By Name")
|
||||
TSet<UMaterialInterface*> MaterialOverridesByName;
|
||||
|
||||
public:
|
||||
|
||||
UMaterialConverter();
|
||||
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld*, TScriptInterface<ISpeckleConverter>&) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative|Overrides")
|
||||
virtual bool TryGetOverride(const URenderMaterial* SpeckleMaterial, UMaterialInterface*& OutMaterial) const;
|
||||
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UMaterialInterface* RenderMaterialToNative(const URenderMaterial* SpeckleMaterial, UPackage* Package);
|
||||
|
||||
virtual void FinishConversion_Implementation() override;
|
||||
|
||||
protected:
|
||||
|
||||
/** Transient cache of materials converted from stream RenderMaterial objects */
|
||||
UPROPERTY(AdvancedDisplay, BlueprintReadOnly, Transient, Category="ToNative|Cache")
|
||||
TMap<FString, UMaterialInterface*> ConvertedMaterials;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="ToNative")
|
||||
virtual UPackage* GetPackage(const FString& ObjectID) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="ToNative")
|
||||
virtual FString RemoveInvalidFileChars(const FString& InString) const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="ToNative")
|
||||
static bool ShouldCreateConstMaterial(TEnumAsByte<EConstMaterialOptions> Options);
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
|
||||
#include "PointCloudConverter.generated.h"
|
||||
|
||||
@@ -12,6 +13,9 @@ class ALidarPointCloudActor;
|
||||
class ULidarPointCloud;
|
||||
class UPointCloud;
|
||||
|
||||
/**
|
||||
* Converts Speckle Point Cloud objects into LidarPointClouds
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UPointCloudConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
@@ -19,22 +23,26 @@ class SPECKLEUNREAL_API UPointCloudConverter : public UObject, public ISpeckleC
|
||||
|
||||
CONVERTS_SPECKLE_TYPES()
|
||||
|
||||
protected:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual ALidarPointCloudActor* CreateActor(const ASpeckleUnrealManager* Manager, ULidarPointCloud* PointCloudData);
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TSubclassOf<ALidarPointCloudActor> PointCloudActorType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TEnumAsByte<EComponentMobility::Type> ActorMobility;
|
||||
|
||||
UPointCloudConverter();
|
||||
|
||||
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>&) override;
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual ALidarPointCloudActor* PointCloudToNative(const UPointCloud* SpecklePointCloud, ASpeckleUnrealManager* Manager);
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual ALidarPointCloudActor* PointCloudToNative(const UPointCloud* SpecklePointCloud, UWorld* World);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UPointCloud* PointCloudToSpeckle(const ULidarPointCloudComponent* Object, ASpeckleUnrealManager* Manager);
|
||||
UFUNCTION(BlueprintCallable, Category="ToSpeckle")
|
||||
virtual UPointCloud* PointCloudToSpeckle(const ULidarPointCloudComponent* Object);
|
||||
|
||||
protected:
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual ALidarPointCloudActor* CreateActor(UWorld* World, ULidarPointCloud* PointCloudData);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
#include "ProceduralMeshConverter.generated.h"
|
||||
|
||||
class UMaterialConverter;
|
||||
class UProceduralMeshComponent;
|
||||
class UMesh;
|
||||
class URenderMaterial;
|
||||
|
||||
|
||||
/**
|
||||
* Converts Speckle Mesh objects into native actors with a procedural mesh component.
|
||||
*
|
||||
* Compared with the StaticMeshConverter, this converter has some serious limitations
|
||||
* - Cannot convert displayValues,
|
||||
* - N-gon faces will be ignored,
|
||||
* - Meshes are transient, and won't persist on level reload
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UProceduralMeshConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
@@ -18,29 +30,26 @@ class SPECKLEUNREAL_API UProceduralMeshConverter : public UObject, public ISpec
|
||||
|
||||
CONVERTS_SPECKLE_TYPES()
|
||||
|
||||
protected:
|
||||
|
||||
virtual AActor* CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TSubclassOf<AActor> MeshActorType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TEnumAsByte<EComponentMobility::Type> ActorMobility;
|
||||
|
||||
// Sets default values for this actor's properties
|
||||
UProceduralMeshConverter();
|
||||
|
||||
|
||||
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters) override;
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual AActor* MeshToNative(const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager);
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual AActor* MeshToNative(const UMesh* SpeckleMesh, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UMesh* MeshToSpeckle(const UProceduralMeshComponent* Object, ASpeckleUnrealManager* Manager);
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UMesh* MeshToSpeckle(const UProceduralMeshComponent* Object);
|
||||
|
||||
virtual AActor* CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
|
||||
};
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
|
||||
#include "StaticMeshConverter.generated.h"
|
||||
|
||||
class UMaterialConverter;
|
||||
class AStaticMeshActor;
|
||||
class UMesh;
|
||||
class URenderMaterial;
|
||||
|
||||
|
||||
/**
|
||||
* Converts Speckle Mesh objects into native Actors with a StaticMesh component
|
||||
*
|
||||
* Can convert multiple Speckle Mesh objects (eg with different materials) in one StaticMesh
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UStaticMeshConverter : public UObject, public ISpeckleConverter
|
||||
{
|
||||
@@ -20,40 +30,86 @@ class SPECKLEUNREAL_API UStaticMeshConverter : public UObject, public ISpeckleCo
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TSubclassOf<AActor> MeshActorType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool UseFullBuild;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool GenerateLightmapUV;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
TEnumAsByte<EComponentMobility::Type> ActorMobility;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
#if WITH_EDITORONLY_DATA
|
||||
// If true, will use the full Editor Only build process
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
bool UseFullBuild;
|
||||
|
||||
// When true, will display FSlowTask progress of editor only build process
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative|EditorOnly")
|
||||
bool DisplayBuildProgressBar;
|
||||
|
||||
// When true, will allow cancellation of FSlowTask progress of editor only build process
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative|EditorOnly")
|
||||
bool AllowCancelBuild;
|
||||
#endif
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
bool BuildSimpleCollision;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ToNative")
|
||||
bool Transient;
|
||||
|
||||
protected:
|
||||
|
||||
virtual AActor* CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative")
|
||||
bool GenerateLightmapUV;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative")
|
||||
int32 MinLightmapResolution;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative")
|
||||
bool BuildReversedIndexBuffer;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative")
|
||||
bool UseFullPrecisionUVs;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category="ToNative")
|
||||
bool RemoveDegeneratesOnBuild;
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
UStaticMeshConverter();
|
||||
|
||||
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
|
||||
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UStaticMesh* MeshToNative(UObject* Outer, const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UMesh* MeshToSpeckle(const UStaticMeshComponent* Object, ASpeckleUnrealManager* Manager);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager);
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters) override;
|
||||
virtual void ConvertToSpeckle_Implementation(const UObject* Object, UBase* SpeckleObject) override;
|
||||
virtual void FinishConversion_Implementation() override;
|
||||
|
||||
// Converts a multiple Speckle Meshes to a native actor of type MeshActorType
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual AActor* MeshesToNativeActor(const UBase* Parent, const TArray<UMesh*>& SpeckleMeshes, UWorld* World, TScriptInterface<ISpeckleConverter>& RenderMaterialConverter);
|
||||
|
||||
// Converts a single Speckle Mesh to a native actor of type MeshActorType
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual AActor* MeshToNativeActor(const UMesh* SpeckleMesh, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter);
|
||||
|
||||
virtual AActor* CreateEmptyActor(UWorld* World, const FTransform& Transform = FTransform::Identity, const FActorSpawnParameters& SpawnParameters =
|
||||
FActorSpawnParameters());
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToSpeckle")
|
||||
virtual TArray<UMesh*> MeshToSpeckle(const UStaticMeshComponent* Object);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
FCriticalSection Lock_StaticMeshesToBuild;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Transient, Category="ToNative")
|
||||
TArray<UStaticMesh*> StaticMeshesToBuild;
|
||||
|
||||
virtual void GenerateMeshParams(UStaticMesh::FBuildMeshDescriptionsParams& MeshParams) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial, UWorld* World,
|
||||
TScriptInterface<ISpeckleConverter>& MaterialConverter) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UStaticMesh* MeshesToNativeMesh(UObject* Outer, const UBase* Parent, const TArray<UMesh*>& SpeckleMeshes, TScriptInterface<ISpeckleConverter>
|
||||
& MaterialConverter);
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UStaticMesh* MeshToNativeMesh(UObject* Outer, const UMesh* SpeckleMesh, TScriptInterface<ISpeckleConverter>& MaterialConverter);
|
||||
};
|
||||
|
||||
@@ -1,48 +1,61 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
|
||||
#include "SpeckleConverter.generated.h"
|
||||
|
||||
class UBase;
|
||||
class ASpeckleUnrealManager;
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE()
|
||||
class USpeckleConverter : public UInterface
|
||||
class SPECKLEUNREAL_API USpeckleConverter : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
* Interfaces for conversion functions (ToSpeckle and ToNative) of a specific native type.
|
||||
* Interface for conversion functions (ToSpeckle and ToNative) of a specific speckle type(s).
|
||||
*
|
||||
* Implementors of this interface provide conversion functions of specific types : Base
|
||||
*/
|
||||
class SPECKLEUNREAL_API ISpeckleConverter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
public:
|
||||
/// Will return true if this converter can convert a given BaseType
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="ToNative")
|
||||
bool CanConvertToNative(TSubclassOf<UBase> BaseType);
|
||||
|
||||
/// Returns the type of Base expected for a given SpeckleType
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
TSubclassOf<UBase> ToNativeBase(const FString& SpeckleType);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
AActor* ConvertToNative(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager);
|
||||
/// Tries to convert a given SpeckleBase into a native Actor
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="ToNative")
|
||||
UObject* ConvertToNative(const UBase* SpeckleBase, UWorld* World, UPARAM(ref) TScriptInterface<ISpeckleConverter>& AvailableConverters);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
UBase* ConvertToSpeckle(const UObject* Object, ASpeckleUnrealManager* Manager);
|
||||
/// Clean up cached resources, and finish any pending build tasks to complete ToNative conversion.
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="ToNative")
|
||||
void FinishConversion();
|
||||
|
||||
|
||||
/// Will return true if this converter can convert a given
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="ToSpeckle")
|
||||
void CanConvertToSpeckle(const AActor* Actor);
|
||||
|
||||
/// Tries to convert a given Actor or Component into a Speckle Base
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="ToSpeckle")
|
||||
void ConvertToSpeckle(const UObject* Object, UBase* SpeckleObject);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#define CONVERTS_SPECKLE_TYPES() \
|
||||
protected: \
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite) \
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion") \
|
||||
TSet<TSubclassOf<UBase>> SpeckleTypes; \
|
||||
public: \
|
||||
virtual bool CanConvertToNative_Implementation(TSubclassOf<UBase> BaseType) override { return SpeckleTypes.Contains(BaseType); } \
|
||||
|
||||
@@ -1,51 +1,62 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
|
||||
#include "SpeckleConverterComponent.generated.h"
|
||||
|
||||
class USpeckleConverter;
|
||||
class ASpeckleUnrealManager;
|
||||
class UBase;
|
||||
class ISpeckleConverter;
|
||||
DECLARE_DYNAMIC_DELEGATE_TwoParams(FActorPredicate, const AActor*, Actor, bool&, OutShouldConvert);
|
||||
|
||||
class ITransport;
|
||||
class ISpeckleConverter;
|
||||
class UBase;
|
||||
class UAggregateConverter;
|
||||
struct FSlowTask;
|
||||
|
||||
/**
|
||||
* An Actor Component for encapsulating recursive conversion of Speckle Objects
|
||||
*/
|
||||
UCLASS(ClassGroup=(Speckle), meta=(BlueprintSpawnableComponent))
|
||||
class SPECKLEUNREAL_API USpeckleConverterComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
// A lazily initialised mapping of SpeckleType -> converters.
|
||||
TMap<TSubclassOf<UBase>, TScriptInterface<ISpeckleConverter>> SpeckleTypeMap;
|
||||
|
||||
public:
|
||||
|
||||
// Array of converters
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle|Conversion")
|
||||
TArray<UObject*> SpeckleConverters;
|
||||
UAggregateConverter* SpeckleConverter;
|
||||
|
||||
// Sets default values for this component's properties
|
||||
USpeckleConverterComponent();
|
||||
|
||||
// Converts the given Base and all children into native actors.
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion|ToNative")
|
||||
UPARAM(DisplayName = "RootActor") AActor* RecursivelyConvertToNative(AActor* AOwner, const UBase* Base, const TScriptInterface<ITransport>& LocalTransport, bool DisplayProgressBar, TArray<AActor*>& OutActors);
|
||||
|
||||
// Validates changes to SpeckleConverters, Should be called after modifying SpeckleConverters
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
virtual void OnConvertersChangeHandler();
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
UBase* ConvertToSpeckle(UObject* Object);
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion|ToNative")
|
||||
virtual void FinishConversion();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
AActor* ConvertToNative(const UBase* Object, ASpeckleUnrealManager* Manager);
|
||||
// Converts the given Base and all children into native actors.
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion|ToSpeckle")
|
||||
virtual UBase* RecursivelyConvertToSpeckle(const TArray<AActor*>& RootActors, FActorPredicate& Predicate);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion|ToSpeckle")
|
||||
virtual void RecurseTreeToSpeckle(const AActor* RootActor, FActorPredicate& Predicate, TArray<UBase*>& OutConverted);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual AActor* RecursivelyConvertToNative_Internal(AActor* AOwner, const UBase* Base, const TScriptInterface<ITransport>& LocalTransport, FSlowTask* Task, TArray<AActor*>& OutActors);
|
||||
|
||||
|
||||
virtual void ConvertChild(const TSharedPtr<FJsonValue> Object, AActor* AOwner, const TScriptInterface<ITransport>& LocalTransport, FSlowTask* Task, TArray<AActor*>& OutActors);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
TScriptInterface<ISpeckleConverter> GetConverter(const TSubclassOf<UBase> BaseType);
|
||||
virtual void AttachConvertedToOwner(AActor* AOwner, const UBase* Base, UObject* Converted);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogSpeckle, Log, All);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/// Anonymous telemetry to help us understand how to make a better Speckle.
|
||||
/// This really helps us to deliver a better open source project and product!
|
||||
class FAnalytics
|
||||
{
|
||||
protected:
|
||||
static const FString MixpanelToken;
|
||||
static const FString MixpanelServer;
|
||||
static const FString VersionedApplicationName;
|
||||
|
||||
public:
|
||||
static void TrackEvent(const FString& Email, const FString& Server, const FString& EventName);
|
||||
static void TrackEvent(const FString& Email, const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties);
|
||||
static FString Hash(const FString& Input);
|
||||
};
|
||||
@@ -1,22 +1,21 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/RegisteringBase.h"
|
||||
#include "DynamicBase.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
|
||||
#include "Base.generated.h"
|
||||
|
||||
class ITransport;
|
||||
class ASpeckleUnrealManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* Base type that all Object Models inherit from
|
||||
*/
|
||||
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class SPECKLEUNREAL_API UBase : public URegisteringBase
|
||||
UCLASS(BlueprintType, meta=(ScriptName="Base (Speckle.Objects)"))
|
||||
class SPECKLEUNREAL_API UBase : public UDynamicBase
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -24,7 +23,7 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
explicit UBase(const wchar_t* SpeckleType): SpeckleType(SpeckleType) {}
|
||||
explicit UBase(const TCHAR* SpeckleType): SpeckleType(FString(SpeckleType)) {}
|
||||
explicit UBase(const FString& SpeckleType) : SpeckleType(SpeckleType) {}
|
||||
|
||||
public:
|
||||
@@ -35,19 +34,45 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FString Id;
|
||||
|
||||
//UPROPERTY(BlueprintReadOnly)
|
||||
//TMap<FString, FString> Properties; //TODO figure out how I'm going to do custom properties
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FString Units;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FString SpeckleType;
|
||||
|
||||
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
Obj->TryGetStringField("id", Id);
|
||||
Obj->TryGetStringField("units", Units);
|
||||
}
|
||||
};
|
||||
bool IsValid = false;
|
||||
DynamicProperties.Reserve(Obj->Values.Num());
|
||||
for(const auto kvp : Obj->Values)
|
||||
{
|
||||
SetDynamicProperty(kvp.Key, kvp.Value->Type);
|
||||
}
|
||||
|
||||
if(Obj->TryGetStringField("id", Id))
|
||||
{
|
||||
IsValid = true;
|
||||
DynamicProperties.Remove("id");
|
||||
}
|
||||
|
||||
if(Obj->TryGetStringField("units", Units)) DynamicProperties.Remove("units");
|
||||
if(Obj->TryGetStringField("speckle_type", SpeckleType)) DynamicProperties.Remove("speckle_type");
|
||||
|
||||
return IsValid;
|
||||
}
|
||||
|
||||
|
||||
virtual void ToJson(TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> Writer)
|
||||
{
|
||||
Writer.WriteValue(TEXT("units"), Units);
|
||||
Writer.WriteValue(TEXT("speckle_type"), SpeckleType);
|
||||
|
||||
for(const auto& p : DynamicProperties)
|
||||
{
|
||||
//Writer.WriteValue(p.Key, p.Value.mmmmmm));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Base.h"
|
||||
#include "BuiltElement.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UBuiltElement : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
static TArray<FString> DisplayValueAliasStrings;
|
||||
void AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager);
|
||||
|
||||
public:
|
||||
|
||||
UBuiltElement() : UBase(TEXT("Objects.Elements")) {}
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
TArray<UBase*> DisplayValue;
|
||||
|
||||
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Base.h"
|
||||
|
||||
#include "DisplayValueElement.generated.h"
|
||||
|
||||
|
||||
class UMesh;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UDisplayValueElement : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
static TArray<FString> DisplayValueAliasStrings;
|
||||
bool AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport);
|
||||
|
||||
public:
|
||||
|
||||
UDisplayValueElement() : UBase(TEXT("Objects.BuiltElements")) {}
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
TArray<UMesh*> DisplayValue;
|
||||
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright AEC Systems Ltd
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "DynamicBase.generated.h"
|
||||
|
||||
|
||||
|
||||
class UBase;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SPECKLEUNREAL_API UDynamicBase : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
TMap<FString, TSharedPtr<FVariant>> DynamicProperties;
|
||||
|
||||
public:
|
||||
|
||||
template <typename T>
|
||||
bool TryGetDynamicProperty(const FString& Key, T& OutValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
bool TryGetDynamicStringProperty(UPARAM(ref) const FString& Key, FString& OutValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
bool TryGetDynamicIntProperty(UPARAM(ref) const FString& Key, int32& OutValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
bool TryGetDynamicFloatProperty(UPARAM(ref) const FString& Key, float& OutValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
bool TryGetDynamicBoolProperty(UPARAM(ref) const FString& Key, bool& OutValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
bool TryGetDynamicBaseProperty(UPARAM(ref) const FString& Key, UBase*& OutValue) const;
|
||||
|
||||
|
||||
template <typename T>
|
||||
void SetDynamicProperty(const FString& Key, T Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
void SetDynamicStringProperty(UPARAM(ref) const FString& Key, FString& OutValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
void SetDynamicIntProperty(UPARAM(ref) const FString& Key, int32& OutValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
void SetDynamicFloatProperty(UPARAM(ref) const FString& Key, float& OutValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
void SetDynamicBoolProperty(UPARAM(ref) const FString& Key, bool& OutValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
void SetDynamicBaseProperty(UPARAM(ref) const FString& Key, UBase*& OutValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
int32 RemoveDynamicProperty(UPARAM(ref) const FString& Key);
|
||||
|
||||
};
|
||||
+6
-4
@@ -1,14 +1,14 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Base.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "Mesh.generated.h"
|
||||
|
||||
class URenderMaterial;
|
||||
class ASpeckleUnrealManager;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -39,7 +39,9 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
URenderMaterial* RenderMaterial;
|
||||
|
||||
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override;
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
|
||||
virtual void ToJson(TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> Writer) override;
|
||||
|
||||
protected:
|
||||
virtual void AlignVerticesWithTexCoordsByIndex();
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Base.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "PointCloud.generated.h"
|
||||
|
||||
/**
|
||||
@@ -16,7 +17,7 @@ class SPECKLEUNREAL_API UPointCloud : public UBase
|
||||
|
||||
public:
|
||||
|
||||
UPointCloud() : UBase(TEXT("Objects.Other.Pointcloud")) {}
|
||||
UPointCloud() : UBase(TEXT("Objects.Geometry.Pointcloud")) {}
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
TArray<FVector> Points;
|
||||
@@ -27,5 +28,5 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
TArray<float> Sizes;
|
||||
|
||||
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override;
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
};
|
||||
+16
-7
@@ -1,20 +1,25 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
|
||||
#include "RegisteringBase.generated.h"
|
||||
#include "ObjectModelRegistry.generated.h"
|
||||
|
||||
class UBase;
|
||||
|
||||
UCLASS(Abstract)
|
||||
class SPECKLEUNREAL_API URegisteringBase : public UObject
|
||||
/**
|
||||
* Handles the mapping of Speckle type to Object Model
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UObjectModelRegistry : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
//static TOptional<TMap<FString, TSubclassOf<UBase>>> TypeRegistry;
|
||||
private:
|
||||
|
||||
static TMap<FString, TSubclassOf<UBase>> TypeRegistry;
|
||||
|
||||
static void GenerateTypeRegistry();
|
||||
@@ -24,7 +29,7 @@ public:
|
||||
|
||||
/// Attempts to find the closest registered TSubclassOf<UBase>
|
||||
/// by recursively stripping away a the most specific name specifier from the given SpeckleType
|
||||
/// until a converter is found or the FString is exhausted.
|
||||
/// until a UBase type is found or the FString is exhausted.
|
||||
///
|
||||
/// Eg. with an input of "Objects.Elements.Wall"
|
||||
/// Will first look for a registered type of "Objects.Elements.Wall"
|
||||
@@ -32,12 +37,16 @@ public:
|
||||
/// Returns nullptr if none found.
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
static TSubclassOf<UBase> FindClosestType(const FString& SpeckleType);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
static bool ParentType(const FString& Type, FString& NextType);
|
||||
|
||||
/// Attempts to find a TSubclassOf<UBase> with a UBase::SpeckleType matching the given SpeckleType param
|
||||
/// Returns nullptr if none found.
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
static TSubclassOf<UBase> GetRegisteredType(const FString& SpeckleType);
|
||||
|
||||
/// Attempts to find a TSubclassOf<UBase> with a UBase::SpeckleType matching the given SpeckleType param
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
|
||||
static bool TryGetRegisteredType(const FString& SpeckleType, TSubclassOf<UBase>& OutType);
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "BlockInstance.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UBlockInstance : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
TArray<UBase*> Geometry;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
|
||||
FMatrix Transform;
|
||||
|
||||
UBlockInstance() : UBase(TEXT("Objects.Other.BlockInstance")) {}
|
||||
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "RenderMaterial.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API URenderMaterial : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
URenderMaterial() : UBase(TEXT("Objects.Other.RenderMaterial")) {}
|
||||
|
||||
UPROPERTY()
|
||||
FString Name;
|
||||
|
||||
UPROPERTY()
|
||||
double Opacity = 1;
|
||||
|
||||
UPROPERTY()
|
||||
double Metalness = 0;
|
||||
|
||||
UPROPERTY()
|
||||
double Roughness = 1;
|
||||
|
||||
UPROPERTY()
|
||||
FLinearColor Diffuse = FColor{221,221,221};
|
||||
|
||||
UPROPERTY()
|
||||
FLinearColor Emissive = FLinearColor::Black;
|
||||
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override
|
||||
{
|
||||
if(!Super::Parse(Obj, ReadTransport)) return false;
|
||||
|
||||
if(Obj->TryGetStringField("name", Name)) DynamicProperties.Remove("name");
|
||||
if(Obj->TryGetNumberField("opacity", Opacity)) DynamicProperties.Remove("opacity");
|
||||
if(Obj->TryGetNumberField("metalness", Metalness)) DynamicProperties.Remove("metalness");
|
||||
if(Obj->TryGetNumberField("roughness", Roughness)) DynamicProperties.Remove("roughness");
|
||||
|
||||
bool IsValid = false;
|
||||
|
||||
int32 ARGB;
|
||||
if(Obj->TryGetNumberField("diffuse", ARGB))
|
||||
{
|
||||
Diffuse = FColor(ARGB);
|
||||
DynamicProperties.Remove("diffuse");
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
if(Obj->TryGetNumberField("emissive", ARGB))
|
||||
{
|
||||
Emissive = FColor(ARGB);
|
||||
DynamicProperties.Remove("emissive");
|
||||
}
|
||||
|
||||
return IsValid;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "View3D.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API UView3D : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FVector Origin;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FVector UpDirection;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|Objects")
|
||||
FVector ForwardDirection;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|Objects")
|
||||
bool IsOrthogonal;
|
||||
|
||||
UView3D() : UBase(TEXT("Objects.BuiltElements.View")) {}
|
||||
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Objects/Base.h"
|
||||
|
||||
#include "RenderMaterial.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API URenderMaterial : public UBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
URenderMaterial() : UBase(TEXT("Objects.Other.RenderMaterial")) {}
|
||||
|
||||
UPROPERTY()
|
||||
FString Name;
|
||||
|
||||
UPROPERTY()
|
||||
double Opacity = 1;
|
||||
|
||||
UPROPERTY()
|
||||
double Metalness = 0;
|
||||
|
||||
UPROPERTY()
|
||||
double Roughness = 1;
|
||||
|
||||
UPROPERTY()
|
||||
FLinearColor Diffuse = FColor{221,221,221};
|
||||
|
||||
UPROPERTY()
|
||||
FLinearColor Emissive = FLinearColor::Black;
|
||||
|
||||
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override
|
||||
{
|
||||
Super::Parse(Obj, Manager);
|
||||
|
||||
Obj->TryGetStringField("name", Name);
|
||||
Obj->TryGetNumberField("opacity", Opacity);
|
||||
Obj->TryGetNumberField("metalness", Metalness);
|
||||
Obj->TryGetNumberField("roughness", Roughness);
|
||||
|
||||
int32 ARGB;
|
||||
if(Obj->TryGetNumberField("diffuse", ARGB))
|
||||
Diffuse = FColor(ARGB);
|
||||
|
||||
if(Obj->TryGetNumberField("emissive", ARGB))
|
||||
Emissive = FColor(ARGB);
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "SpeckleObjectUtils.generated.h"
|
||||
|
||||
class ITransport;
|
||||
class UBase;
|
||||
class FJsonValue;
|
||||
class FJsonObject;
|
||||
class UWorld;
|
||||
class AActor;
|
||||
|
||||
/**
|
||||
* Several helper functions useful for handling JSON Speckle Objects
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API USpeckleObjectUtils : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static TArray<TSharedPtr<FJsonValue>> CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField, const TScriptInterface<ITransport> Transport);
|
||||
|
||||
static bool ResolveReference(const TSharedPtr<FJsonObject> Object, const TScriptInterface<ITransport> Transport, TSharedPtr<FJsonObject>& OutObject);
|
||||
|
||||
static bool TryParseTransform(const TSharedPtr<FJsonObject> SpeckleObject, FMatrix& OutMatrix);
|
||||
|
||||
|
||||
static bool ParseVector(const TSharedPtr<FJsonObject> Object, const TScriptInterface<ITransport> Transport, FVector& OutObject);
|
||||
static bool ParseVectorProperty(const TSharedPtr<FJsonObject> Base, const FString& PropertyName, const TScriptInterface<ITransport> ReadTransport, FVector& OutObject);
|
||||
|
||||
template <typename T>
|
||||
static bool ParseSpeckleObject(const TSharedPtr<FJsonObject> Object, const TScriptInterface<ITransport> Transport, T*& OutObject);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle/ObjectUtils")
|
||||
static float ParseScaleFactor(const FString& UnitsString);
|
||||
|
||||
// Given a Right Handed Z-up transformation matrix (Speckle's system), will create an equivalent Left Handed Z-up FTransform (UE's system)
|
||||
UFUNCTION(BlueprintPure, Category="Speckle/ObjectUtils")
|
||||
static FTransform CreateTransform(UPARAM(ref) const FMatrix& TransformMatrix);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "Class"), Category="Speckle/ObjectUtils")
|
||||
static AActor* SpawnActorInWorld(const TSubclassOf<AActor> Class, UWorld* World, UPARAM(ref) const FTransform& Transform);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "SpeckleUnrealActor.generated.h"
|
||||
|
||||
class UMeshDescriptionBase;
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class SPECKLEUNREAL_API ASpeckleUnrealActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(VisibleAnywhere)
|
||||
USceneComponent* Scene;
|
||||
|
||||
// Sets default values for this actor's properties
|
||||
ASpeckleUnrealActor();
|
||||
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "SpeckleUnrealLayer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class SPECKLEUNREAL_API USpeckleUnrealLayer : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
|
||||
FString LayerName;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
|
||||
FLinearColor LayerColor;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
|
||||
int32 StartIndex;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
|
||||
int32 ObjectCount;
|
||||
|
||||
USpeckleUnrealLayer();
|
||||
|
||||
void Init(FString NewLayerName, int32 NewStartIndex, int32 NewObjectCount);
|
||||
};
|
||||
@@ -1,135 +1,83 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
// json manipulation
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Dom/JsonValue.h"
|
||||
|
||||
// web requests
|
||||
#include "Runtime/Online/HTTP/Public/Http.h"
|
||||
|
||||
#include "SpeckleUnrealLayer.h"
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "SpeckleUnrealManager.generated.h"
|
||||
|
||||
class UServerTransport;
|
||||
class ITransport;
|
||||
class USpeckleConverterComponent;
|
||||
class FJsonObject;
|
||||
|
||||
class URenderMaterial;
|
||||
|
||||
UCLASS(ClassGroup=(Speckle), BlueprintType)
|
||||
/**
|
||||
* An Actor to handle the receiving of Speckle objects into a level
|
||||
*/
|
||||
UCLASS(ClassGroup=(Speckle), AutoCollapseCategories=("Speckle|Conversion"), BlueprintType)
|
||||
class SPECKLEUNREAL_API ASpeckleUnrealManager : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
FHttpModule* Http;
|
||||
|
||||
/* The actual HTTP call */
|
||||
UFUNCTION(CallInEditor, Category = "Speckle")
|
||||
void ImportSpeckleObject();
|
||||
|
||||
UFUNCTION(CallInEditor, Category = "Speckle")
|
||||
void DeleteObjects();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
USpeckleConverterComponent* Converter;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString ServerUrl;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
|
||||
FString ServerUrl {
|
||||
"https://speckle.xyz"
|
||||
};
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString StreamID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
|
||||
FString StreamID {
|
||||
""
|
||||
};
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString ObjectID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
|
||||
FString ObjectID {
|
||||
""
|
||||
};
|
||||
// A Personal Access Token can be created from your Speckle Profile page (Treat tokens like passwords, do not share publicly) - Required for receiving private streams
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", meta=(PasswordField = true))
|
||||
FString AuthToken;
|
||||
|
||||
/** A Personal Access Token can be created from your Speckle Profile page (Treat tokens like passwords, do not share publicly) */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
|
||||
FString AuthToken {
|
||||
""
|
||||
};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
|
||||
// When true, will call `Receive` on BeginPlay
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay)
|
||||
bool ImportAtRuntime;
|
||||
|
||||
// When true, will maintain an in-memory (transient) cache of received speckle objects
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay)
|
||||
bool KeepCache;
|
||||
|
||||
/** Material to be applied to meshes when no RenderMaterial can be converted */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
|
||||
UMaterialInterface* DefaultMeshMaterial;
|
||||
|
||||
/** Material Parent for converted opaque materials*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
|
||||
UMaterialInterface* BaseMeshOpaqueMaterial;
|
||||
|
||||
/** Material Parent for converted materials with an opacity less than one */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
|
||||
UMaterialInterface* BaseMeshTransparentMaterial;
|
||||
// The Conversion component to convert received speckle objects into native Actors
|
||||
UPROPERTY(VisibleAnywhere, Category="Speckle", BlueprintReadWrite)
|
||||
USpeckleConverterComponent* Converter;
|
||||
|
||||
/** When generating meshes, materials in this TMap will be used instead of converted ones if the key matches the ID of the Object's RenderMaterial. (Takes priority over name matching)*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials|Overrides", DisplayName = "By Speckle ID")
|
||||
TMap<FString, UMaterialInterface*> MaterialOverridesById;
|
||||
|
||||
/** When generating meshes, materials in this TSet will be used instead of converted ones if the material name matches the name of the Object's RenderMaterial. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials|Overrides", DisplayName = "By Name")
|
||||
TSet<UMaterialInterface*> MaterialOverridesByName;
|
||||
|
||||
/** Materials converted from stream RenderMaterial objects */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle|Materials")
|
||||
TMap<FString, UMaterialInterface*> ConvertedMaterials;
|
||||
// Used to stagger transport requests, useful when making requests for a large number of child objects
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay)
|
||||
bool DisplayProgressBar;
|
||||
|
||||
|
||||
void OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||
|
||||
// Sets default values for this actor's properties
|
||||
ASpeckleUnrealManager();
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
// Receives specified object from specified Speckle server
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category="Speckle", meta=(DisplayPriority=0))
|
||||
virtual void Receive();
|
||||
|
||||
// Deletes the Actors created by the previous receive operation
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category="Speckle" , meta=(DisplayAfter="Receive"))
|
||||
virtual void DeleteObjects();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
float ParseScaleFactor(const FString& Units) const;
|
||||
|
||||
//TODO move to conversion functions
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool TryGetMaterial(const URenderMaterial* SpeckleMaterial, bool AcceptMaterialOverride,
|
||||
UMaterialInterface*& OutMaterial);
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
bool HasObject(const FString& Id) const;
|
||||
|
||||
UBase* DeserializeBase(const TSharedPtr<FJsonObject> Obj) const;
|
||||
bool ResolveReference(const TSharedPtr<FJsonObject> Object, TSharedPtr<FJsonObject>& OutObject) const;
|
||||
|
||||
TSharedPtr<FJsonObject, ESPMode::Fast> GetSpeckleObject(const FString& Id) const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
UWorld* World;
|
||||
|
||||
float WorldToCentimeters;
|
||||
|
||||
// Cache received Speckle objects
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle")
|
||||
TScriptInterface<ITransport> LocalObjectCache;
|
||||
|
||||
TMap<FString, TSharedPtr<FJsonObject>> SpeckleObjects;
|
||||
// Array of Actors created by the previous receive operation
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle")
|
||||
TArray<AActor*> Actors;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<UObject*> CreatedObjectsCache;
|
||||
TArray<UObject*> InProgressObjectsCache;
|
||||
// Callback when JSON has been received
|
||||
virtual void HandleReceive(TSharedPtr<FJsonObject> RootObject, bool DisplayProgress = false);
|
||||
|
||||
// Callback when error
|
||||
virtual void HandleError(FString& Message);
|
||||
|
||||
void ImportObjectFromCache(AActor* AOwner, const TSharedPtr<FJsonObject> SpeckleObject);
|
||||
|
||||
//AActor* CreateMesh(const TSharedPtr<FJsonObject> Obj, const TSharedPtr<FJsonObject> Parent = nullptr);
|
||||
AActor* CreateBlockInstance(const TSharedPtr<FJsonObject> Obj);
|
||||
//AActor* CreatePointCloud(const TSharedPtr<FJsonObject> Obj);
|
||||
|
||||
virtual void PrintMessage(FString& Message, bool IsError = false) const;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Transport.h"
|
||||
|
||||
#include "MemoryTransport.generated.h"
|
||||
|
||||
/**
|
||||
* An in memory storage of speckle objects.
|
||||
*/
|
||||
UCLASS(Transient, BlueprintType)
|
||||
class SPECKLEUNREAL_API UMemoryTransport : public UObject, public ITransport
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
TMap<FString, TSharedPtr<FJsonObject>> SpeckleObjects;
|
||||
|
||||
public:
|
||||
|
||||
virtual TSharedPtr<FJsonObject> GetSpeckleObject(const FString& ObjectId) const override;
|
||||
virtual void SaveObject(const FString& ObjectId, const FString& SerializedObject) override;
|
||||
|
||||
virtual bool HasObject(const FString& ObjectId) const override;
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId, TScriptInterface<ITransport> TargetTransport, const FTransportCopyObjectCompleteDelegate& OnCompleteAction, const FTransportErrorDelegate& OnErrorAction) override { unimplemented(); }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Speckle|Transports")
|
||||
static UMemoryTransport* CreateEmptyMemoryTransport()
|
||||
{
|
||||
UMemoryTransport* Transport = NewObject<UMemoryTransport>();
|
||||
return Transport;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Transport.h"
|
||||
|
||||
#include "ServerTransport.generated.h"
|
||||
|
||||
class FBatchSender;
|
||||
class FHttpModule;
|
||||
|
||||
// Data for graphQL request for object ids.
|
||||
USTRUCT()
|
||||
struct FObjectIdRequest
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
TArray<FString> Ids;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transport for receiving objects from a Speckle Server
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class SPECKLEUNREAL_API UServerTransport : public UObject, public ITransport
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY()
|
||||
FString ServerUrl;
|
||||
UPROPERTY()
|
||||
FString StreamId;
|
||||
UPROPERTY(meta=(PasswordField))
|
||||
FString AuthToken;
|
||||
|
||||
UPROPERTY()
|
||||
int32 MaxNumberOfObjectsPerRequest = 20000;
|
||||
|
||||
|
||||
FTransportCopyObjectCompleteDelegate OnComplete;
|
||||
FTransportErrorDelegate OnError;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
virtual ~UServerTransport() override;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Speckle|Transports")
|
||||
static UServerTransport* CreateServerTransport(UPARAM(ref) FString& _ServerUrl, UPARAM(ref) FString& _StreamId, UPARAM(ref) FString& _AuthToken)
|
||||
{
|
||||
UServerTransport* Transport = NewObject<UServerTransport>();
|
||||
Transport->ServerUrl = _ServerUrl;
|
||||
Transport->StreamId = _StreamId;
|
||||
Transport->AuthToken = _AuthToken;
|
||||
return Transport;
|
||||
}
|
||||
|
||||
virtual TSharedPtr<FJsonObject> GetSpeckleObject(const FString& ObjectId) const override;
|
||||
|
||||
virtual void SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject) override;
|
||||
virtual void BeginWrite() override;
|
||||
virtual void EndWrite() override;
|
||||
|
||||
virtual bool HasObject(const FString& ObjectId) const override;
|
||||
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) override;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void HandleRootObjectResponse(const FString& RootObjSerialized, TScriptInterface<ITransport> TargetTransport, const FString& ObjectId) const;
|
||||
|
||||
/**
|
||||
* Iteratively fetches chunks of children
|
||||
* @param TargetTransport the transport to store the fetched objects
|
||||
* @param RootObjectId the id of the root object
|
||||
* @param ChildrenIds array of all children to be fetched
|
||||
* @param CStart the index in ChildrenIds of the start point of the current chunk
|
||||
*/
|
||||
virtual void FetchChildren(TScriptInterface<ITransport> TargetTransport,
|
||||
const FString& RootObjectId,
|
||||
const TArray<FString>& ChildrenIds,
|
||||
int32 CStart = 0) const;
|
||||
|
||||
virtual void InvokeOnError(FString& Message) const;
|
||||
|
||||
static bool LoadJson(const FString& ObjectJson, TSharedPtr<FJsonObject>& OutJsonObject);
|
||||
static int32 SplitLines(const FString& Content, TArray<FString>& OutLines);
|
||||
|
||||
FBatchSender BatchSender;
|
||||
|
||||
// FCriticalSection Lock_SendBuffer;
|
||||
// TArray<TTuple<const FString, const FString>> SendBuffer;
|
||||
// FRunnableThread* SendingThread;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
|
||||
#include "Transport.generated.h"
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FTransportCopyObjectCompleteDelegate, TSharedPtr<FJsonObject>);
|
||||
DECLARE_DELEGATE_OneParam(FTransportErrorDelegate, FString&);
|
||||
//DECLARE_DELEGATE_OneParam(FTransportTotalChildrenCountKnownDelegate, int32);
|
||||
//DECLARE_DELEGATE_OneParam(FTransportProgressDelegate, int32);
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE(Blueprintable)
|
||||
class UTransport : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SPECKLEUNREAL_API ITransport
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
//TODO consider changing SerializedObject to FString&
|
||||
virtual void SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject) = 0;
|
||||
|
||||
//virtual void SaveObjectFromTransport(FString& ObjectID, TScriptInterface<ITransport> SourceTransport) = 0;
|
||||
|
||||
virtual TSharedPtr<FJsonObject> GetSpeckleObject(const FString& ObjectId) const = 0;
|
||||
virtual bool HasObject(const FString& ObjectId) const;
|
||||
|
||||
virtual void BeginWrite();
|
||||
virtual void EndWrite();
|
||||
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) = 0;
|
||||
|
||||
};
|
||||
@@ -26,7 +26,7 @@ public class SpeckleUnreal : ModuleRules
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"Http",
|
||||
"HTTP",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"ProceduralMeshComponent",
|
||||
|
||||
@@ -3,14 +3,28 @@
|
||||
|
||||
#include "Conversion/ConverterFactory.h"
|
||||
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
#include "ClassViewerModule.h"
|
||||
#include "InterfaceClassFilter.h"
|
||||
#include "SpeckleUnrealEditorModule.h"
|
||||
#include "Kismet2/SClassPickerDialog.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealEditorModule"
|
||||
|
||||
UConverterFactory::UConverterFactory(UClass* SupportedClass)
|
||||
UConverterFactory::UConverterFactory(UClass* SClass)
|
||||
: UConverterFactory()
|
||||
{
|
||||
SupportedClass = SClass;
|
||||
ConverterClass = SClass;
|
||||
}
|
||||
|
||||
UConverterFactory::UConverterFactory()
|
||||
{
|
||||
bCreateNew = true;
|
||||
bEditAfterNew = true;
|
||||
this->SupportedClass = SupportedClass;
|
||||
SupportedClass = USpeckleConverter::StaticClass(); //This is not super valid, but it doesn't matter
|
||||
|
||||
ConverterClass = nullptr;
|
||||
}
|
||||
|
||||
uint32 UConverterFactory::GetMenuCategories() const
|
||||
@@ -19,11 +33,56 @@ uint32 UConverterFactory::GetMenuCategories() const
|
||||
return SpeckleCategory;
|
||||
}
|
||||
|
||||
bool UConverterFactory::ConfigureProperties()
|
||||
{
|
||||
// Null the CurveClass so we can get a clean class
|
||||
ConverterClass = nullptr;
|
||||
|
||||
// Load the classviewer module to display a class picker
|
||||
FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer");
|
||||
|
||||
// Fill in options
|
||||
FClassViewerInitializationOptions Options;
|
||||
Options.Mode = EClassViewerMode::ClassPicker;
|
||||
|
||||
TSharedPtr<FInterfaceClassFilter> Filter = MakeShareable(new FInterfaceClassFilter);
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
Options.ClassFilters.Emplace(Filter.ToSharedRef());
|
||||
#else
|
||||
Options.ClassFilter = Filter;
|
||||
#endif
|
||||
|
||||
Filter->InterfaceThatMustBeImplemented = USpeckleConverter::StaticClass();
|
||||
Filter->bAllowAbstract = false;
|
||||
Filter->ClassPropertyMetaClass = UObject::StaticClass();
|
||||
Filter->AllowedClassFilters = {UObject::StaticClass()};
|
||||
const FText TitleText = LOCTEXT("CreateConverterOptions", "Pick Converter Class");
|
||||
UClass* ChosenClass = nullptr;
|
||||
const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UObject::StaticClass());
|
||||
|
||||
if ( bPressedOk )
|
||||
{
|
||||
ConverterClass = ChosenClass;
|
||||
}
|
||||
|
||||
return bPressedOk;
|
||||
}
|
||||
|
||||
|
||||
UObject* UConverterFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags,
|
||||
UObject* Context, FFeedbackContext* Warn)
|
||||
UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
UObject* NewObjectAsset = NewObject<UObject>(InParent, Class, Name, Flags);
|
||||
UObject* NewObjectAsset = nullptr;
|
||||
if(ConverterClass != nullptr)
|
||||
{
|
||||
NewObjectAsset = NewObject<UObject>(InParent, ConverterClass, Name, Flags);
|
||||
}
|
||||
return NewObjectAsset;
|
||||
}
|
||||
|
||||
FText UConverterFactory::GetDisplayName() const
|
||||
{
|
||||
return LOCTEXT("ConverterFactoryDisplayName", "Speckle Converter");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "IAssetTools.h"
|
||||
|
||||
#include "Conversion/ConverterActions.h"
|
||||
#include "Conversion/ConverterFactory.h"
|
||||
#include "Conversion/SpeckleConverter.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealEditorModule"
|
||||
|
||||
@@ -26,24 +26,22 @@ void FSpeckleUnrealEditorModule::StartupModule()
|
||||
// Register Speckle Category
|
||||
SpeckleAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Speckle")), LOCTEXT("SpeckleCategoryText","Speckle"));
|
||||
|
||||
// Finds all class definitions : UConverterFactory and creates a FConverterActions for their supported class.
|
||||
|
||||
// See UAssetToolsImpl::GetNewAssetFactories() for reference
|
||||
for (TObjectIterator<UClass> It; It; ++It)
|
||||
{
|
||||
const UClass* Class = *It;
|
||||
if (Class->IsChildOf(UConverterFactory::StaticClass()) &&
|
||||
!Class->HasAnyClassFlags(CLASS_Abstract))
|
||||
{
|
||||
const UConverterFactory* Factory = Class->GetDefaultObject<UConverterFactory>();
|
||||
UClass* Class = *It;
|
||||
|
||||
if (Factory->ShouldShowInNewMenu() &&
|
||||
ensure(!Factory->GetDisplayName().IsEmpty()))
|
||||
{
|
||||
AssetTools.RegisterAssetTypeActions(MakeShareable(new FConverterActions(Factory->SupportedClass, SpeckleAssetCategoryBit)));
|
||||
}
|
||||
// Create FConverterActions for USpeckleConverter types
|
||||
if ( Class->ImplementsInterface(USpeckleConverter::StaticClass())
|
||||
&& !Class->HasAnyClassFlags(CLASS_Abstract))
|
||||
{
|
||||
AssetTools.RegisterAssetTypeActions(MakeShareable(new FConverterActions(Class, SpeckleAssetCategoryBit)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AssetTools.RegisterAssetTypeActions(MakeShareable(new FConverterActions(USpeckleConverter::StaticClass(), SpeckleAssetCategoryBit)));
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AssetTypeActions_Base.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Asset action for ISpeckleConverter implementors.
|
||||
*/
|
||||
class FConverterActions : public FAssetTypeActions_Base
|
||||
{
|
||||
|
||||
@@ -1,62 +1,31 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "Conversion/Converters/ProceduralMeshConverter.h"
|
||||
#include "Conversion/Converters/StaticMeshConverter.h"
|
||||
#include "Conversion/Converters/PointCloudConverter.h"
|
||||
|
||||
#include "ConverterFactory.generated.h"
|
||||
|
||||
/**
|
||||
* This class is designed to reduce the boiler plate required to define a UFactory for ISpeckleTypeConverter types.
|
||||
*
|
||||
* For each ISpeckleTypeConverter we want to appear in the "Create Advanced Asset" context menu
|
||||
* A class definition : UConverterFactory should be created (see below examples)
|
||||
*
|
||||
* A FConverterAction : FAssetTypeActions_Base instance will automatically be created by
|
||||
* FSpeckleUnrealEditorModule::StartupModule for each class definition : UConverterFactory.
|
||||
* So we don't need to worry about manually registering the SupportedClass types with FAssetToolsModule.
|
||||
* Factory for ISpeckleConverter classes to appear in the asset creation context menu.
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
UCLASS()
|
||||
class SPECKLEUNREALEDITOR_API UConverterFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
explicit UConverterFactory(UClass* SupportedClass);
|
||||
|
||||
public:
|
||||
UConverterFactory() : Super() {}
|
||||
UPROPERTY(EditAnywhere, Category="Speckle|Factories", meta = (MustImplement = USpeckleConverter))
|
||||
UClass* ConverterClass;
|
||||
|
||||
UConverterFactory();
|
||||
|
||||
virtual uint32 GetMenuCategories() const override;
|
||||
|
||||
virtual bool ConfigureProperties() override;
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class SPECKLEUNREALEDITOR_API UStaticMeshConverterFactory : public UConverterFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UStaticMeshConverterFactory() : Super( UStaticMeshConverter::StaticClass() ) { }
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class SPECKLEUNREALEDITOR_API UProceduralMeshConverterFactory : public UConverterFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UProceduralMeshConverterFactory() : Super( UProceduralMeshConverter::StaticClass() ) { }
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class SPECKLEUNREALEDITOR_API UPointCloudConverterFactory : public UConverterFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPointCloudConverterFactory() : Super( UPointCloudConverter::StaticClass() ) { }
|
||||
};
|
||||
virtual FText GetDisplayName() const override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ClassViewerModule.h"
|
||||
#include "ClassViewerFilter.h"
|
||||
|
||||
|
||||
/**
|
||||
* Class filter for classes that implement a given interface
|
||||
* based on FPropertyEditorClassFilter
|
||||
*/
|
||||
class FInterfaceClassFilter : public IClassViewerFilter
|
||||
{
|
||||
public:
|
||||
/** The meta class for the property that classes must be a child-of. */
|
||||
const UClass* ClassPropertyMetaClass;
|
||||
|
||||
/** The interface that must be implemented. */
|
||||
const UClass* InterfaceThatMustBeImplemented;
|
||||
|
||||
/** Whether or not abstract classes are allowed. */
|
||||
bool bAllowAbstract;
|
||||
|
||||
/** Classes that can be picked */
|
||||
TArray<UClass*> AllowedClassFilters;
|
||||
|
||||
/** Classes that can't be picked */
|
||||
TArray<UClass*> DisallowedClassFilters;
|
||||
|
||||
virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs ) override
|
||||
{
|
||||
return IsClassAllowedHelper(InClass);
|
||||
}
|
||||
|
||||
virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InBlueprint, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override
|
||||
{
|
||||
return IsClassAllowedHelper(InBlueprint);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <typename TClass>
|
||||
bool IsClassAllowedHelper(TClass InClass)
|
||||
{
|
||||
bool bMatchesFlags = !InClass->HasAnyClassFlags(CLASS_Hidden | CLASS_HideDropDown | CLASS_Deprecated) &&
|
||||
(bAllowAbstract || !InClass->HasAnyClassFlags(CLASS_Abstract));
|
||||
|
||||
if (bMatchesFlags && InClass->IsChildOf(ClassPropertyMetaClass)
|
||||
&& (!InterfaceThatMustBeImplemented || InClass->ImplementsInterface(InterfaceThatMustBeImplemented)))
|
||||
{
|
||||
auto PredicateFn = [InClass](const UClass* Class)
|
||||
{
|
||||
return InClass->IsChildOf(Class);
|
||||
};
|
||||
|
||||
if (DisallowedClassFilters.FindByPredicate(PredicateFn) == nullptr &&
|
||||
(AllowedClassFilters.Num() == 0 || AllowedClassFilters.FindByPredicate(PredicateFn) != nullptr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,15 +1,19 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FSpeckleUnrealEditorModule : public IModuleInterface
|
||||
{
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
protected:
|
||||
uint32 SpeckleAssetCategoryBit = 0;
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
public:
|
||||
virtual uint32 GetSpeckleAssetCategory() const;
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,7 @@ public class SpeckleUnrealEditor : ModuleRules
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"Http",
|
||||
"HTTP",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"UnrealEd",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "0.0.1",
|
||||
"FriendlyName": "SpeckleUnreal",
|
||||
"VersionName": "2.5.2",
|
||||
"FriendlyName": "Speckle Unreal",
|
||||
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
|
||||
"Category": "AEC",
|
||||
"CreatedBy": "Speckle",
|
||||
"CreatedByURL": "https://speckle.systems/",
|
||||
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"SupportURL": "https://speckle.community/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
@@ -18,12 +18,14 @@
|
||||
{
|
||||
"Name": "SpeckleUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac", "IOS", "Android" ]
|
||||
},
|
||||
{
|
||||
"Name": "SpeckleUnrealEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit"
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
|
||||
Reference in New Issue
Block a user