This commit is contained in:
Cristian Balas 2021-04-14 15:16:04 +03:00
Родитель e3ef0e8a1e
Коммит 52a2a153ca
14 изменённых файлов: 349 добавлений и 305 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -37,3 +37,4 @@ SpeckleUnrealProject/Plugins/SpeckleUnreal/Binaries/
SpeckleUnrealProject/Plugins/SpeckleUnreal/Intermediate/
SpeckleUnrealProject/Saved/
SpeckleUnrealProject/.vs/
.idea/

Просмотреть файл

@ -1,29 +1,13 @@
# SpeckleUnreal
# speckle-unreal
[![Version](https://img.shields.io/badge/Version-v0.1.0-orange)](https://github.com/mobiusnode/SpeckleUnreal) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](http://makeapullrequest.com)
[![Version](https://img.shields.io/badge/Version-v0.1.0-orange)](https://github.com/specklesystems/speckle-unreal) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](http://makeapullrequest.com)
Our Team is developing a Speckle plugin and interoperability transport schema for UE4. Our goal is to enable Revit/Dynamo and Rhino/Grasshopper to send + receive geometry to UE4 for visualization. Our current priority is to establish and release a data sender (TO UE4). Were also working on receiver methods, however our initial focus is on Rhino/Grasshopper to UE4 translation and the attachment of UE4-specific metadata to the core JSON blobs in transport.
Plugin for Unreal Engine 4 to import objects from Speckle v2.
In this repository you will find the source code, assets and project settings of the SpeckleUnreal plugin for Unreal Engine app development (Unreal Engine 4.25.1 or newer recommended).
# Useful Links
Use the following links to access resources related to bug reporting, issues, feature requests, and general questions regarding SpeckleUnreal. Future releases may not contain these links & note.
## Speckle Unreal Server
https://speckle.mobiusnode.io
## Discourse Forums (Bugs, Issues, etc.)
https://discourse.mobiusnode.io
## SpeckleUnreal Slack Workspace
https://speckle-works-unreal.slack.com
## YouTub Demo & Tutorial - Getting Started
https://bit.ly/3ehHQE6
## NOTICE
* Tested on Windows and MacOS and Linux.
* Tested on Windows, Unreal Engine v4.26 and Visual Studio Community 2019
* Only displays meshes. Breps are converted using their display values.
* Does not use the Speckle Kit workflow as conversions all happen in C++.
@ -37,20 +21,5 @@ https://bit.ly/3ehHQE6
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.
---
## Roadmap
> Roadmap is subject to change. Last reviewed 10th of July 2020.
| Version | Defining Feature |
| ------- | -------------------------------------------------------------------------------- |
| ~0.1~ | ~First prototype release as Unreal Engine plugin~ |
| 0.2 | New component workflow and custom materials assigned via inspector~ |
| 0.3 | Spawn geometry in transform heirarchy based on layer data |
| 0.4 | User login API, get Stream API and no dependency on a local install of Speckle |
| 0.5 | Rendering Rule API |
| 0.6 | Support Lines, Points, Numbers and Text|
| 0.7 | Local caching of Speckle streams |
| 0.8 | Implement Sender API |
| 1.0 | Production ready (out of preview) |
## 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 @@

Двоичные данные
SpeckleUnrealProject/Content/NewMap_BuiltData.uasset Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.7 KiB

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Просмотреть файл

@ -3,10 +3,17 @@
// 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();
// default conversion is millimeters to centimeters because streams tend to be in ml and unreal is in cm by defaults
ScaleFactor = 0.1;
World = GetWorld();
DefaultMeshOpaqueMaterial = SpeckleMaterial.Object;
DefaultMeshTransparentMaterial = SpeckleGlassMaterial.Object;
}
// Called when the game starts or when spawned
@ -15,242 +22,308 @@ void ASpeckleUnrealManager::BeginPlay()
Super::BeginPlay();
World = GetWorld();
GetStream();
if (ImportAtRuntime)
ImportSpeckleObject();
}
void ASpeckleUnrealManager::SetUpGetRequest(TSharedRef<IHttpRequest> Request)
/*Import the Speckle object*/
void ASpeckleUnrealManager::ImportSpeckleObject()
{
FString url = ServerUrl + "/objects/" + StreamID + "/" + ObjectID;
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "[Speckle] Downloading: " + url);
FHttpRequestRef Request = Http->CreateRequest();
Request->SetVerb("GET");
Request->SetHeader("Content-Type", TEXT("application/json"));
Request->SetHeader("Authorization", AuthToken);
}
/*Http call*/
void ASpeckleUnrealManager::GetStream()
{
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Downloading: " + StreamID);
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID);
Request->SetHeader("Accept", TEXT("text/plain"));
Request->SetHeader("Authorization", "Bearer " + AuthToken);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamTextResponseReceived);
Request->SetURL(url);
Request->ProcessRequest();
}
/*Assigned function on successfull http call*/
void ASpeckleUnrealManager::OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
void ASpeckleUnrealManager::OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Stream Request failed");
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;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
FString response = Response->GetContentAsString();
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
// 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);
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Parsing %d downloaded objects..."), lineCount));
for (auto& line : lines)
{
//Get the value of the json object by field name
FString ResponseMessage = ResponseJsonObject->GetStringField("message");
TSharedPtr<FJsonObject> Stream = ResponseJsonObject->GetObjectField("resource");
FString StreamName = Stream->GetStringField("name");
FString StreamDescription = Stream->GetStringField("description");
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;
FString Units = ResponseJsonObject->GetObjectField("baseProperties")->GetStringField("units").ToLower();
// unreal engine units are in cm by default but the conversion is editable by users so
// this needs to be accounted for later.
if (Units == "meters" || Units == "metres")
ScaleFactor = 100;
if (Units == "centimeters" || Units == "centimetres")
ScaleFactor = 1;
if (Units == "millimeters" || Units == "millimetres")
ScaleFactor = 0.1;
if (Units == "yards")
ScaleFactor = 91.4402757;
if (Units == "feet")
ScaleFactor = 30.4799990;
if (Units == "inches")
ScaleFactor = 2.5399986;
TArray<TSharedPtr<FJsonValue>> LayersInStream = Stream->GetArrayField("layers");
SpeckleUnrealLayers = TArray<USpeckleUnrealLayer*>();
for (size_t i = 0; i < LayersInStream.Num(); i++)
{
TSharedPtr<FJsonObject> LayerObject = LayersInStream[i]->AsObject();
FString LayerName = LayerObject->GetStringField("name");
int32 StartIndex = LayerObject->GetIntegerField("startIndex");
int32 ObjectCount = LayerObject->GetIntegerField("objectCount");
//USpeckleUnrealLayer NewLayer = USpeckleUnrealLayer(LayerName, StartIndex, ObjectCount);
USpeckleUnrealLayer* NewLayer = NewObject<USpeckleUnrealLayer> (this);
NewLayer->Init(LayerName, StartIndex, ObjectCount);
SpeckleUnrealLayers.Add(NewLayer);
}
//Output it to the engine
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Units: " + FString::SanitizeFloat(ScaleFactor));
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Green, "Status: " + ResponseMessage);
GEngine->AddOnScreenDebugMessage(2, 5.0f, FColor::Green, "Name: " + StreamName);
GEngine->AddOnScreenDebugMessage(3, 5.0f, FColor::Green, "Description: " + StreamDescription);
TArray<TSharedPtr<FJsonValue>> ObjectPlaceholderArray = Stream->GetArrayField("objects");
GetStreamObjects(ObjectPlaceholderArray.Num());
SpeckleObjects.Add(objectId, jsonObject);
}
else
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Converting %d objects..."), lineCount));
ImportObjectFromCache(SpeckleObjects[ObjectID]);
for (auto& m : CreatedSpeckleMeshes)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from stream response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
if (InProgressSpeckleMeshes.Contains(m.Key) && InProgressSpeckleMeshes[m.Key] == m.Value)
continue;
if (m.Value->Scene) // actors removed by the user in the editor have the Scene set to nullptr
m.Value->Destroy();
}
CreatedSpeckleMeshes = InProgressSpeckleMeshes;
InProgressSpeckleMeshes.Empty();
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Objects imported successfully. Created %d Actors"), CreatedSpeckleMeshes.Num()));
}
void ASpeckleUnrealManager::GetStreamObjects(int32 objectCount)
ASpeckleUnrealMesh* ASpeckleUnrealManager::GetExistingMesh(const FString &objectId)
{
int32 RequestLimit = 1;
CurrentObjectIndex = 0;
LayerIndex = 0;
if (InProgressSpeckleMeshes.Contains(objectId))
return InProgressSpeckleMeshes[objectId];
for (size_t i = 0; i < objectCount; i += RequestLimit)
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamObjectResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID + "/objects?limit=" + FString::FromInt(RequestLimit) + "&offset=" + FString::FromInt(i));
Request->ProcessRequest();
}
if(!CreatedSpeckleMeshes.Contains(objectId))
return nullptr;
ASpeckleUnrealMesh* meshActor = CreatedSpeckleMeshes[objectId];
// Check if actor has been deleted by the user
if (!meshActor || !meshActor->Scene)
return nullptr;
return meshActor;
}
void ASpeckleUnrealManager::OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
void ASpeckleUnrealManager::ImportObjectFromCache(const TSharedPtr<FJsonObject> speckleObj)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Object Request failed");
if (!speckleObj->HasField("speckle_type"))
return;
if (speckleObj->GetStringField("speckle_type") == "reference" && speckleObj->HasField("referencedId")) {
TSharedPtr<FJsonObject> referencedObj;
if (SpeckleObjects.Contains(speckleObj->GetStringField("referencedId")))
ImportObjectFromCache(SpeckleObjects[speckleObj->GetStringField("referencedId")]);
return;
}
if (!speckleObj->HasField("id"))
return;
FString objectId = speckleObj->GetStringField("id");
FString speckleType = speckleObj->GetStringField("speckle_type");
// UE_LOG(LogTemp, Warning, TEXT("Importing object %s (type %s)"), *objectId, *speckleType);
if (speckleObj->GetStringField("speckle_type") == "Objects.Geometry.Mesh") {
ASpeckleUnrealMesh* mesh = GetExistingMesh(objectId);
if (!mesh)
mesh = CreateMesh(speckleObj);
InProgressSpeckleMeshes.Add(objectId, mesh);
return;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
if (speckleObj->HasField("@displayMesh"))
{
UMaterialInterface* explicitMaterial = nullptr;
if (speckleObj->HasField("renderMaterial"))
explicitMaterial = CreateMaterial(speckleObj->GetObjectField("renderMaterial"));
int32 Offset = FCString::Atoi (*Request->GetURLParameter("offset"));
//Get the value of the json object by field name
TArray<TSharedPtr<FJsonValue>> StreamObjects = ResponseJsonObject->GetArrayField("resources");
// Check if the @displayMesh is an object or an array
const TSharedPtr<FJsonObject> *meshObjPtr;
const TArray<TSharedPtr<FJsonValue>> *meshArrayPtr;
for (size_t i = 0; i < SpeckleUnrealLayers.Num(); i++)
if (speckleObj->TryGetObjectField("@displayMesh", meshObjPtr))
{
if (Offset >= SpeckleUnrealLayers[i]->StartIndex)
{
if (Offset < (SpeckleUnrealLayers[i]->StartIndex + SpeckleUnrealLayers[i]->ObjectCount))
LayerIndex = i;
}
TSharedPtr<FJsonObject> meshObj = SpeckleObjects[(*meshObjPtr)->GetStringField("referencedId")];
ASpeckleUnrealMesh* mesh = GetExistingMesh(objectId);
if (!mesh)
mesh = CreateMesh(meshObj, explicitMaterial);
InProgressSpeckleMeshes.Add(objectId, mesh);
}
for (size_t i = 0; i < StreamObjects.Num(); i++)
else if (speckleObj->TryGetArrayField("@displayMesh", meshArrayPtr))
{
TSharedPtr<FJsonObject> StreamObject = StreamObjects[i].Get()->AsObject();
TSharedPtr<FJsonObject> ObjectToConvert = StreamObject;
FString objectType = ObjectToConvert->GetStringField("type");
if (objectType.ToLower().Contains("brep"))
for (auto& meshObjValue : *meshArrayPtr)
{
ObjectToConvert = StreamObject->GetObjectField("displayValue");
objectType = ObjectToConvert->GetStringField("type");
}
if (objectType.ToLower().Contains("mesh"))
{
AActor* ActorInstance = World->SpawnActor(MeshActor);
ASpeckleUnrealMesh* MeshInstance = (ASpeckleUnrealMesh*)ActorInstance;
TArray<TSharedPtr<FJsonValue>> ObjectVertices = ObjectToConvert->GetArrayField("vertices");
TArray<TSharedPtr<FJsonValue>> ObjectFaces = ObjectToConvert->GetArrayField("faces");
TArray<FVector> ParsedVerticies;
for (size_t j = 0; j < ObjectVertices.Num(); j += 3)
{
ParsedVerticies.Add(FVector
(
(float)(ObjectVertices[j].Get()->AsNumber()) * -1,
(float)(ObjectVertices[j + 1].Get()->AsNumber()),
(float)(ObjectVertices[j + 2].Get()->AsNumber())
) * ScaleFactor);
}
//convert mesh faces into triangle array regardless of whether or not they are quads
TArray<int32> ParsedTriangles;
int32 j = 0;
while (j < ObjectFaces.Num())
{
if (ObjectFaces[j].Get()->AsNumber() == 0)
{
//Triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
j += 4;
}
else
{
//Quads to triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 4].Get()->AsNumber());
j += 5;
}
}
if (RandomColorsPerLayer)
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, SpeckleUnrealLayers[LayerIndex]->LayerColor);
else
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, FLinearColor::White);
UE_LOG(LogTemp, Warning, TEXT("%d"), Offset);
UE_LOG(LogTemp, Warning, TEXT("%s"), *SpeckleUnrealLayers[LayerIndex]->LayerName);
FString meshId = meshObjValue->AsObject()->GetStringField("referencedId");
FString unrealMeshKey = objectId + meshId;
ASpeckleUnrealMesh* mesh = GetExistingMesh(unrealMeshKey);
if (!mesh)
mesh = CreateMesh(SpeckleObjects[meshId], explicitMaterial);
InProgressSpeckleMeshes.Add(unrealMeshKey, mesh);
}
}
}
else
// Go recursively into all object fields (except @displayMesh)
for (auto& kv : speckleObj->Values)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from object response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
if (kv.Key == "@displayMesh")
continue;
const TSharedPtr< FJsonObject > *subObjectPtr;
if (kv.Value->TryGetObject(subObjectPtr))
{
ImportObjectFromCache(*subObjectPtr);
continue;
}
const TArray<TSharedPtr<FJsonValue>> *subArrayPtr;
if (kv.Value->TryGetArray(subArrayPtr))
{
for (auto& arrayElement : *subArrayPtr)
{
const TSharedPtr<FJsonObject> *arraySubObjPtr;
if (!arrayElement->TryGetObject(arraySubObjPtr))
continue;
ImportObjectFromCache(*arraySubObjPtr);
}
}
}
}
UMaterialInterface* ASpeckleUnrealManager::CreateMaterial(TSharedPtr<FJsonObject> obj)
{
if (obj->GetStringField("speckle_type") == "reference")
obj = SpeckleObjects[obj->GetStringField("referencedId")];
int opacity;
if (obj->TryGetNumberField("opacity", opacity)) {
if (opacity < 1) {
return DefaultMeshTransparentMaterial;
}
}
return DefaultMeshOpaqueMaterial;
}
ASpeckleUnrealMesh* ASpeckleUnrealManager::CreateMesh(TSharedPtr<FJsonObject> obj, UMaterialInterface* explicitMaterial)
{
UE_LOG(LogTemp, Warning, TEXT("Creating mesh for object %s"), *obj->GetStringField("id"));
FString Units = obj->GetStringField("units");
// unreal engine units are in cm by default but the conversion is editable by users so
// this needs to be accounted for later.
ScaleFactor = 1;
if (Units == "meters" || Units == "metres" || Units == "m")
ScaleFactor = 100;
if (Units == "centimeters" || Units == "centimetres" || Units == "cm")
ScaleFactor = 1;
if (Units == "millimeters" || Units == "millimetres" || Units == "mm")
ScaleFactor = 0.1;
if (Units == "yards" || Units == "yd")
ScaleFactor = 91.4402757;
if (Units == "feet" || Units == "ft")
ScaleFactor = 30.4799990;
if (Units == "inches" || Units == "in")
ScaleFactor = 2.5399986;
// The following line can be used to debug large objects
// ScaleFactor = ScaleFactor * 0.1;
FString verticesId = obj->GetArrayField("vertices")[0]->AsObject()->GetStringField("referencedId");
FString facesId = obj->GetArrayField("faces")[0]->AsObject()->GetStringField("referencedId");
TArray<TSharedPtr<FJsonValue>> ObjectVertices = SpeckleObjects[verticesId]->GetArrayField("data");
TArray<TSharedPtr<FJsonValue>> ObjectFaces = SpeckleObjects[facesId]->GetArrayField("data");
AActor* ActorInstance = World->SpawnActor(MeshActor);
ASpeckleUnrealMesh* MeshInstance = (ASpeckleUnrealMesh*)ActorInstance;
#if WITH_EDITOR
MeshInstance->SetFolderPath(FName(GetActorLabel() + FString(TEXT("_")) + StreamID));
#endif
TArray<FVector> ParsedVerticies;
for (size_t j = 0; j < ObjectVertices.Num(); j += 3)
{
ParsedVerticies.Add(FVector
(
(float)(ObjectVertices[j].Get()->AsNumber()) * -1,
(float)(ObjectVertices[j + 1].Get()->AsNumber()),
(float)(ObjectVertices[j + 2].Get()->AsNumber())
) * ScaleFactor);
}
//convert mesh faces into triangle array regardless of whether or not they are quads
TArray<int32> ParsedTriangles;
int32 j = 0;
while (j < ObjectFaces.Num())
{
if (ObjectFaces[j].Get()->AsNumber() == 0)
{
//Triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
j += 4;
}
else
{
//Quads to triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 4].Get()->AsNumber());
j += 5;
}
}
// Material priority (low to high): DefaultMeshOpaqueMaterial, renderMaterial set on parent, renderMaterial set on mesh
if (!explicitMaterial)
explicitMaterial = DefaultMeshOpaqueMaterial;
if (obj->HasField("renderMaterial"))
explicitMaterial = CreateMaterial(obj->GetObjectField("renderMaterial"));
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, explicitMaterial, FLinearColor::White);
// UE_LOG(LogTemp, Warning, TEXT("Added %d vertices and %d triangles"), ParsedVerticies.Num(), ParsedTriangles.Num());
return MeshInstance;
}
void ASpeckleUnrealManager::DeleteObjects()
{
for (auto& m : CreatedSpeckleMeshes)
{
if (m.Value->Scene)
m.Value->Destroy();
}
CreatedSpeckleMeshes.Empty();
InProgressSpeckleMeshes.Empty();
}

Просмотреть файл

@ -11,9 +11,10 @@ ASpeckleUnrealMesh::ASpeckleUnrealMesh()
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>("Mesh");
ProceduralMesh->SetupAttachment(RootComponent);
}
void ASpeckleUnrealMesh::SetMesh(TArray<FVector> Vertices, TArray<int32> Triangles, UMaterialInterface* Material, FLinearColor Color)
void ASpeckleUnrealMesh::SetMesh(const TArray<FVector> &Vertices, const TArray<int32> &Triangles, UMaterialInterface* Material, FLinearColor Color)
{
ProceduralMesh->ClearAllMeshSections();
@ -45,7 +46,7 @@ void ASpeckleUnrealMesh::SetMesh(TArray<FVector> Vertices, TArray<int32> Triangl
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(Material, this);
DynMaterial->SetVectorParameterValue("BaseColor", Color);
DynMaterial->SetVectorParameterValue("BaseColor", FLinearColor::White);
ProceduralMesh->SetMaterial(0, DynMaterial);
}

Просмотреть файл

@ -26,12 +26,16 @@ public:
FHttpModule* Http;
/* The actual HTTP call */
UFUNCTION()
void GetStream();
UFUNCTION(CallInEditor, Category = "Speckle")
void ImportSpeckleObject();
UFUNCTION(CallInEditor, Category = "Speckle")
void DeleteObjects();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ServerUrl {
"https://hestia.speckle.works/api/"
"https://speckle.xyz"
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
@ -39,24 +43,33 @@ public:
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ObjectID {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString AuthToken {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
TSubclassOf<ASpeckleUnrealMesh> MeshActor;
TSubclassOf<ASpeckleUnrealMesh> MeshActor {
ASpeckleUnrealMesh::StaticClass()
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
UMaterialInterface* DefaultMeshMaterial;
UMaterialInterface* DefaultMeshOpaqueMaterial;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
bool RandomColorsPerLayer;
UMaterialInterface* DefaultMeshTransparentMaterial;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
bool ImportAtRuntime;
TArray<USpeckleUnrealLayer*> SpeckleUnrealLayers;
/*Assign this function to call when the GET request processes sucessfully*/
void OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
void OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// Sets default values for this actor's properties
ASpeckleUnrealManager();
@ -70,12 +83,16 @@ protected:
float ScaleFactor;
int32 LayerIndex;
int32 CurrentObjectIndex;
TMap<FString, TSharedPtr<FJsonObject>> SpeckleObjects;
void SetUpGetRequest(TSharedRef<IHttpRequest> Request);
TMap<FString, ASpeckleUnrealMesh*> CreatedSpeckleMeshes;
TMap<FString, ASpeckleUnrealMesh*> InProgressSpeckleMeshes;
void GetStreamObjects(int32 objectCount);
ASpeckleUnrealMesh* GetExistingMesh(const FString &objectId);
void ImportObjectFromCache(const TSharedPtr<FJsonObject> speckleObject);
UMaterialInterface* CreateMaterial(TSharedPtr<FJsonObject>);
ASpeckleUnrealMesh* CreateMesh(TSharedPtr<FJsonObject>, UMaterialInterface *explicitMaterial = nullptr);
void OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};

Просмотреть файл

@ -22,7 +22,7 @@ public:
// Sets default values for this actor's properties
ASpeckleUnrealMesh();
virtual void SetMesh(TArray<FVector> Vertices, TArray<int32> Triangles, UMaterialInterface* Material, FLinearColor Color);
virtual void SetMesh(const TArray<FVector> &Vertices, const TArray<int32> &Triangles, UMaterialInterface* Material, FLinearColor Color);
protected:
// Called when the game starts or when spawned

Просмотреть файл

@ -5,9 +5,9 @@
"FriendlyName": "SpeckleUnreal",
"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": "Mobius Node",
"CreatedByURL": "https://www.mobiusnode.io/",
"DocsURL": "https://github.com/mobiusnode/SpeckleUnreal",
"CreatedBy": "Speckle",
"CreatedByURL": "https://speckle.systems/",
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,

Просмотреть файл

@ -7,81 +7,63 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{94A6C6
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Games", "Games", "{8E2F6A87-1826-34F4-940C-CC23A48F9FE4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE4", "Intermediate\ProjectFiles\UE4.vcxproj", "{DE17FD13-E94F-4875-9509-F6023C8B5747}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE4", "Intermediate\ProjectFiles\UE4.vcxproj", "{B30CC638-032E-480C-8503-6B5AB8230CA3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpeckleUnrealProject", "Intermediate\ProjectFiles\SpeckleUnrealProject.vcxproj", "{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpeckleUnrealProject", "Intermediate\ProjectFiles\SpeckleUnrealProject.vcxproj", "{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualizers", "Visualizers", "{1CCEC849-CC72-4C59-8C36-2F7C38706D4C}"
ProjectSection(SolutionItems) = preProject
..\..\..\Epic Games\UE_4.25\Engine\Extras\VisualStudioDebugging\UE4.natvis = ..\..\..\Epic Games\UE_4.25\Engine\Extras\VisualStudioDebugging\UE4.natvis
..\..\..\..\Program Files (x86)\Epic Games\UE_4.26\Engine\Extras\VisualStudioDebugging\UE4.natvis = ..\..\..\..\Program Files (x86)\Epic Games\UE_4.26\Engine\Extras\VisualStudioDebugging\UE4.natvis
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
DebugGame Editor|Android = DebugGame Editor|Android
DebugGame Editor|Win32 = DebugGame Editor|Win32
DebugGame Editor|Win64 = DebugGame Editor|Win64
DebugGame|Android = DebugGame|Android
DebugGame|Win32 = DebugGame|Win32
DebugGame|Win64 = DebugGame|Win64
Development Editor|Android = Development Editor|Android
Development Editor|Win32 = Development Editor|Win32
Development Editor|Win64 = Development Editor|Win64
Development|Android = Development|Android
Development|Win32 = Development|Win32
Development|Win64 = Development|Win64
Shipping|Android = Shipping|Android
Shipping|Win32 = Shipping|Win32
Shipping|Win64 = Shipping|Win64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Android.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win32.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Android.ActiveCfg = Android_DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Android.Build.0 = Android_DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win32.ActiveCfg = DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win32.Build.0 = DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win64.ActiveCfg = DebugGame|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win64.Build.0 = DebugGame|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Android.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win32.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win64.ActiveCfg = Development_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win64.Build.0 = Development_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Android.ActiveCfg = Android_Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Android.Build.0 = Android_Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win32.ActiveCfg = Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win32.Build.0 = Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win64.ActiveCfg = Development|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win64.Build.0 = Development|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Android.ActiveCfg = Android_Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Android.Build.0 = Android_Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win32.ActiveCfg = Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win32.Build.0 = Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win64.ActiveCfg = Shipping|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win64.Build.0 = Shipping|x64
{B30CC638-032E-480C-8503-6B5AB8230CA3}.DebugGame Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.DebugGame Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.DebugGame|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.DebugGame|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Development Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Development Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Development|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Development|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Shipping|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{B30CC638-032E-480C-8503-6B5AB8230CA3}.Shipping|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame Editor|Win32.ActiveCfg = Invalid|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame|Win32.ActiveCfg = DebugGame|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame|Win32.Build.0 = DebugGame|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame|Win64.ActiveCfg = DebugGame|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.DebugGame|Win64.Build.0 = DebugGame|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development Editor|Win32.ActiveCfg = Invalid|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development Editor|Win64.ActiveCfg = Development_Editor|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development Editor|Win64.Build.0 = Development_Editor|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development|Win32.ActiveCfg = Development|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development|Win32.Build.0 = Development|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development|Win64.ActiveCfg = Development|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Development|Win64.Build.0 = Development|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Shipping|Win32.ActiveCfg = Shipping|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Shipping|Win32.Build.0 = Shipping|Win32
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Shipping|Win64.ActiveCfg = Shipping|x64
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B}.Shipping|Win64.Build.0 = Shipping|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{DE17FD13-E94F-4875-9509-F6023C8B5747} = {94A6C6F3-99B3-346E-9557-ABF9D4064DBD}
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9} = {8E2F6A87-1826-34F4-940C-CC23A48F9FE4}
{B30CC638-032E-480C-8503-6B5AB8230CA3} = {94A6C6F3-99B3-346E-9557-ABF9D4064DBD}
{1FCE0C58-EC21-48B6-82CD-DE8E94CC9C5B} = {8E2F6A87-1826-34F4-940C-CC23A48F9FE4}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -1,6 +1,6 @@
{
"FileVersion": 3,
"EngineAssociation": "4.25",
"EngineAssociation": "4.26",
"Category": "",
"Description": "",
"Modules": [