Files
UnrealEngine/Engine/Source/Programs/Enterprise/Datasmith/DatasmithARCHICADExporter/Private/SyncData.cpp
2025-05-18 13:04:45 +08:00

1822 lines
56 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SyncData.h"
#include "ElementID.h"
#include "Synchronizer.h"
#include "Commander.h"
#include "Utils/ElementTools.h"
#include "Element2StaticMesh.h"
#include "MetaData.h"
#include "GeometryUtil.h"
#include "Utils/AutoChangeDatabase.h"
#include "Utils/TimeStat.h"
#include "TexturesCache.h"
#include "Utils/TaskMgr.h"
#include <stdexcept>
BEGIN_NAMESPACE_UE_AC
#define TRACE_ATTACH_OBSERVERS 0
// Control access on this object (for queue operations)
GS::Lock ChildAccessControl;
// Condition variable
GS::Condition ChildAccessControlCV(ChildAccessControl);
// Control access on this object (for queue operations)
GS::Lock ElementAccessControl;
// Condition variable
GS::Condition ElementAccessControlCV(ElementAccessControl);
// Constructor
FSyncData::FSyncData(const GS::Guid& InGuid)
: ElementId(InGuid)
{
}
// Destructor
FSyncData::~FSyncData()
{
if (Parent != nullptr)
{
UE_AC_DebugF("FSyncData::~FSyncData - Deleting child while attached to it's parent {%s}\n",
ElementId.ToUniString().ToUtf8());
Parent->RemoveChild(this);
Parent = nullptr;
}
for (FChildsArray::SizeType i = Childs.Num(); i != 0; --i)
{
Childs[0]->SetParent(nullptr);
UE_AC_Assert(i == Childs.Num());
}
}
// Update data from a 3d element
void FSyncData::Update(const FElementID& InElementID)
{
const API_Elem_Head& Header = InElementID.GetHeader();
UE_AC_Assert(ElementId == APIGuid2GSGuid(Header.guid));
UE_AC_Assert(Index3D == 0 && InElementID.GetIndex3D() != 0);
Index3D = InElementID.GetIndex3D();
if (GenId != InElementID.GetElement3D().GetGenId())
{
GenId = InElementID.GetElement3D().GetGenId();
bIsModified = true;
}
// If AC element has been modified, recheck connections
if (ModificationStamp != Header.modiStamp)
{
if (ModificationStamp > Header.modiStamp)
{
UE_AC_DebugF("FSyncData::Update {%s} New stamp younger: %lld, %lld\n",
APIGuidToString(Header.guid).ToUtf8(), ModificationStamp, Header.modiStamp);
}
ModificationStamp = Header.modiStamp;
InElementID.HandleDepedencies();
bIsModified = true;
}
if (ModificationStamp == 0)
{
UE_AC_DebugF("FSyncData::Update {%s} ModificationStamp == 0\n", APIGuidToString(Header.guid).ToUtf8());
}
SetDefaultParent(InElementID);
}
// Recursively clean. Delete element that hasn't 3d geometry related to it
void FSyncData::CleanAfterScan(FSyncDatabase* IOSyncDatabase)
{
for (FChildsArray::SizeType IdxChild = Childs.Num(); IdxChild != 0;)
{
Childs[--IdxChild]->CleanAfterScan(IOSyncDatabase);
}
if (Childs.Num() == 0 && Index3D == 0)
{
DeleteMe(IOSyncDatabase);
}
}
// Delete this sync data
void FSyncData::DeleteMe(FSyncDatabase* IOSyncDatabase)
{
SetParent(nullptr);
IOSyncDatabase->DeleteSyncData(ElementId);
delete this;
}
void FSyncData::SetParent(FSyncData* InParent)
{
if (Parent != InParent)
{
if (InParent)
{
InParent->AddChild(this);
}
if (Parent)
{
Parent->RemoveChild(this);
}
Parent = InParent;
}
}
// Connect this actor to a default parent if it doesn't already have one
void FSyncData::SetDefaultParent(const FElementID& InElementID)
{
if (!HasParent())
{
const API_Elem_Head& Header = InElementID.GetHeader();
if (Header.hotlinkGuid == APINULLGuid)
{
// Parent is a layer
SetParent(&InElementID.SyncContext.GetSyncDatabase().GetLayerSyncData(Header.layer));
}
else
{
// Parent is a hot link instance
FSyncData*& ParentFound =
InElementID.SyncContext.GetSyncDatabase().GetSyncData(APIGuid2GSGuid(Header.hotlinkGuid));
if (ParentFound == nullptr)
{
ParentFound = new FSyncData::FHotLinkInstance(APIGuid2GSGuid(Header.hotlinkGuid),
&InElementID.SyncContext.GetSyncDatabase());
}
SetParent(ParentFound);
}
}
}
// Add a child to this sync data
void FSyncData::AddChild(FSyncData* InChild)
{
UE_AC_TestPtr(InChild);
for (auto& i : Childs)
{
if (i == InChild)
{
UE_AC_VerboseF("FSyncData::AddChild - Child already present\n");
return;
}
}
Childs.Add(InChild);
}
// Remove a child from this sync data
void FSyncData::RemoveChild(FSyncData* InChild)
{
if (Childs.RemoveSingle(InChild) == 0)
{
UE_AC_VerboseF("FSyncData::RemoveChild - Child not present\n");
}
}
// Return true if this element and all it's childs have been cut out
bool FSyncData::CheckAllCutOut()
{
return true;
}
// Guid given to the scene element.
const GS::Guid FSyncData::FScene::SceneGUID("CBDEFBEF-0D4E-4162-8C4C-64AC34CEB4E6");
FSyncData::FScene::FScene()
: FSyncData(SceneGUID)
{
}
// Delete this sync data
void FSyncData::FScene::DeleteMe(FSyncDatabase* IOSyncDatabase)
{
if (SceneInfoMetaData.IsValid())
{
IOSyncDatabase->GetScene()->RemoveMetaData(SceneInfoMetaData);
SceneInfoMetaData.Reset();
}
if (SceneInfoActorElement.IsValid())
{
IOSyncDatabase->GetScene()->RemoveActor(SceneInfoActorElement, EDatasmithActorRemovalRule::RemoveChildren);
SceneInfoActorElement.Reset();
}
FSyncData::DeleteMe(IOSyncDatabase);
}
void FSyncData::FScene::AddChildActor(const TSharedPtr< IDatasmithActorElement >& InActor)
{
GS::Guard< GS::Lock > lck(ChildAccessControl);
if (!SceneElement.IsValid())
{
ThrowAssertionFail(__FILE__, __LINE__);
}
SceneElement->AddActor(InActor);
}
// Set (or replace) datasmith actor element related to this sync data
void FSyncData::FScene::SetActorElement(const TSharedPtr< IDatasmithActorElement >& /* InActor */)
{
UE_AC_Assert(false); // Scene is not an actor
}
// Set the element to the scene element
void FSyncData::FScene::Process(FProcessInfo* IOProcessInfo)
{
if (SceneElement.IsValid())
{
UE_AC_Assert(SceneElement == IOProcessInfo->SyncContext.GetSyncDatabase().GetScene());
}
SceneElement = IOProcessInfo->SyncContext.GetSyncDatabase().GetScene();
UpdateInfo(IOProcessInfo);
}
void FSyncData::FScene::UpdateInfo(FProcessInfo* IOProcessInfo)
{
if (!SceneInfoActorElement.IsValid())
{
SceneInfoActorElement = FDatasmithSceneFactory::CreateActor(GSStringToUE(SceneGUID.ToUniString()));
IOProcessInfo->SyncContext.GetScene().AddActor(SceneInfoActorElement);
}
FMetaData InfoMetaData(SceneInfoActorElement);
GSErrCode err = NoError;
GS::UniString projectName = GS::UniString("Untitled");
// Project info
{
API_ProjectInfo projectInfo;
err = ACAPI_Environment(APIEnv_ProjectID, &projectInfo);
if (err == NoError)
{
if (!projectInfo.untitled || projectInfo.projectName == nullptr)
projectName = *projectInfo.projectName;
InfoMetaData.AddStringProperty(TEXT("ProjectName"), projectName);
if (projectInfo.projectPath != nullptr)
{
InfoMetaData.AddStringProperty(TEXT("ProjectPath"), *projectInfo.projectPath);
}
if (projectInfo.location != nullptr)
{
InfoMetaData.AddStringProperty(TEXT("ProjectLocation"), projectInfo.location->ToDisplayText());
}
if (projectInfo.location_team != nullptr)
{
InfoMetaData.AddStringProperty(TEXT("SharedProjectLocation"),
projectInfo.location_team->ToDisplayText());
}
}
}
// Project note info
{
API_ProjectNoteInfo projectNoteInfo;
BNZeroMemory(&projectNoteInfo, sizeof(API_ProjectNoteInfo));
err = ACAPI_Environment(APIEnv_GetProjectNotesID, &projectNoteInfo);
if (err == NoError)
{
InfoMetaData.AddStringProperty(TEXT("Client"), projectNoteInfo.client);
InfoMetaData.AddStringProperty(TEXT("Company"), projectNoteInfo.company);
InfoMetaData.AddStringProperty(TEXT("Country"), projectNoteInfo.country);
InfoMetaData.AddStringProperty(TEXT("PostalCode"), projectNoteInfo.code);
InfoMetaData.AddStringProperty(TEXT("City"), projectNoteInfo.city);
InfoMetaData.AddStringProperty(TEXT("Street"), projectNoteInfo.street);
InfoMetaData.AddStringProperty(TEXT("MainArchitect"), projectNoteInfo.architect);
InfoMetaData.AddStringProperty(TEXT("Draftsperson"), projectNoteInfo.draftsmen);
InfoMetaData.AddStringProperty(TEXT("ProjectStatus"), projectNoteInfo.projectStatus);
InfoMetaData.AddStringProperty(TEXT("DateOfIssue"), projectNoteInfo.dateOfIssue);
InfoMetaData.AddStringProperty(TEXT("Keywords"), projectNoteInfo.keywords);
InfoMetaData.AddStringProperty(TEXT("Notes"), projectNoteInfo.notes);
}
}
// Place info
{
API_PlaceInfo placeInfo;
err = ACAPI_Environment(APIEnv_GetPlaceSetsID, &placeInfo);
if (err == NoError)
{
InfoMetaData.AddStringProperty(TEXT("Longitude"), GS::ValueToUniString(placeInfo.longitude));
InfoMetaData.AddStringProperty(TEXT("Latitude"), GS::ValueToUniString(placeInfo.latitude));
InfoMetaData.AddStringProperty(TEXT("Altitude"), GS::ValueToUniString(placeInfo.altitude));
InfoMetaData.AddStringProperty(TEXT("North"), GS::ValueToUniString(placeInfo.north));
InfoMetaData.AddStringProperty(TEXT("SunAngleXY"), GS::ValueToUniString(placeInfo.sunAngXY));
InfoMetaData.AddStringProperty(TEXT("SunAngleZ"), GS::ValueToUniString(placeInfo.sunAngZ));
InfoMetaData.AddStringProperty(TEXT("TimeZoneInMinutes"),
GS::ValueToUniString(placeInfo.timeZoneInMinutes));
InfoMetaData.AddStringProperty(TEXT("TimeZoneOffset"), GS::ValueToUniString(placeInfo.timeZoneOffset));
GSTime gstime;
GSTimeRecord timeRecord(placeInfo.year, placeInfo.month, 0, placeInfo.day, placeInfo.hour, placeInfo.minute,
placeInfo.second, 0);
TIGetGSTime(&timeRecord, &gstime, TI_LOCAL_TIME);
InfoMetaData.AddStringProperty(TEXT("LocalDateTime"),
TIGetTimeString(gstime, TI_LONG_DATE_FORMAT | TI_SHORT_TIME_FORMAT));
}
}
FAutoChangeDatabase AutoRestoreDB(APIWind_3DModelID);
API_Coord DbOffset = {0.0, 0.0};
GSErrCode GSErr = ACAPI_Database(APIDb_GetOffsetID, &DbOffset);
if (GSErr == NoError)
{
InfoMetaData.AddStringProperty(TEXT("VirtualToWorldOffset.X"), GS::ValueToUniString(DbOffset.x));
InfoMetaData.AddStringProperty(TEXT("VirtualToWorldOffset.Y"), GS::ValueToUniString(DbOffset.y));
}
else
{
UE_AC_DebugF("FSyncData::FScene::UpdateInfo - APIDb_GetOffsetID return error %s", GetErrorName(GSErr));
}
API_Coord3D LocOrigo = {0.0, 0.0, 0.0};
GSErr = ACAPI_Database(APIDb_GetLocOrigoID, &LocOrigo);
if (GSErr == NoError)
{
InfoMetaData.AddStringProperty(TEXT("UserOrigin.X"), GS::ValueToUniString(LocOrigo.x));
InfoMetaData.AddStringProperty(TEXT("UserOrigin.Y"), GS::ValueToUniString(LocOrigo.y));
InfoMetaData.AddStringProperty(TEXT("UserOrigin.Z"), GS::ValueToUniString(LocOrigo.z));
}
else
{
UE_AC_DebugF("FSyncData::FScene::UpdateInfo - APIDb_GetLocOrigoID return error %s", GetErrorName(GSErr));
}
SceneInfoActorElement->SetLabel(GSStringToUE(GS::UniString(projectName + " Project Informations")));
InfoMetaData.SetOrUpdate(&SceneInfoMetaData, &IOProcessInfo->SyncContext.GetScene());
}
// Return Element as an actor
const TSharedPtr< IDatasmithActorElement >& FSyncData::FScene::GetActorElement() const
{
static TSharedPtr< IDatasmithActorElement > NotAnActor;
return NotAnActor;
}
void FSyncData::FScene::RemoveChildActor(const TSharedPtr< IDatasmithActorElement >& InActor)
{
UE_AC_Assert(SceneElement.IsValid());
SceneElement->RemoveActor(InActor, EDatasmithActorRemovalRule::RemoveChildren);
}
FSyncData::FActor::FActor(const GS::Guid& InGuid)
: FSyncData(InGuid)
{
}
// Delete this sync data
void FSyncData::FActor::DeleteMe(FSyncDatabase* IOSyncDatabase)
{
IOSyncDatabase->GetScene()->RemoveMetaData(MetaData);
SetActorElement(TSharedPtr< IDatasmithActorElement >());
FSyncData::DeleteMe(IOSyncDatabase);
}
void FSyncData::FActor::AddChildActor(const TSharedPtr< IDatasmithActorElement >& InActor)
{
GS::Guard< GS::Lock > lck(ChildAccessControl);
if (!ActorElement.IsValid())
{
ThrowAssertionFail(__FILE__, __LINE__);
}
ActorElement->AddChild(InActor);
}
void FSyncData::FActor::RemoveChildActor(const TSharedPtr< IDatasmithActorElement >& InActor)
{
GS::Guard< GS::Lock > lck(ChildAccessControl);
UE_AC_Assert(ActorElement.IsValid());
ActorElement->RemoveChild(InActor);
}
// Set (or replace) datasmith actor element related to this sync data
void FSyncData::FActor::SetActorElement(const TSharedPtr< IDatasmithActorElement >& InElement)
{
GS::Guard< GS::Lock > lck(ElementAccessControl);
if (ActorElement != InElement)
{
UE_AC_TestPtr(Parent);
if (ActorElement.IsValid())
{
Parent->RemoveChildActor(ActorElement);
ActorElement.Reset();
}
if (InElement.IsValid())
{
Parent->AddChildActor(InElement);
ActorElement = InElement;
}
}
}
// Add tags data
bool FSyncData::FActor::UpdateTags(const FTagsArray& InTags)
{
int32 Count = (int32)InTags.GetSize();
int32 Index = 0;
if (ActorElement->GetTagsCount() == Count)
{
while (Index < Count && FCString::Strcmp(GSStringToUE(InTags[Index]), ActorElement->GetTag(Index)) == 0)
{
++Index;
}
if (Index == Count)
{
return false; // All Tags unchanged
}
}
ActorElement->ResetTags();
for (Index = 0; Index < Count; ++Index)
{
ActorElement->AddTag(GSStringToUE(InTags[Index]));
}
return true; // Tags changed
}
// Replace the current meta data by this new one
void FSyncData::FActor::ReplaceMetaData(IDatasmithScene& IOScene,
const TSharedPtr< IDatasmithMetaDataElement >& InNewMetaData)
{
// Disconnect previous meta data
if (MetaData.IsValid())
{
IOScene.RemoveMetaData(MetaData);
MetaData.Reset();
}
MetaData = InNewMetaData;
// Connect previous meta data
if (MetaData.IsValid())
{
MetaData->SetAssociatedElement(ActorElement);
}
IOScene.AddMetaData(MetaData);
}
// Guid used to synthetize layer guid
const GS::Guid FSyncData::FLayer::LayerGUID("97D32F90-A33E-0000-8305-D1A7D3FCED66");
// Return the synthetized layer guid.
GS::Guid FSyncData::FLayer::GetLayerGUID(short Layer)
{
GS::Guid TmpGUID = LayerGUID;
reinterpret_cast< short* >(&TmpGUID)[3] = Layer;
return TmpGUID;
}
// Return true if this guid is for a layer
short FSyncData::FLayer::IsLayerGUID(GS::Guid LayerID)
{
reinterpret_cast< short* >(&LayerID)[3] = 0;
return LayerID == LayerGUID;
}
// Return the layer index
short FSyncData::FLayer::GetLayerIndex(const GS::Guid& InLayerID)
{
return reinterpret_cast< const short* >(&InLayerID)[3];
}
FSyncData::FLayer::FLayer(const GS::Guid& InGuid)
: FSyncData::FActor(InGuid)
{
}
void FSyncData::FLayer::Process(FProcessInfo* /* IOProcessInfo */)
{
if (!ActorElement.IsValid())
{
short LayerIndex = GetLayerIndex(ElementId);
// Get the layer's name
GS::UniString LayerName;
API_Attribute attribute;
Zap(&attribute);
attribute.header.typeID = API_LayerID;
#if AC_VERSION > 26
attribute.header.index = ACAPI_CreateAttributeIndex(LayerIndex);
#else
attribute.header.index = short(LayerIndex);
#endif
attribute.header.uniStringNamePtr = &LayerName;
GSErrCode error = ACAPI_Attribute_Get(&attribute);
if (error != NoError)
{
// This case happened for the special ArchiCAD layer
UE_AC_DebugF("CElementsHierarchy::CreateLayerNode - Error %s for layer index=%d\n", GetErrorName(error),
LayerIndex);
if (error == APIERR_DELETED)
{
static const GS::UniString LayerDeleted("Layer deleted");
LayerName = LayerDeleted;
}
else
{
utf8_string LayerError(Utf8StringFormat("Layer error=%s", GetErrorName(error)));
LayerName = GS::UniString(LayerError.c_str(), CC_UTF8);
}
}
else if (LayerName == "\x14") // Special ARCHICAD layer
LayerName = "ARCHICAD";
UE_AC_Assert(LayerName.GetLength() > 0);
GS::Guid LayerGuid = APIGuid2GSGuid(attribute.layer.head.guid);
TSharedRef< IDatasmithActorElement > NewActor =
FDatasmithSceneFactory::CreateActor(GSStringToUE(LayerGuid.ToUniString()));
NewActor->SetLabel(GSStringToUE(LayerName));
SetActorElement(NewActor);
}
}
inline Geometry::Transformation3D Convert(const ModelerAPI::Transformation& InMatrix)
{
Geometry::Matrix33 M33;
M33.Set(0, 0, InMatrix.matrix[0][0]);
M33.Set(0, 1, InMatrix.matrix[0][1]);
M33.Set(0, 2, InMatrix.matrix[0][2]);
M33.Set(1, 0, InMatrix.matrix[1][0]);
M33.Set(1, 1, InMatrix.matrix[1][1]);
M33.Set(1, 2, InMatrix.matrix[1][2]);
M33.Set(2, 0, InMatrix.matrix[2][0]);
M33.Set(2, 1, InMatrix.matrix[2][1]);
M33.Set(2, 2, InMatrix.matrix[2][2]);
Geometry::Transformation3D Converted;
Converted.SetMatrix(M33);
Converted.SetOffset(Vector3D(InMatrix.matrix[0][3], InMatrix.matrix[1][3], InMatrix.matrix[2][3]));
return Converted;
}
class FConvertGeometry2MeshElement : public FTaskMgr::FTask
{
public:
FConvertGeometry2MeshElement(const FSyncContext& InSyncContext, FSyncData::FElement* InElementSyncData,
FMeshClass* InMeshClass);
void AddElementGeometry(FElementID* IOElementID, const Geometry::Vector3D& InGeometryShift);
bool HasGeometry() const { return Element2StaticMesh.HasGeometry(); }
void Run()
{
/*
#if PLATFORM_WINDOWS
SetThreadName(GS::Thread::GetCurrent().GetName().ToUtf8());
#else
pthread_setname_np(GS::Thread::GetCurrent().GetName().ToUtf8());
#endif
*/
try
{
TSharedPtr< IDatasmithMeshElement > Mesh;
if (HasGeometry())
{
Mesh = Element2StaticMesh.CreateMesh();
}
MeshClass->SetMeshElement(Mesh);
MeshClass->SetWaitingInstanceMesh(&SyncContext.GetSyncDatabase());
}
catch (std::exception& e)
{
UE_AC_DebugF("FConvertGeometry2MeshElement::Run - Catch std exception %s\n", e.what());
}
catch (GS::GSException& gs)
{
UE_AC_DebugF("FConvertGeometry2MeshElement::Run - Catch gs exception %s\n", gs.GetMessage().ToUtf8());
}
catch (...)
{
UE_AC_DebugF("FConvertGeometry2MeshElement::Run - Catch unknown exception\n");
}
}
private:
const FSyncContext& SyncContext;
FElement2StaticMesh Element2StaticMesh;
FSyncData::FElement& ElementSyncData;
FMeshClass* MeshClass = nullptr;
};
FConvertGeometry2MeshElement::FConvertGeometry2MeshElement(const FSyncContext& InSyncContext,
FSyncData::FElement* InElementSyncData,
FMeshClass* InMeshClass)
: SyncContext(InSyncContext)
, Element2StaticMesh(InSyncContext)
, ElementSyncData(*InElementSyncData)
, MeshClass(InMeshClass)
{
UE_AC_TestPtr(InElementSyncData);
}
void FConvertGeometry2MeshElement::AddElementGeometry(FElementID* IOElementID,
const Geometry::Vector3D& InGeometryShift
)
{
UE_AC_TestPtr(IOElementID);
Element2StaticMesh.AddElementGeometry(IOElementID->GetElement3D(), InGeometryShift);
}
FSyncData::FElement::FElement(const GS::Guid& InGuid, const FSyncContext& /* InSyncContext */)
: FSyncData::FActor(InGuid)
{
}
static void CopyActor(IDatasmithActorElement* OutDestActor, const IDatasmithActorElement& InSourceActor)
{
UE_AC_TestPtr(OutDestActor);
if (&InSourceActor == OutDestActor)
{
return;
}
OutDestActor->SetLabel(InSourceActor.GetLabel());
OutDestActor->SetLayer(InSourceActor.GetLayer());
OutDestActor->SetIsAComponent(InSourceActor.IsAComponent());
OutDestActor->SetTranslation(InSourceActor.GetTranslation(), false);
OutDestActor->SetRotation(InSourceActor.GetRotation(), false);
// Copy childs from old actor to new one
int32 ChildrenCount = InSourceActor.GetChildrenCount();
for (int32 ChildIndex = 0; ChildIndex < ChildrenCount; ++ChildIndex)
{
OutDestActor->AddChild(InSourceActor.GetChild(ChildIndex));
}
int32 Count = InSourceActor.GetTagsCount();
OutDestActor->ResetTags();
for (int32 Index = 0; Index < Count; ++Index)
{
OutDestActor->AddTag(InSourceActor.GetTag(Index));
}
}
void FSyncData::FElement::MeshElementChanged()
{
UE_AC_Assert(ActorElement.IsValid());
if (MeshElement.IsValid())
{
// UE_AC_STAT(IOProcessInfo->SyncContext.Stats.TotalInstancesCreated++);
if (!ActorElement->IsA(EDatasmithElementType::StaticMeshActor))
{
TSharedPtr< IDatasmithActorElement > OldActor = ActorElement;
TSharedPtr< IDatasmithMeshActorElement > NewMeshActor =
FDatasmithSceneFactory::CreateMeshActor(OldActor->GetName());
SetActorElement(NewMeshActor);
CopyActor(NewMeshActor.Get(), *OldActor.Get());
}
IDatasmithMeshActorElement& MeshActor = *StaticCastSharedPtr< IDatasmithMeshActorElement >(ActorElement).Get();
MeshActor.SetStaticMeshPathName(MeshElement->GetName());
// MeshElement->SetLabel(ActorElement->GetLabel());
}
else
{
// UE_AC_STAT(IOProcessInfo->SyncContext.Stats.TotalEmptyInstancesCreated++);
if (ActorElement->IsA(EDatasmithElementType::StaticMeshActor))
{
TSharedPtr< IDatasmithActorElement > OldMeshActor = ActorElement;
TSharedPtr< IDatasmithActorElement > NewActor =
FDatasmithSceneFactory::CreateActor(OldMeshActor->GetName());
SetActorElement(NewActor);
CopyActor(NewActor.Get(), *OldMeshActor.Get());
}
}
}
// Return true if this element and all it's childs have been cut out
bool FSyncData::FElement::CheckAllCutOut()
{
if (Index3D != 0)
{
return false;
}
for (FChildsArray::SizeType IterChild = 0; IterChild < Childs.Num(); ++IterChild)
{
if (!Childs[IterChild]->CheckAllCutOut())
{
return false;
}
}
return true;
}
void FSyncData::FElement::Process(FProcessInfo* IOProcessInfo)
{
if (Index3D == 0) // No 3D imply an hierarchical parent or recently cut out element
{
if (ActorElement.IsValid())
{
if (ActorElement->IsA(EDatasmithElementType::StaticMeshActor)) // Previously was a mesh, now presume cut out
{
// Element is a child cut out and it's parent hasn't been completely cut-out.
SetActorElement(TSharedPtr< IDatasmithActorElement >());
if (!CheckAllCutOut())
{
UE_AC_DebugF("FSyncData::FElement::Process - Element cut out with uncut child %s\n",
ElementId.ToUniString().ToUtf8());
}
}
}
else // Hierarchical parent
{
IOProcessInfo->ElementID.InitElement(this);
IOProcessInfo->ElementID.InitHeader(GSGuid2APIGuid(ElementId));
CheckModificationStamp(IOProcessInfo->ElementID.GetHeader().modiStamp);
TypeID = IOProcessInfo->ElementID.GetTypeID();
UE_AC_STAT(IOProcessInfo->SyncContext.Stats.TotalOwnerCreated++);
TSharedRef< IDatasmithActorElement > NewActor =
FDatasmithSceneFactory::CreateActor(GSStringToUE(ElementId.ToUniString()));
GS::UniString ElemenInfo;
if (FElementTools::GetInfoString(IOProcessInfo->ElementID.GetHeader().guid, &ElemenInfo))
{
NewActor->SetLabel(GSStringToUE(ElemenInfo));
}
else
{
NewActor->SetLabel(TEXT("Unnamed"));
}
NewActor->SetIsAComponent(bIsAComponent);
SetActorElement(NewActor);
bMetadataProcessed = false;
if (IOProcessInfo->bProcessMetaData)
{
ProcessMetaData(&IOProcessInfo->SyncContext.GetSyncDatabase());
}
}
}
else
{
if (IsModified())
{
// Advance progression bar to the current value
IOProcessInfo->SyncContext.NewCurrentValue(++IOProcessInfo->ProgessValue);
IOProcessInfo->ElementID.InitElement(this);
IOProcessInfo->ElementID.InitHeader();
TypeID = IOProcessInfo->ElementID.GetTypeID();
ModelerAPI::Transformation LocalToWorld =
IOProcessInfo->ElementID.GetElement3D().GetElemLocalToWorldTransformation();
// Shift geometry to pivot it at the bounds center
Geometry::Vector3D GeometryShift;
{
Box3D LocalBounds = IOProcessInfo->ElementID.GetElement3D().GetBounds(
ModelerAPI::CoordinateSystem::ElemLocal);
Geometry::Point3D LocalBoundsCenter{
(LocalBounds.xMin + LocalBounds.xMax) * 0.5,
(LocalBounds.yMin + LocalBounds.yMax) * 0.5,
LocalBounds.zMin};
// Transform center to world
TRANMAT LocalToWorldTranmatOrig;
LocalToWorld.ToTRANMAT(&LocalToWorldTranmatOrig);
Geometry::Point3D BoundsCenterWorld = Geometry::TransformPoint(LocalToWorldTranmatOrig, LocalBoundsCenter);
// "Re-pivot" object to the center of bounding box by
// ...shifting geometry to have its local zero coordinates at the geometry bounds center
GeometryShift = -LocalBoundsCenter;
// ...and changing transform to translate geometry zero point to bounds center in world space we computed
LocalToWorld.matrix[0][3] = BoundsCenterWorld[0];
LocalToWorld.matrix[1][3] = BoundsCenterWorld[1];
LocalToWorld.matrix[2][3] = BoundsCenterWorld[2];
LocalToWorld.status = BoundsCenterWorld.IsNullVector(EPS) ? TR_IDENT : TR_TRANSL_ONLY;
}
if (!ActorElement.IsValid())
{
SetActorElement(FDatasmithSceneFactory::CreateMeshActor(GSStringToUE(ElementId.ToUniString())));
}
ActorElement->SetIsAComponent(bIsAComponent);
// Set actor label
GS::UniString ElemenInfo;
if (FElementTools::GetInfoString(IOProcessInfo->ElementID.GetHeader().guid, &ElemenInfo))
{
ActorElement->SetLabel(GSStringToUE(ElemenInfo));
}
else
{
ActorElement->SetLabel(TEXT("Unnamed"));
}
ActorElement->SetTranslation(FGeometryUtil::GetTranslationVector(LocalToWorld.matrix));
ActorElement->SetRotation(FGeometryUtil::GetRotationQuat(LocalToWorld.matrix));
// Set actor layer
#if AC_VERSION > 26
const short Index = short(IOProcessInfo->ElementID.GetHeader().layer.ToInt32_Deprecated());
ActorElement->SetLayer(*IOProcessInfo->SyncContext.GetSyncDatabase().GetLayerName(Index));
#else
ActorElement->SetLayer(*IOProcessInfo->SyncContext.GetSyncDatabase().GetLayerName(IOProcessInfo->ElementID.GetHeader().layer));
#endif
bMetadataProcessed = false;
if (IOProcessInfo->bProcessMetaData)
{
ProcessMetaData(&IOProcessInfo->SyncContext.GetSyncDatabase());
}
FMeshClass* MeshClass = IOProcessInfo->ElementID.GetMeshClass();
UE_AC_Assert(MeshClass != nullptr);
constexpr short IsRelative = short(TR_DET_1 | TR_TRANSL_ONLY);
if (MeshClass->AddInstance(this, &IOProcessInfo->SyncContext.GetSyncDatabase()) == FMeshClass::kBuild)
{
MeshClass->Translation = ActorElement->GetTranslation();
MeshClass->Rotation = ActorElement->GetRotation();
FConvertGeometry2MeshElement* ConvertGeometry2MeshElement =
new FConvertGeometry2MeshElement(IOProcessInfo->SyncContext, this, MeshClass);
ConvertGeometry2MeshElement->AddElementGeometry(&IOProcessInfo->ElementID, GeometryShift);
if (ConvertGeometry2MeshElement->HasGeometry())
{
UE_AC_STAT(IOProcessInfo->SyncContext.Stats.TotalMeshClassesCreated++);
}
else
{
UE_AC_STAT(IOProcessInfo->SyncContext.Stats.TotalEmptyMeshClassesCreated++);
}
// ConvertGeometry2MeshElement->Run();
// delete ConvertGeometry2MeshElement;
FTaskMgr::GetMgr()->AddTask(ConvertGeometry2MeshElement, FTaskMgr::kSchedule);
}
}
}
}
// Add tags data
bool FSyncData::FElement::AddTags(FSyncDatabase* IOSyncDatabase)
{
UE_AC_Assert(ActorElement.IsValid());
FTagsArray Tags(10);
static const GS::UniString PrefixTagUniqueID("Archicad.Element.UniqueID.");
Tags.Push(PrefixTagUniqueID + ElementId.ToUniString());
static const GS::UniString PrefixTagType("Archicad.Element.Type.");
GS::UniString ElementTypeName(FElementTools::TypeName(TypeID), CC_UTF8);
Tags.Push(PrefixTagType + ElementTypeName);
if (TypeID == API_ObjectID || TypeID == API_LampID || TypeID == API_WindowID || TypeID == API_DoorID)
{
API_Element APIElement;
Zap(&APIElement);
APIElement.header.guid = GSGuid2APIGuid(ElementId);
GSErrCode GSErr = ACAPI_Element_Get(&APIElement, 0);
UE_AC_Assert(GET_HEADER_TYPEID(APIElement.header) == TypeID);
if (GSErr == NoError)
{
GS::Int32 LibPartIndex = 0;
switch (TypeID)
{
case API_ObjectID:
LibPartIndex = APIElement.object.libInd;
break;
case API_LampID:
LibPartIndex = APIElement.lamp.libInd;
break;
case API_WindowID:
LibPartIndex = APIElement.window.openingBase.libInd;
break;
case API_DoorID:
LibPartIndex = APIElement.door.openingBase.libInd;
break;
default:
UE_AC_Assert(false);
break;
}
FLibPartInfo* LibPartInfo = IOSyncDatabase->GetLibPartInfo(LibPartIndex);
if (LibPartInfo != nullptr)
{
static const GS::UniString PrefixLibPartMain("Archicad.Element.LibPart.Main.");
Tags.Push(PrefixLibPartMain + LibPartInfo->Guid.Main.ToUniString());
static const GS::UniString PrefixLibPartRev("Archicad.Element.LibPart.Rev.");
Tags.Push(PrefixLibPartRev + LibPartInfo->Guid.Rev.ToUniString());
static const GS::UniString PrefixLibPartName("Archicad.Element.LibPart.Name.");
Tags.Push(PrefixLibPartName + LibPartInfo->Name);
}
if (TypeID == API_ObjectID || TypeID == API_LampID)
{
UE_AC_Assert(offsetof(API_Element, object.reflected) == offsetof(API_Element, lamp.reflected));
if (APIElement.object.useObjMaterials)
{
static const GS::UniString TagUseObjectMaterial("Archicad.Element.UseObjectMaterial");
Tags.Push(TagUseObjectMaterial);
}
if (APIElement.object.reflected)
{
static const GS::UniString TagObjectReflected("Archicad.Element.Reflected");
Tags.Push(TagObjectReflected);
}
}
else if (TypeID == API_WindowID || TypeID == API_DoorID)
{
UE_AC_Assert(offsetof(API_Element, window.openingBase) == offsetof(API_Element, door.openingBase));
if (APIElement.window.openingBase.reflected)
{
static const GS::UniString TagObjectReflected("Archicad.Element.Mirror.Y");
Tags.Push(TagObjectReflected);
}
if (APIElement.window.openingBase.oSide)
{
static const GS::UniString TagObjectReflected("Archicad.Element.Mirror.X");
Tags.Push(TagObjectReflected);
}
if (APIElement.window.openingBase.refSide)
{
static const GS::UniString TagObjectReflected("Archicad.Element.Mirror.X.Same");
Tags.Push(TagObjectReflected);
}
}
}
}
return UpdateTags(Tags);
}
// Return true if this element need to update tags and metadata
bool FSyncData::FElement::NeedTagsAndMetaDataUpdate()
{
return !bMetadataProcessed;
}
// Called from process meta data idle task
bool FSyncData::FElement::ProcessMetaData(FSyncDatabase* IOSyncDatabase)
{
if (bMetadataProcessed)
{
return false;
}
bMetadataProcessed = true;
return AddTags(IOSyncDatabase) | UpdateMetaData(&IOSyncDatabase->GetScene().Get());
}
void FSyncData::FElement::SetMesh(FSyncDatabase* IOSyncDatabase, const TSharedPtr< IDatasmithMeshElement >& InMesh)
{
IOSyncDatabase->SetMesh(&MeshElement, InMesh);
MeshElementChanged();
}
// Attach observer for Auto Sync
bool FSyncData::FElement::AttachObserver(FAttachObservers* IOAttachObservers)
{
bool bChanged = false;
// We attach observer only when we will need it
if (bIsObserved == false)
{
#if ATTACH_ONSERVER_STAT
FTimeStat SlotStart;
#endif
bIsObserved = true;
GSErrCode GSErr = ACAPI_Element_AttachObserver(GSGuid2APIGuid(ElementId), APINotifyElement_EndEvents);
if (GSErr != NoError && GSErr != APIERR_LINKEXIST)
{
UE_AC_DebugF("FSyncData::FElement::AttachObserver - ACAPI_Element_AttachObserver error=%s\n",
GetErrorName(GSErr));
}
#if ATTACH_ONSERVER_STAT
double AfterAttachObserver = FTimeStat::CpuTimeClock();
#endif
API_Elem_Head ElementHead;
Zap(&ElementHead);
ElementHead.guid = GSGuid2APIGuid(ElementId);
GSErr = ACAPI_Element_GetHeader(&ElementHead);
if (GSErr == NoError)
{
bChanged = ElementHead.modiStamp != ModificationStamp;
if (bChanged)
{
UE_AC_TraceF("FSyncData::FElement::AttachObserver - Object {%s} - ModificationStamp %lld -> %lld\n",
ElementId.ToUniString().ToUtf8(), ModificationStamp, ElementHead.modiStamp);
}
}
else
{
UE_AC_DebugF("FSyncData::FElement::AttachObserver - ACAPI_Element_GetHeader error=%s\n",
GetErrorName(GSErr));
}
#if ATTACH_ONSERVER_STAT
IOAttachObservers->CumulateStats(SlotStart, AfterAttachObserver);
#endif
}
return bChanged;
}
// Delete this sync data
void FSyncData::FElement::DeleteMe(FSyncDatabase* IOSyncDatabase)
{
IOSyncDatabase->SetMesh(&MeshElement, TSharedPtr< IDatasmithMeshElement >());
FSyncData::FActor::DeleteMe(IOSyncDatabase);
}
// Rebuild the meta data of this element
bool FSyncData::FElement::UpdateMetaData(IDatasmithScene* IOScene)
{
FMetaData MetaDataExporter(ActorElement);
MetaDataExporter.ExportMetaData(ElementId);
// Add some object and lamp meta data
if (TypeID == API_ObjectID || TypeID == API_LampID)
{
FAutoMemo AutoMemo(GSGuid2APIGuid(ElementId), APIMemoMask_AddPars);
if (AutoMemo.GSErr == NoError)
{
if (AutoMemo.Memo.params) // Can be null
{
GS::UniString EscapeAssetId;
if (GetParameter(AutoMemo.Memo.params, "enscapeAssetId", &EscapeAssetId))
{
MetaDataExporter.AddProperty(TEXT("EnscapeAssetId"), EDatasmithKeyValuePropertyType::String,
EscapeAssetId);
}
}
}
}
return MetaDataExporter.SetOrUpdate(&MetaData, IOScene);
}
void FSyncData::FCameraSet::Process(FProcessInfo* /* IOProcessInfo */)
{
if (!ActorElement.IsValid())
{
TSharedRef< IDatasmithActorElement > NewActor =
FDatasmithSceneFactory::CreateActor(GSStringToUE(ElementId.ToUniString()));
NewActor->SetLabel(GSStringToUE(Name));
SetActorElement(NewActor);
if (bOpenedPath)
{
NewActor->AddTag(TEXT("Path.opened"));
}
else
{
NewActor->AddTag(TEXT("Path.closed"));
}
}
}
// Guid given to the current view.
const GS::Guid FSyncData::FCamera::CurrentViewGUID("B2BD9C50-60EB-4E64-902B-D1574FADEC45");
void FSyncData::FCamera::Process(FProcessInfo* /* IOProcessInfo */)
{
if (!ActorElement.IsValid())
{
SetActorElement(FDatasmithSceneFactory::CreateCameraActor(GSStringToUE(ElementId.ToUniString())));
MarkAsModified();
}
if (ElementId == CurrentViewGUID)
{
InitWithCurrentView();
}
else
{
if (IsModified())
{
InitWithCameraElement();
}
}
}
void FSyncData::FCamera::InitWithCurrentView()
{
FAutoChangeDatabase changeDB(APIWind_3DModelID);
IDatasmithCameraActorElement& CameraElement = static_cast< IDatasmithCameraActorElement& >(*ActorElement.Get());
CameraElement.SetLabel(TEXT("Current view"));
// Set the camera data from AC 3D projection info
API_3DProjectionInfo projSets;
GSErrCode GSErr = ACAPI_Environment(APIEnv_Get3DProjectionSetsID, &projSets, NULL);
if (GSErr == GS::NoError)
{
if (projSets.isPersp)
{
CameraElement.SetTranslation(FGeometryUtil::GetTranslationVector(
{projSets.u.persp.pos.x, projSets.u.persp.pos.y, projSets.u.persp.cameraZ}));
CameraElement.SetRotation(FGeometryUtil::GetRotationQuat(
FGeometryUtil::GetPitchAngle(projSets.u.persp.cameraZ, projSets.u.persp.targetZ,
projSets.u.persp.distance),
projSets.u.persp.azimuth, projSets.u.persp.rollAngle));
CameraElement.SetFocusDistance(FGeometryUtil::GetDistance3D(
abs(projSets.u.persp.cameraZ - projSets.u.persp.targetZ), projSets.u.persp.distance));
CameraElement.SetFocalLength(
FGeometryUtil::GetCameraFocalLength(CameraElement.GetSensorWidth(), projSets.u.persp.viewCone));
}
else
{
// Get the matrix.
CameraElement.SetTranslation(FGeometryUtil::GetTranslationVector(
reinterpret_cast< const double(*)[4] >(projSets.u.axono.invtranmat.tmx)));
CameraElement.SetRotation(FGeometryUtil::GetRotationQuat(
reinterpret_cast< const double(*)[4] >(projSets.u.axono.invtranmat.tmx)));
CameraElement.SetFocusDistance(10000);
CameraElement.SetFocalLength(FGeometryUtil::GetCameraFocalLength(CameraElement.GetSensorWidth(), 45));
}
}
else
{
UE_AC_DebugF("FSyncData::FCamera::InitWithCurrentView - APIEnv_Get3DProjectionSetsID returned error %d\n",
GSErr);
}
}
void FSyncData::FCamera::InitWithCameraElement()
{
API_Element camera;
Zap(&camera);
camera.header.guid = GSGuid2APIGuid(ElementId);
UE_AC_TestGSError(ACAPI_Element_Get(&camera));
IDatasmithCameraActorElement& CameraElement = static_cast< IDatasmithCameraActorElement& >(*ActorElement.Get());
const TCHAR* cameraSetLabel = TEXT("Unamed camera");
UE_AC_TestPtr(Parent);
if (Parent->GetElement().IsValid())
{
cameraSetLabel = Parent->GetElement()->GetLabel();
}
CameraElement.SetLabel(*FString::Printf(TEXT("%s Camera %d"), cameraSetLabel, Index));
const API_PerspPars& camPars = camera.camera.perspCam.persp;
CameraElement.SetTranslation(FGeometryUtil::GetTranslationVector({camPars.pos.x, camPars.pos.y, camPars.cameraZ}));
CameraElement.SetRotation(
FGeometryUtil::GetRotationQuat(FGeometryUtil::GetPitchAngle(camPars.cameraZ, camPars.targetZ, camPars.distance),
camPars.azimuth * RADDEG, camPars.rollAngle * RADDEG));
CameraElement.SetFocusDistance(
FGeometryUtil::GetDistance3D(abs(camPars.cameraZ - camPars.targetZ), camPars.distance));
CameraElement.SetFocalLength(
FGeometryUtil::GetCameraFocalLength(CameraElement.GetSensorWidth(), camPars.viewCone * RADDEG));
}
void FSyncData::FLight::Process(FProcessInfo* IOProcessInfo)
{
if (!ActorElement.IsValid())
{
if (Parameters.bIsAreaLight)
{
SetActorElement(FDatasmithSceneFactory::CreateAreaLight(GSStringToUE(ElementId.ToUniString())));
}
else if (Parameters.bIsParallelLight)
{
SetActorElement(FDatasmithSceneFactory::CreateDirectionalLight(GSStringToUE(ElementId.ToUniString())));
}
else
{
switch (LightData.LightType)
{
case ModelerAPI::Light::Type::DirectionLight:
SetActorElement(
FDatasmithSceneFactory::CreateDirectionalLight(GSStringToUE(ElementId.ToUniString())));
break;
case ModelerAPI::Light::Type::SpotLight:
SetActorElement(FDatasmithSceneFactory::CreateSpotLight(GSStringToUE(ElementId.ToUniString())));
break;
case ModelerAPI::Light::Type::PointLight:
SetActorElement(FDatasmithSceneFactory::CreatePointLight(GSStringToUE(ElementId.ToUniString())));
break;
default:
throw std::runtime_error(
Utf8StringFormat("FSyncData::FLight::Process - Invalid light type %d\n", LightData.LightType)
.c_str());
}
}
}
if (IsModified())
{
IDatasmithLightActorElement& LightElement = static_cast< IDatasmithLightActorElement& >(*ActorElement.Get());
const TCHAR* ParentLabel = TEXT("Unamed object");
UE_AC_TestPtr(Parent);
if (Parent->GetElement().IsValid())
{
ParentLabel = Parent->GetElement()->GetLabel();
}
LightElement.SetLabel(*FString::Printf(TEXT("%s - Light %d"), ParentLabel, Index));
if (Parent->GetActorElement().IsValid())
{
LightElement.SetLayer(Parent->GetActorElement()->GetLayer());
}
LightElement.SetTranslation(LightData.Position);
LightElement.SetRotation(LightData.Rotation);
LightElement.SetIntensity(Parameters.Intensity);
if (LightData.Color != FLinearColor::Black || Parameters.ColorComponentCount != 3)
{
LightElement.SetColor(LightData.Color);
}
else
{
LightElement.SetColor(ACRGBColorToUELinearColor(Parameters.GS_Color));
}
if (LightElement.IsA(EDatasmithElementType::PointLight))
{
IDatasmithPointLightElement& PointLightElement = static_cast< IDatasmithPointLightElement& >(LightElement);
if (Parameters.bUsePhotometric)
{
PointLightElement.SetIntensityUnits(Parameters.Units);
}
else
{
PointLightElement.SetAttenuationRadius(
float(Parameters.DetRadius * IOProcessInfo->SyncContext.ScaleLength));
}
}
if (LightElement.IsA(EDatasmithElementType::SpotLight))
{
IDatasmithSpotLightElement& PointLightElement = static_cast< IDatasmithSpotLightElement& >(LightElement);
float InnerConeAngleClamped = FGeometryUtil::Clamp(LightData.InnerConeAngle, 1.0f, 89.0f - 0.001f);
PointLightElement.SetInnerConeAngle(InnerConeAngleClamped);
float OuterConeAngleClamped =
FGeometryUtil::Clamp(LightData.OuterConeAngle, InnerConeAngleClamped + 0.001f, 89.0f);
PointLightElement.SetOuterConeAngle(OuterConeAngleClamped);
}
if (LightElement.IsA(EDatasmithElementType::AreaLight))
{
IDatasmithAreaLightElement& AreaLightElement = static_cast< IDatasmithAreaLightElement& >(LightElement);
EDatasmithLightShape LightShape = EDatasmithLightShape::None;
switch (Parameters.AreaShape)
{
case FLightGDLParameters::EC4dDetAreaShape::kRectangle:
case FLightGDLParameters::EC4dDetAreaShape::kCube:
case FLightGDLParameters::EC4dDetAreaShape::kLine:
LightShape = EDatasmithLightShape::Rectangle;
break;
case FLightGDLParameters::EC4dDetAreaShape::kDisc:
LightShape = EDatasmithLightShape::Disc;
break;
case FLightGDLParameters::EC4dDetAreaShape::kSphere:
case FLightGDLParameters::EC4dDetAreaShape::kHemisphere:
LightShape = EDatasmithLightShape::Sphere;
break;
case FLightGDLParameters::EC4dDetAreaShape::kCylinder:
case FLightGDLParameters::EC4dDetAreaShape::kPerpendicularCylinder:
LightShape = EDatasmithLightShape::Cylinder;
break;
default:
LightShape = EDatasmithLightShape::Rectangle;
break;
}
AreaLightElement.SetLightShape(LightShape);
EDatasmithAreaLightType AreaLightType = EDatasmithAreaLightType::Point;
switch (LightData.LightType)
{
case ModelerAPI::Light::Type::DirectionLight:
AreaLightType = EDatasmithAreaLightType::Rect;
break;
case ModelerAPI::Light::Type::SpotLight:
AreaLightType = EDatasmithAreaLightType::Spot;
break;
case ModelerAPI::Light::Type::PointLight:
AreaLightType = EDatasmithAreaLightType::Point;
break;
default:
AreaLightType = EDatasmithAreaLightType::Point;
break;
}
AreaLightElement.SetLightType(AreaLightType);
AreaLightElement.SetWidth(float(Parameters.AreaSize.x * IOProcessInfo->SyncContext.ScaleLength));
AreaLightElement.SetLength(float(Parameters.AreaSize.y * IOProcessInfo->SyncContext.ScaleLength));
}
if (!Parameters.IESFileName.IsEmpty())
{
LightElement.SetUseIes(true);
const FTexturesCache::FIESTexturesCacheElem& Texture =
IOProcessInfo->SyncContext.GetTexturesCache().GetIESTexture(IOProcessInfo->SyncContext,
GSStringToUE(Parameters.IESFileName));
LightElement.SetIesTexturePathName(*Texture.TexturePath);
LightElement.SetUseIesBrightness(Parameters.bUsePhotometric);
// LightElement.SetIesBrightnessScale(1.0);
// LightElement.SetIesRotation(const FQuat& IesRotation);
}
else
{
LightElement.SetUseIes(false);
LightElement.SetUseIesBrightness(false);
LightElement.SetIesTexturePathName(TEXT(""));
}
}
}
FSyncData::FLight::FLightGDLParameters::FLightGDLParameters() {}
FSyncData::FLight::FLightGDLParameters::FLightGDLParameters(const API_Guid& InLightGuid,
const FLibPartInfo* InLibPartInfo)
{
FAutoMemo AutoMemo(InLightGuid, APIMemoMask_AddPars);
if (AutoMemo.GSErr == NoError)
{
if (AutoMemo.Memo.params) // Can be null
{
double value;
if (GetParameter(AutoMemo.Memo.params, "gs_light_intensity", &value))
{
Intensity = value / 100.0 * 5000;
}
if (GetParameter(AutoMemo.Memo.params, "gs_color_red", &GS_Color.red))
{
++ColorComponentCount;
}
if (GetParameter(AutoMemo.Memo.params, "gs_color_green", &GS_Color.green))
{
++ColorComponentCount;
}
if (GetParameter(AutoMemo.Memo.params, "gs_color_blue", &GS_Color.blue))
{
++ColorComponentCount;
}
GetParameter(AutoMemo.Memo.params, "c4dPhoPhotometric", &bUsePhotometric);
if (bUsePhotometric)
{
GS::UniString PhoUnit; //
GetParameter(AutoMemo.Memo.params, "c4dPhoUnit", &PhoUnit);
if (PhoUnit == "candela")
{
Units = EDatasmithLightUnits::Candelas;
GetParameter(AutoMemo.Memo.params, "photoIntensityCandela", &Intensity);
}
if (PhoUnit == "lumen")
{
Units = EDatasmithLightUnits::Lumens;
GetParameter(AutoMemo.Memo.params, "photoIntensityLumen", &Intensity);
}
}
else
{
GetParameter(AutoMemo.Memo.params, "c4dDetRadius", &DetRadius);
}
GetParameter(AutoMemo.Memo.params, "c4dPhoIESFile", &IESFileName);
bIsAreaLight = GetParameter(AutoMemo.Memo.params, "c4dDetAreaX", &AreaSize.x);
bIsAreaLight |= GetParameter(AutoMemo.Memo.params, "c4dDetAreaY", &AreaSize.y);
bIsAreaLight |= GetParameter(AutoMemo.Memo.params, "c4dDetAreaZ", &AreaSize.z);
double TmpAreaShape;
if (GetParameter(AutoMemo.Memo.params, "iC4dDetAreaShape", &TmpAreaShape))
{
if (TmpAreaShape >= static_cast<double>(kDisc) && TmpAreaShape <= static_cast<double>(kPerpendicularCylinder))
{
AreaShape = (EC4dDetAreaShape) int(TmpAreaShape + 0.5);
}
}
GetParameter(AutoMemo.Memo.params, "rotAngleX", &WindowLightAngle);
GetParameter(AutoMemo.Memo.params, "angleSunAzimuth", &SunAzimuthAngle);
GetParameter(AutoMemo.Memo.params, "angleSunAltitude", &SunAltitudeAngle);
if (InLibPartInfo != nullptr)
{
static GS::Guid ParallelLightMainGuid("FF603AFE-10AE-466E-A360-87924FA7E24A");
static GS::Guid SunLightMainGuid("0A68517B-9FCA-483A-93A7-2E85FE6BDBF9");
bIsParallelLight =
InLibPartInfo->Guid.Main == ParallelLightMainGuid || InLibPartInfo->Guid.Main == SunLightMainGuid;
}
GetParameter(AutoMemo.Memo.params, "bGenShadow", &bGenShadow); // Currently not available in Datasmith.
}
}
else
{
UE_AC_DebugF(
"FSyncData::FLight::FLightGDLParameters::FLightGDLParameters - Error=%d when getting element memo\n",
AutoMemo.GSErr);
}
if (ColorComponentCount != 0 && ColorComponentCount != 3)
{
UE_AC_DebugF("FSyncData::FLight::FLightGDLParameters::FLightGDLParameters - ColorComponentCount is %u\n",
ColorComponentCount);
}
};
bool FSyncData::FLight::FLightGDLParameters::operator!=(const FLightGDLParameters& InOther) const
{
return GS_Color != InOther.GS_Color || ColorComponentCount != InOther.ColorComponentCount ||
Intensity != InOther.Intensity || bUsePhotometric != InOther.bUsePhotometric || Units != InOther.Units ||
DetRadius != InOther.DetRadius || IESFileName != InOther.IESFileName || AreaShape != InOther.AreaShape ||
AreaSize != InOther.AreaSize || WindowLightAngle != InOther.WindowLightAngle ||
SunAzimuthAngle != InOther.SunAzimuthAngle || SunAltitudeAngle != InOther.SunAltitudeAngle ||
bIsParallelLight != InOther.bIsParallelLight || bGenShadow != InOther.bGenShadow;
}
FSyncData::FLight::FLightData::FLightData() {}
FSyncData::FLight::FLightData::FLightData(const ModelerAPI::Light& InLight)
{
LightType = InLight.GetType();
InnerConeAngle = float(InLight.GetFalloffAngle1() * 180.0f / PI);
OuterConeAngle = float(InLight.GetFalloffAngle2() * 180.0f / PI);
Color = ACRGBColorToUELinearColor(InLight.GetColor());
Position = FGeometryUtil::GetTranslationVector(InLight.GetPosition());
Rotation = FGeometryUtil::GetRotationQuat(InLight.GetDirection(), InLight.GetUpVector());
}
bool FSyncData::FLight::FLightData::operator!=(const FLightData& InOther) const
{
return LightType != InOther.LightType || InnerConeAngle != InOther.InnerConeAngle ||
OuterConeAngle != InOther.OuterConeAngle || Color != InOther.Color || Position != InOther.Position ||
Rotation != InOther.Rotation;
}
const GS::Guid FSyncData::FHotLinksRoot::HotLinksRootGUID("C4BFD876-FDE9-4CCF-8899-12023968DC0D");
void FSyncData::FHotLinksRoot::Process(FProcessInfo* /* IOProcessInfo */)
{
if (!ActorElement.IsValid())
{
SetActorElement(FDatasmithSceneFactory::CreateActor(GSStringToUE(ElementId.ToUniString())));
ActorElement->SetLabel(TEXT("Hot Links"));
}
}
void FSyncData::FHotLinkNode::Process(FProcessInfo* IOProcessInfo)
{
if (!ActorElement.IsValid())
{
SetActorElement(FDatasmithSceneFactory::CreateActor(GSStringToUE(ElementId.ToUniString())));
#if AC_VERSION < 26
API_HotlinkNode hotlinkNode;
Zap(&hotlinkNode);
#else
API_HotlinkNode hotlinkNode = {0};
#endif
hotlinkNode.guid = GSGuid2APIGuid(ElementId);
GSErrCode err = ACAPI_Database(APIDb_GetHotlinkNodeID, &hotlinkNode);
if (err == NoError)
{
GS::UniString Label = hotlinkNode.name;
if (hotlinkNode.refFloorName[0] != '\0')
{
Label += " Floor ";
Label += hotlinkNode.refFloorName;
}
ActorElement->SetLabel(GSStringToUE(Label));
FMetaData MyMetaData(ActorElement);
const TCHAR* HotLinkType = TEXT("Unknown");
if (hotlinkNode.type == APIHotlink_Module)
{
HotLinkType = TEXT("Module");
}
else if (hotlinkNode.type == APIHotlink_XRef)
{
HotLinkType = TEXT("XRef");
}
MyMetaData.AddStringProperty(TEXT("HotLinkType"), HotLinkType);
if (hotlinkNode.sourceLocation != nullptr)
{
MyMetaData.AddStringProperty(TEXT("HotLinkLocation"), hotlinkNode.sourceLocation->ToDisplayText());
}
if (hotlinkNode.serverSourceLocation != nullptr)
{
MyMetaData.AddStringProperty(TEXT("HotLinkSharedLocation"),
hotlinkNode.serverSourceLocation->ToDisplayText());
}
MyMetaData.AddStringProperty(TEXT("StoryRangeType"), hotlinkNode.storyRangeType == APIHotlink_SingleStory
? TEXT("Single")
: TEXT("All"));
const TCHAR* SourceLinkType = TEXT("Unknown");
if (hotlinkNode.sourceType == APIHotlink_LocalFile)
{
SourceLinkType = TEXT("LocalFile");
}
else if (hotlinkNode.sourceType == APIHotlink_TWFS)
{
SourceLinkType = TEXT("TWFS");
}
else if (hotlinkNode.sourceType == APIHotlink_TWProject)
{
SourceLinkType = TEXT("TWProject");
}
MyMetaData.AddStringProperty(TEXT("StorySourceType"), SourceLinkType);
ReplaceMetaData(IOProcessInfo->SyncContext.GetScene(), MyMetaData.GetMetaData());
#if AC_VERSION < 25
// No need to free API_HotlinkNode allocated members starting with ArchiCAD 25
delete hotlinkNode.sourceLocation;
delete hotlinkNode.serverSourceLocation;
BMKillPtr(&hotlinkNode.userData.data);
#endif
}
else
{
UE_AC_DebugF("FSyncData::FHotLinkInstance::Process - ACAPI_Element_Get - Error=%d\n", err);
}
}
}
FSyncData::FHotLinkInstance::FHotLinkInstance(const GS::Guid& InGuid, FSyncDatabase* IOSyncDatabase)
: FSyncData::FActor(InGuid)
{
Transformation = {};
Transformation.tmx[0] = 1.0;
Transformation.tmx[5] = 1.0;
Transformation.tmx[10] = 1.0;
API_Element hotlinkElem;
Zap(&hotlinkElem);
GET_HEADER_TYPEID(hotlinkElem.header) = API_HotlinkID;
hotlinkElem.header.guid = GSGuid2APIGuid(ElementId);
GSErrCode err = ACAPI_Element_Get(&hotlinkElem);
if (err == NoError)
{
// Parent is a hot link node
FSyncData*& RefHotLinkNode = IOSyncDatabase->GetSyncData(APIGuid2GSGuid(hotlinkElem.hotlink.hotlinkNodeGuid));
FSyncData* HotLinkNode = RefHotLinkNode;
if (HotLinkNode == nullptr)
{
HotLinkNode = new FSyncData::FHotLinkNode(APIGuid2GSGuid(hotlinkElem.hotlink.hotlinkNodeGuid));
RefHotLinkNode = HotLinkNode;
if (!HotLinkNode->HasParent())
{
FSyncData*& HotLinksRoot = IOSyncDatabase->GetSyncData(FSyncData::FHotLinksRoot::HotLinksRootGUID);
if (HotLinksRoot == nullptr)
{
HotLinksRoot = new FSyncData::FHotLinksRoot();
HotLinksRoot->SetParent(&IOSyncDatabase->GetSceneSyncData());
}
HotLinkNode->SetParent(HotLinksRoot);
}
}
SetParent(HotLinkNode);
}
else
{
UE_AC_DebugF("FSyncData::FHotLinkInstance::FHotLinkInstance - ACAPI_Element_Get - Error=%d\n", err);
}
}
void FSyncData::FHotLinkInstance::Process(FProcessInfo* IOProcessInfo)
{
if (!ActorElement.IsValid())
{
SetActorElement(FDatasmithSceneFactory::CreateActor(GSStringToUE(ElementId.ToUniString())));
API_Element hotlinkElem;
Zap(&hotlinkElem);
GET_HEADER_TYPEID(hotlinkElem.header) = API_HotlinkID;
hotlinkElem.header.guid = GSGuid2APIGuid(ElementId);
GSErrCode err = ACAPI_Element_Get(&hotlinkElem);
if (err == NoError)
{
const TCHAR* HotLinkType = TEXT("Unknown");
if (hotlinkElem.hotlink.type == APIHotlink_Module)
{
HotLinkType = TEXT("Module");
}
else if (hotlinkElem.hotlink.type == APIHotlink_XRef)
{
HotLinkType = TEXT("XRef");
}
const TCHAR* ParentLabel = TEXT("Unamed object");
UE_AC_Assert(Parent != nullptr && Parent->ElementId == hotlinkElem.hotlink.hotlinkNodeGuid);
if (Parent->GetElement().IsValid())
{
ParentLabel = Parent->GetElement()->GetLabel();
}
ActorElement->SetLabel(*FString::Printf(TEXT("%s - %s Instance %llu"), ParentLabel, HotLinkType,
IOProcessInfo->GetCurrentIndex()));
Transformation = hotlinkElem.hotlink.transformation;
FMetaData MyMetaData(ActorElement);
MyMetaData.AddStringProperty(TEXT("HotLinkType"), HotLinkType);
ReplaceMetaData(IOProcessInfo->SyncContext.GetScene(), MyMetaData.GetMetaData());
// What do we do with hotlinkElem.hotlink.hotlinkGroupGuid ?
}
else
{
UE_AC_DebugF("FSyncData::FHotLinkInstance::Process - ACAPI_Element_Get - Error=%d\n", err);
}
}
}
// Start the process with this root observer
void FSyncData::FInterator::Start(FSyncData* Root)
{
Stop();
Stack.Add({Root, 0});
ProcessedCount = 0;
ProcessTime = 0.0;
}
// Stop processing
void FSyncData::FInterator::Stop()
{
// Stop process
Stack.Empty();
}
FSyncData::FInterator::EProcessControl FSyncData::FInterator::ProcessUntil(double TimeSliceEnd)
{
double startTime = FTimeStat::RealTimeClock();
EProcessControl ProcessControl = kContinue;
while (FTimeStat::RealTimeClock() < TimeSliceEnd && ProcessControl == kContinue)
{
FSyncData* Current = Next();
ProcessControl = Process(Current);
}
if (ProcessControl == kInterrupted)
{
Stop();
}
ProcessTime += FTimeStat::RealTimeClock() - startTime;
return ProcessControl;
}
// Return the next FSyncData
FSyncData* FSyncData::FInterator::Next()
{
FSyncData* Current = nullptr;
while (Stack.Num() != 0 && Current == nullptr)
{
FSyncData* Parent = Stack.Top().Parent;
// Start with root
if (ProcessedCount == 0)
{
ProcessedCount = 1;
return Parent;
}
// Traverse all tree
FChildsArray::SizeType ChildIndex = Stack.Top().ChildIndex;
if (ChildIndex < Parent->Childs.Num())
{
Current = Parent->Childs[ChildIndex++];
++ProcessedCount;
Stack.Top().ChildIndex = ChildIndex;
Stack.Add({Current, 0});
}
else
{
Stack.Pop(EAllowShrinking::No);
}
}
return Current;
}
// Return the index of the current.
FSyncData::FChildsArray::SizeType FSyncData::FInterator::GetCurrentIndex()
{
return Stack.Num() > 1 ? Stack[Stack.Num() - 2].ChildIndex : 0;
}
// Start the process with this root observer
void FSyncData::FProcessMetadata::Start(FSyncData* Root)
{
FSyncData::FInterator::Start(Root);
MetadataProcessedCount = 0;
bMetadataUpdated = false;
}
// Call ProcessMetaData for the sync data
FSyncData::FInterator::EProcessControl FSyncData::FProcessMetadata::Process(FSyncData* InCurrent)
{
if (InCurrent == nullptr)
{
return FInterator::kDone;
}
if (InCurrent->NeedTagsAndMetaDataUpdate())
{
++MetadataProcessedCount;
bMetadataUpdated |= InCurrent->ProcessMetaData(Synchronizer->GetSyncDatabase());
}
return FInterator::kContinue;
}
// Constructor
FSyncData::FAttachObservers::FAttachObservers() {}
// Start the process with this root observer
void FSyncData::FAttachObservers::Start(FSyncData* Root)
{
FSyncData::FInterator::Start(Root);
#if ATTACH_ONSERVER_STAT
AttachObserverProcessTimeStart.ReStart();
AttachObserverProcessTimeEnd = AttachObserverProcessTimeStart;
AttachObserverStartTime = FTimeStat::RealTimeClock();
AttachObserverTime = 0.0;
GetHeaderTime = 0.0;
AttachCount = 0;
#endif
}
FSyncData::FInterator::EProcessControl FSyncData::FAttachObservers::Process(FSyncData* InCurrent)
{
if (InCurrent == nullptr)
{
return FInterator::kDone;
}
if (InCurrent->AttachObserver(this))
{
return FInterator::kInterrupted;
}
return FInterator::kContinue;
}
// Process attachment until done or until time slice finish
bool FSyncData::FAttachObservers::ProcessAttachUntil(double TimeSliceEnd)
{
#if TRACE_ATTACH_OBSERVERS
int NbProcessedStart = GetProcessedCount();
double startTime = FTimeStat::RealTimeClock();
#endif
FInterator::EProcessControl ProcessControl = ProcessUntil(TimeSliceEnd);
#if TRACE_ATTACH_OBSERVERS
if (GetProcessedCount() - NbProcessedStart != 0)
{
UE_AC_TraceF("FSyncData::FAttachObservers::ProcessAttachUntil - Nb Processed = %d (Start=%lf, End=%lf)\n",
GetProcessedCount() - NbProcessedStart, startTime, FTimeStat::RealTimeClock());
}
#endif
#if ATTACH_ONSERVER_STAT
if (ProcessControl == FInterator::kInterrupted || ProcessControl == FInterator::kDone)
{
PrintStat();
}
#endif
return ProcessControl == FInterator::kInterrupted; // If interrupted then we request an update
}
#if ATTACH_ONSERVER_STAT
void FSyncData::FAttachObservers::CumulateStats(const FTimeStat& SlotStart, double AfterAttachObserver)
{
++AttachCount;
AttachObserverTime += AfterAttachObserver - SlotStart.GetCpuTime();
GetHeaderTime += FTimeStat::CpuTimeClock() - AfterAttachObserver;
AttachObserverProcessTimeEnd.AddDiff(SlotStart);
}
// Log attach observer statistics
void FSyncData::FAttachObservers::PrintStat()
{
if (AttachCount != 0)
{
double AttachObserversTime = FTimeStat::RealTimeClock() - AttachObserverStartTime;
if (AttachObserversTime < 0.0)
{
AttachObserversTime += AttachObserversTime + 24 * 60 * 60;
}
UE_AC_ReportF("TraceObserverStat - Count = %d TotalTime=%.1lfs (AttachObserver=%.1lfns, GetHeader=%.1lfns)\n",
AttachCount, AttachObserversTime, AttachObserverTime / AttachCount * 1000000.0,
GetHeaderTime / AttachCount * 1000000.0);
AttachObserverProcessTimeEnd.PrintDiff("Attach Observers", AttachObserverProcessTimeStart);
AttachCount = 0;
}
}
#endif
END_NAMESPACE_UE_AC