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

915 lines
26 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SyncDatabase.h"
#include "MaterialsDatabase.h"
#include "TexturesCache.h"
#include "ElementID.h"
#include "Utils/ElementTools.h"
#include "GeometryUtil.h"
#include "Utils/3DElement2String.h"
#include "Utils/Element2String.h"
#include "Utils/TaskMgr.h"
#include "ModelMeshBody.hpp"
#include "Light.hpp"
#include "AttributeIndex.hpp"
#undef TicksPerSecond
#include "DatasmithUtils.h"
#include "IDirectLinkUI.h"
#include "IDatasmithExporterUIModule.h"
#include "FileManager.h"
#include "Paths.h"
#if PLATFORM_MAC
#include "Misc/ConfigCacheIni.h"
#endif
BEGIN_NAMESPACE_UE_AC
// Control access on this object (for queue operations)
GS::Lock FMeshClass::AccessControl;
// Condition variable
GS::Condition FMeshClass::CV(FMeshClass::AccessControl);
// Define the mesh element for this hash value
void FMeshClass::SetMeshElement(const TSharedPtr< IDatasmithMeshElement >& InMeshElement)
{
if (MeshElement.IsValid())
{
UE_AC_DebugF("FMeshClass::SetMeshElement - Mesh class %08x:\"%s\" already defined\n", Hash,
TCHAR_TO_UTF8(MeshElement->GetName()));
UE_AC_Assert(bMeshElementInitialized);
return;
}
GS::Guard< GS::Lock > lck(AccessControl);
bMeshElementInitialized = true;
MeshElement = InMeshElement;
}
// Add an instance, waiting for the resulting mesh
/* return false if we need to build the mesh */
FMeshClass::EBuildMesh FMeshClass::AddInstance(FSyncData* InInstance, FSyncDatabase* IOSyncDatabase)
{
{
GS::Guard< GS::Lock > lck(AccessControl);
if (!bMeshElementInitialized)
{
bool bHasAnother = HeadInstances != nullptr;
HeadInstances = new FSyncDataList(InInstance, HeadInstances);
if (bHasAnother)
{
return kDontBuild; // Another instance is already building this object
}
return kBuild; // First instance, we must build the mesh
}
}
// We have a mesh, we set it the sync data
SetInstanceMesh(InInstance, IOSyncDatabase);
return kDontBuild;
}
FSyncData* FMeshClass::PopInstance()
{
FSyncDataList* Head = nullptr;
{
GS::Guard< GS::Lock > lck(AccessControl);
Head = HeadInstances;
if (Head == nullptr)
{
return nullptr;
}
HeadInstances = Head->Next;
}
FSyncData* SyncData = Head->Element;
delete Head;
return SyncData;
}
void FMeshClass::SetInstanceMesh(FSyncData* InInstance, FSyncDatabase* IOSyncDatabase) const
{
InInstance->SetMesh(IOSyncDatabase, MeshElement);
}
void FMeshClass::SetWaitingInstanceMesh(FSyncDatabase* IOSyncDatabase)
{
FSyncData* SyncData = PopInstance();
while (SyncData != nullptr)
{
SetInstanceMesh(SyncData, IOSyncDatabase);
SyncData = PopInstance();
}
}
FMeshCacheIndexor::FMeshCacheIndexor(const TCHAR* InIndexFilePath)
: IndexFilePath(InIndexFilePath)
, AccessCondition(AccessControl)
{
}
FMeshCacheIndexor::~FMeshCacheIndexor()
{
if (bChanged)
{
UE_AC_VerboseF("FMeshCacheIndexor::~FMeshCacheIndexor - Cache hasn't been saved \"%s\"\n",
TCHAR_TO_UTF8(*IndexFilePath));
}
}
const FMeshDimensions* FMeshCacheIndexor::FindMesh(const TCHAR* InMeshName) const
{
const FMeshDimensions* Dimensions = nullptr;
GS::Guard< GS::Lock > Lock(AccessControl);
const TUniquePtr< FMeshDimensions >* Found = Name2Dimensions.Find(InMeshName);
if (Found != nullptr)
{
Dimensions = Found->Get();
}
return Dimensions;
};
void FMeshCacheIndexor::AddMesh(const IDatasmithMeshElement& InMesh)
{
GS::Guard< GS::Lock > Lock(AccessControl);
if (Name2Dimensions.Find(InMesh.GetName()) == nullptr)
{
Name2Dimensions.Add(InMesh.GetName(), MakeUnique< FMeshDimensions >(InMesh));
bChanged = true;
}
}
#define NoErrorCall(Err, Call) \
{ \
if (Err == NoError) \
Err = Call; \
}
void FMeshCacheIndexor::SaveToFile()
{
GS::Guard< GS::Lock > Lock(AccessControl);
if (bChanged)
{
IO::File Writer(IO::Location(UEToGSString(*IndexFilePath)), IO::File::Create);
GSErrCode GSErr = Writer.Open(IO::File::WriteMode);
NoErrorCall(GSErr, Writer.GetStatus());
if (GSErr == NoError)
{
int32 NbEntries = Name2Dimensions.Num();
UE_AC_VerboseF("FMeshCacheIndexor::SaveToFile - Save %d entries to \"%s\"\n", NbEntries,
TCHAR_TO_UTF8(*IndexFilePath));
GSErr = Writer.Write(NbEntries);
for (const MapName2Dimensions::ElementType& ItName2Dimensions : Name2Dimensions)
{
utf8_string MeshName(TCHAR_TO_UTF8(*ItName2Dimensions.Key));
GS::USize MeshNameSize = (GS::USize)MeshName.size();
NoErrorCall(GSErr, Writer.Write(MeshNameSize));
NoErrorCall(GSErr, Writer.WriteBin(MeshName.c_str(), MeshNameSize));
NoErrorCall(GSErr, Writer.Write(ItName2Dimensions.Value->Area));
NoErrorCall(GSErr, Writer.Write(ItName2Dimensions.Value->Depth));
NoErrorCall(GSErr, Writer.Write(ItName2Dimensions.Value->Height));
NoErrorCall(GSErr, Writer.Write(ItName2Dimensions.Value->Width));
int32 Mark = 0x600DF00D;
NoErrorCall(GSErr, Writer.Write(Mark));
}
}
if (GSErr == NoError)
{
bChanged = false;
}
else
{
UE_AC_DebugF("FMeshCacheIndexor::SaveToFile - \"%s\" Error %s\n", TCHAR_TO_UTF8(*IndexFilePath),
GetErrorName(GSErr));
}
}
}
void FMeshCacheIndexor::ReadFromFile()
{
GS::Guard< GS::Lock > Lock(AccessControl);
bChanged = false;
IO::File Reader(IO::Location(UEToGSString(*IndexFilePath)), IO::File::Fail);
GSErrCode GSErr = Reader.Open(IO::File::ReadMode);
if (GSErr == IO::FileErrors)
{
// Missing file is normal for the first time on a new cache folder or when exporting
UE_AC_TraceF("FMeshCacheIndexor::ReadFromFile - Can't open \"%s\"\n", TCHAR_TO_UTF8(*IndexFilePath));
return;
}
NoErrorCall(GSErr, Reader.GetStatus());
int32 NbEntries = 0;
NoErrorCall(GSErr, Reader.Read(NbEntries));
if (GSErr == NoError)
{
UE_AC_VerboseF("FMeshCacheIndexor::ReadFromFile - Read %d entries from \"%s\"\n", NbEntries,
TCHAR_TO_UTF8(*IndexFilePath));
for (int32 Index = 0; Index < NbEntries && GSErr == NoError; ++Index)
{
utf8_string MeshName;
GS::USize MeshNameSize = 0;
NoErrorCall(GSErr, Reader.Read(MeshNameSize));
if (GSErr == NoError)
{
MeshName.resize(MeshNameSize);
NoErrorCall(GSErr, Reader.ReadBin(const_cast< char* >(MeshName.c_str()), MeshNameSize));
}
if (strlen(MeshName.c_str()) == MeshNameSize)
{
NoErrorCall(GSErr, NoError);
}
else
{
NoErrorCall(GSErr, ErrIO);
}
TUniquePtr< FMeshDimensions > Dimensions = MakeUnique< FMeshDimensions >();
NoErrorCall(GSErr, Reader.Read(Dimensions->Area));
NoErrorCall(GSErr, Reader.Read(Dimensions->Depth));
NoErrorCall(GSErr, Reader.Read(Dimensions->Height));
NoErrorCall(GSErr, Reader.Read(Dimensions->Width));
int32 Mark = 0;
NoErrorCall(GSErr, Reader.Read(Mark));
if (Mark == 0x600DF00D)
{
NoErrorCall(GSErr, NoError);
}
else
{
NoErrorCall(GSErr, ErrIO);
}
if (GSErr == NoError)
{
Name2Dimensions.Add(UTF8_TO_TCHAR(MeshName.c_str()), std::move(Dimensions));
}
}
}
if (GSErr == NoError)
{
bChanged = false;
}
else
{
// On any errors, we reset our index, will be automatically rebuild.
Name2Dimensions.Empty();
UE_AC_DebugF("FMeshCacheIndexor::ReadFromFile - \"%s\" Error %s\n", TCHAR_TO_UTF8(*IndexFilePath),
GetErrorName(GSErr));
}
}
#if defined(DEBUG) && 0
#define UE_AC_DO_TRACE 1
#else
#define UE_AC_DO_TRACE 0
#endif
// Constructor
FSyncDatabase::FSyncDatabase(const TCHAR* InSceneName, const TCHAR* InSceneLabel, const TCHAR* InAssetsPath,
const GS::UniString& InAssetsCache)
: Scene(FDatasmithSceneFactory::CreateScene(*FDatasmithUtils::SanitizeObjectName(InSceneName)))
, AssetsFolderPath(InAssetsPath)
, MaterialsDatabase(new FMaterialsDatabase())
, TexturesCache(new FTexturesCache(InAssetsCache))
, MeshIndexor(*FPaths::Combine(InAssetsPath, TEXT("Meshes.CacheIndex")))
{
Scene->SetLabel(InSceneLabel);
MeshIndexor.ReadFromFile();
}
// Destructor
FSyncDatabase::~FSyncDatabase()
{
// Delete all sync data content by simulatin an emptying a 3d model
ResetBeforeScan();
CleanAfterScan();
int32 RemainingCount = ElementsSyncDataMap.Num();
if (RemainingCount != 0)
{
UE_AC_DebugF("FSyncDatabase::~FSyncDatabase - Database not emptied - %u Remaining\n", RemainingCount);
for (TPair< FGuid, FSyncData* >& Iter : ElementsSyncDataMap)
{
delete Iter.Value;
Iter.Value = nullptr;
}
}
delete MaterialsDatabase;
MaterialsDatabase = nullptr;
delete TexturesCache;
TexturesCache = nullptr;
}
// Return the asset file path
const TCHAR* FSyncDatabase::GetAssetsFolderPath() const
{
return *AssetsFolderPath;
}
// Scan all elements, to determine if they need to be synchronized
void FSyncDatabase::Synchronize(const FSyncContext& InSyncContext)
{
ResetBeforeScan();
UInt32 ModifiedCount = ScanElements(InSyncContext);
InSyncContext.NewPhase(kCommonSetUpLights, 0);
// Cameras from all cameras set
InSyncContext.NewPhase(kCommonSetUpCameras, 0);
ScanCameras(InSyncContext);
// Cameras from the current view
FSyncData*& CameraSyncData = GetSyncData(FSyncData::FCamera::CurrentViewGUID);
if (CameraSyncData == nullptr)
{
CameraSyncData = new FSyncData::FCamera(FSyncData::FCamera::CurrentViewGUID, 0);
CameraSyncData->SetParent(&GetSceneSyncData());
CameraSyncData->MarkAsModified();
}
CameraSyncData->MarkAsExisting();
CleanAfterScan();
InSyncContext.NewPhase(kCommonConvertElements, ModifiedCount);
FSyncData::FProcessInfo ProcessInfo(InSyncContext);
while (ProcessInfo.ProcessUntil(FTimeStat::RealTimeClock() + 60) == FSyncData::FInterator::kContinue)
{
UE_AC_TraceF("FSyncDatabase::Synchronize - %d processed\n", ProcessInfo.GetProcessedCount());
}
UE_AC_TraceF("FSyncDatabase::Synchronize - Done with %d processed\n", ProcessInfo.GetProcessedCount());
FTaskMgr::GetMgr()->Join(InSyncContext.GetProgression());
}
// Before a scan we reset our sync data, so we can detect when an element has been modified or destroyed
void FSyncDatabase::ResetBeforeScan()
{
for (const TPair< FGuid, FSyncData* >& Iter : ElementsSyncDataMap)
{
Iter.Value->ResetBeforeScan();
}
}
inline const FGuid& GSGuid2FGuid(const GS::Guid& InGuid)
{
return reinterpret_cast< const FGuid& >(InGuid);
}
inline const GS::Guid& FGuid2GSGuid(const FGuid& InGuid)
{
return reinterpret_cast< const GS::Guid& >(InGuid);
}
// After a scan, but before syncing, we delete obsolete syncdata (and it's Datasmith Element)
void FSyncDatabase::CleanAfterScan()
{
FSyncData** SyncData = ElementsSyncDataMap.Find(GSGuid2FGuid(FSyncData::FScene::SceneGUID));
if (SyncData != nullptr)
{
(**SyncData).CleanAfterScan(this);
}
}
// Get existing sync data for the specified guid
FSyncData*& FSyncDatabase::GetSyncData(const GS::Guid& InGuid)
{
return ElementsSyncDataMap.FindOrAdd(GSGuid2FGuid(InGuid), nullptr);
}
FSyncData& FSyncDatabase::GetSceneSyncData()
{
FSyncData*& SceneSyncData = GetSyncData(FSyncData::FScene::SceneGUID);
if (SceneSyncData == nullptr)
{
SceneSyncData = new FSyncData::FScene();
}
return *SceneSyncData;
}
FSyncData& FSyncDatabase::GetLayerSyncData(short InLayer)
{
FSyncData*& Layer = GetSyncData(FSyncData::FLayer::GetLayerGUID(InLayer));
if (Layer == nullptr)
{
Layer = new FSyncData::FLayer(FSyncData::FLayer::GetLayerGUID(InLayer));
Layer->SetParent(&GetSceneSyncData());
}
return *Layer;
}
// Delete obsolete syncdata (and it's Datasmith Element)
void FSyncDatabase::DeleteSyncData(const GS::Guid& InGuid)
{
FSyncData** SyncData = ElementsSyncDataMap.Find(GSGuid2FGuid(InGuid));
if (SyncData != nullptr)
{
ElementsSyncDataMap.Remove(GSGuid2FGuid(InGuid));
}
else
{
UE_AC_DebugF("FSyncDatabase::Delete {%s}\n", InGuid.ToUniString().ToUtf8());
}
}
// Return the name of the specified layer
const FString& FSyncDatabase::GetLayerName(short InLayerIndex)
{
FString* Found = LayerIndex2Name.Find(InLayerIndex);
if (Found == nullptr)
{
#if AC_VERSION > 26
LayerIndex2Name.Add(InLayerIndex, GSStringToUE(UE_AC::GetLayerName(ACAPI_CreateAttributeIndex(InLayerIndex))));
#else
LayerIndex2Name.Add(InLayerIndex, GSStringToUE(UE_AC::GetLayerName(InLayerIndex)));
#endif
Found = LayerIndex2Name.Find(InLayerIndex);
UE_AC_TestPtr(Found);
}
return *Found;
}
// Set the mesh in the handle and take care of mesh life cycle.
bool FSyncDatabase::SetMesh(TSharedPtr< IDatasmithMeshElement >* Handle,
const TSharedPtr< IDatasmithMeshElement >& InMesh)
{
if (Handle->IsValid())
{
if (InMesh.IsValid() && FCString::Strcmp(Handle->Get()->GetName(), InMesh->GetName()) == 0)
{
return false; // No change : Same name (hash) --> Same mesh
}
{
GS::Guard< GS::Lock > lck(HashToMeshInfoAccesControl);
FMeshInfo* Older = HashToMeshInfo.Find(Handle->Get()->GetName());
UE_AC_TestPtr(Older);
if (--Older->Count == 0)
{
Scene->RemoveMesh(Older->Mesh);
HashToMeshInfo.Remove(Handle->Get()->GetName());
}
}
Handle->Reset();
}
else
{
if (!InMesh.IsValid())
{
return false; // No change : No mesh before and no mesh after
}
}
if (InMesh.IsValid())
{
{
GS::Guard< GS::Lock > lck(HashToMeshInfoAccesControl);
FMeshInfo& MeshInfo = HashToMeshInfo.FindOrAdd(InMesh->GetName());
if (!MeshInfo.Mesh.IsValid())
{
MeshInfo.Mesh = InMesh;
Scene->AddMesh(InMesh);
}
++MeshInfo.Count;
}
*Handle = InMesh;
}
return true;
}
FMeshClass* FSyncDatabase::GetMeshClass(GS::ULong InHash) const
{
const TUniquePtr< FMeshClass >* Ptr = MeshClasses.GetPtr(InHash);
return Ptr == nullptr ? nullptr : Ptr->Get();
}
void FSyncDatabase::AddInstance(GS::ULong InHash, TUniquePtr< FMeshClass >&& InInstance)
{
MeshClasses.Add(InHash, std::move(InInstance));
}
// Return the libpart from it's index
FLibPartInfo* FSyncDatabase::GetLibPartInfo(GS::Int32 InIndex)
{
TUniquePtr< FLibPartInfo >* LibPartInfo = IndexToLibPart.Find(InIndex);
if (LibPartInfo == nullptr)
{
LibPartInfo = &IndexToLibPart.Add(InIndex, MakeUnique< FLibPartInfo >());
LibPartInfo->Get()->Initialize(InIndex);
}
return LibPartInfo->Get();
}
// Return the libpart from it's unique id
FLibPartInfo* FSyncDatabase::GetLibPartInfo(const char* InUnID)
{
FGSUnID UnID;
GSErrCode GSErr = UnID.InitWithString(InUnID);
if (GSErr != NoError)
{
UE_AC_DebugF("FSyncDatabase::GetLibPartInfo - InitWithString(\"%s\") return error\n", InUnID);
return nullptr;
}
if (UnID.Main == GS::NULLGuid && UnID.Rev == GS::NULLGuid)
{
return nullptr;
}
FLibPartInfo** LibPartInfoPtr = UnIdToLibPart.Find(UnID);
if (LibPartInfoPtr == nullptr)
{
FAuto_API_LibPart LibPart;
strncpy(LibPart.ownUnID, InUnID, sizeof(LibPart.ownUnID));
GSErrCode err = ACAPI_LibPart_Search(&LibPart, false);
if (err != NoError)
{
UE_AC_DebugF("FSyncDatabase::GetLibPartInfo - Can't find libpart \"%s\"\n", InUnID);
return nullptr;
}
FLibPartInfo* LibPartInfo = GetLibPartInfo(LibPart.index);
UnIdToLibPart.Add(UnID, LibPartInfo);
return LibPartInfo;
}
return *LibPartInfoPtr;
}
#if PLATFORM_MAC
static const TCHAR* DirectLinkExporter = TEXT("DirectLinkExporter");
static const TCHAR* DirectLinkCacheSectionAndValue = TEXT("DLCacheFolder");
static FString DirectLinkCacheDirectory;
#endif
// Return the cache path
GS::UniString FSyncDatabase::GetCachePath()
{
const TCHAR* CacheDirectory = nullptr;
IDatasmithExporterUIModule* DsExporterUIModule = IDatasmithExporterUIModule::Get();
if (DsExporterUIModule != nullptr)
{
IDirectLinkUI* DLUI = DsExporterUIModule->GetDirectLinkExporterUI();
if (DLUI != nullptr)
{
CacheDirectory = DLUI->GetDirectLinkCacheDirectory();
}
}
if (CacheDirectory != nullptr && *CacheDirectory != 0)
{
return UEToGSString(CacheDirectory);
}
else
{
#if PLATFORM_MAC
if (DirectLinkCacheDirectory.IsEmpty())
{
FString ConfigPath(FPaths::Combine(FPaths::GeneratedConfigDir(), DirectLinkExporter).Append(TEXT(".ini")));
if (!GConfig->GetString(DirectLinkCacheSectionAndValue, DirectLinkCacheSectionAndValue,
DirectLinkCacheDirectory, ConfigPath))
{
DirectLinkCacheDirectory = FPaths::Combine(FPlatformProcess::UserTempDir(), TEXT("DLExporter"));
GConfig->SetString(DirectLinkCacheSectionAndValue, DirectLinkCacheSectionAndValue,
*DirectLinkCacheDirectory, ConfigPath);
}
}
return UEToGSString(*DirectLinkCacheDirectory);
#else
return GetAddonDataDirectory();
#endif
}
}
#if PLATFORM_MAC
// Change the cache path
void FSyncDatabase::SetCachePath(GS::UniString& InCacheDirectory)
{
// Save to config file
DirectLinkCacheDirectory = GSStringToUE(InCacheDirectory);
FString ConfigPath(FPaths::Combine(FPaths::GeneratedConfigDir(), DirectLinkExporter).Append(TEXT(".ini")));
GConfig->SetString(DirectLinkCacheSectionAndValue, DirectLinkCacheSectionAndValue, *DirectLinkCacheDirectory,
ConfigPath);
}
#endif
// SetSceneInfo
void FSyncDatabase::SetSceneInfo()
{
IDatasmithScene& TheScene = *Scene;
// Set up basics scene informations
TheScene.SetHost(TEXT("ARCHICAD"));
TheScene.SetVendor(TEXT("Graphisoft"));
TheScene.SetProductName(TEXT("ARCHICAD"));
TheScene.SetProductVersion(UTF8_TO_TCHAR(UE_AC_STRINGIZE(AC_VERSION)));
}
void FSyncDatabase::ResetMeshClasses()
{
for (auto& IterMeshClass : MeshClasses)
{
#if AC_VERSION < 28
FMeshClass& MeshClass = **IterMeshClass.value;
#else
FMeshClass& MeshClass = *IterMeshClass.value.Get();
#endif
MeshClass.InstancesCount = 0;
MeshClass.TransformCount = 0;
}
}
void FSyncDatabase::CleanMeshClasses(const FSyncContext& InSyncContext)
{
int MeshClassesForgetCount = 0;
for (auto& IterMeshClass : MeshClasses)
{
#if AC_VERSION < 28
FMeshClass& MeshClass = **IterMeshClass.value;
#else
FMeshClass& MeshClass = *IterMeshClass.value.Get();
#endif
if (MeshClass.InstancesCount == 0 && MeshClass.ElementType != ModelerAPI::Element::Type::UndefinedElement)
{
MeshClass.ElementType = ModelerAPI::Element::Type::UndefinedElement;
MeshClass.MeshElement.Reset();
MeshClass.bMeshElementInitialized = false;
MeshClass.TransformCount = 0;
MeshClassesForgetCount++;
}
}
InSyncContext.Stats.TotalMeshClassesForgot = MeshClassesForgetCount;
}
void FSyncDatabase::ReportMeshClasses() const
{
#if defined(DEBUG) && 1
for (const auto& IterMeshClass : MeshClasses)
{
const FMeshClass& MeshClass = **IterMeshClass.value;
if (MeshClass.InstancesCount > 1)
{
if (MeshClass.InstancesCount != MeshClass.TransformCount)
{
UE_AC_TraceF("FSyncDatabase::ReportMeshClasses - %u instances of %s for hash %u, TransfoCount=%u\n",
MeshClass.InstancesCount, FElementID::GetTypeName(MeshClass.ElementType), MeshClass.Hash,
MeshClass.TransformCount);
}
else
{
UE_AC_VerboseF("FSyncDatabase::ReportMeshClasses - %u instances of %s for hash %u\n",
MeshClass.InstancesCount, FElementID::GetTypeName(MeshClass.ElementType),
MeshClass.Hash);
}
}
else if (MeshClass.InstancesCount == 1 && MeshClass.TransformCount == 1)
{
UE_AC_VerboseF("FSyncDatabase::ReportMeshClasses - %s for hash %u has transform\n",
FElementID::GetTypeName(MeshClass.ElementType), MeshClass.Hash);
}
}
#endif
}
// Scan all elements, to determine if they need to be synchronized
UInt32 FSyncDatabase::ScanElements(const FSyncContext& InSyncContext)
{
// We create this objects here to not construct/destroy at each iteration
FElementID ElementID(InSyncContext);
ResetMeshClasses();
// Loop on all 3D elements
UInt32 ModifiedCount = 0;
GS::Int32 NbElements = InSyncContext.GetModel().GetElementCount();
UE_AC_STAT(InSyncContext.Stats.TotalElements = NbElements);
InSyncContext.NewPhase(kCommonCollectElements, NbElements);
for (GS::Int32 IndexElement = 1; IndexElement <= NbElements; IndexElement++)
{
InSyncContext.NewCurrentValue(IndexElement);
// Get next valid 3d element
ElementID.InitElement(IndexElement);
if (ElementID.IsInvalid())
{
#if UE_AC_DO_TRACE && 1
UE_AC_TraceF("FSynchronizer::ScanElements - Element Index=%d Is invalid\n", IndexElement);
#endif
continue;
}
API_Guid ElementGuid = GSGuid2APIGuid(ElementID.GetElement3D().GetElemGuid());
if (ElementGuid == APINULLGuid)
{
#if UE_AC_DO_TRACE && 1
UE_AC_TraceF("FSynchronizer::ScanElements - Element Index=%d hasn't id\n", IndexElement);
#endif
continue;
}
#if UE_AC_DO_TRACE && 1
// Get the name of the element (To help debugging)
GS::UniString ElementInfo;
FElementTools::GetInfoString(ElementGuid, &ElementInfo);
#if 0
// Print element info in debugger view
FElement2String::DumpInfo(ElementGuid);
#endif
#endif
// Check 3D geometry bounding box
Box3D box = ElementID.GetElement3D().GetBounds(ModelerAPI::CoordinateSystem::ElemLocal);
// Bonding box is empty, must not happen, but it happen
if (box.xMin > box.xMax || box.yMin > box.yMax || box.zMin > box.zMax)
{
#if UE_AC_DO_TRACE && 1
UE_AC_TraceF("FSynchronizer::ScanElements - EmptyBox for %s \"%s\" %d %s", ElementID.GetTypeName(),
ElementInfo.ToUtf8(), IndexElement, APIGuidToString(ElementGuid).ToUtf8());
#endif
continue; // Object is invisible (hidden layer or cutting plane)
}
// Get the header (modification time, layer, floor, element type...)
if (!ElementID.InitHeader())
{
#if UE_AC_DO_TRACE && 1
UE_AC_DebugF("FSynchronizer::ScanElements - Can't get header for %d %s", IndexElement,
APIGuidToString(ElementGuid).ToUtf8());
#endif
continue;
}
UE_AC_STAT(InSyncContext.Stats.TotalElementsWithGeometry++);
ElementID.GetMeshClass();
// Get sync data for this element (Create or reuse already existing)
FSyncData*& RefSyncData = GetSyncData(APIGuid2GSGuid(ElementID.GetHeader().guid));
FSyncData* SyncData = RefSyncData;
if (SyncData == nullptr)
{
SyncData = new FSyncData::FElement(APIGuid2GSGuid(ElementID.GetHeader().guid), InSyncContext);
RefSyncData = SyncData;
}
ElementID.SetSyncData(SyncData);
SyncData->Update(ElementID);
if (SyncData->IsModified())
{
++ModifiedCount;
}
// Add lights
if (ElementID.GetElement3D().GetLightCount() > 0)
{
ScanLights(ElementID);
}
}
UE_AC_STAT(InSyncContext.Stats.TotalElementsModified = ModifiedCount);
InSyncContext.NewCurrentValue(NbElements);
ReportMeshClasses();
CleanMeshClasses(InSyncContext);
return ModifiedCount;
}
#if defined(DEBUG) && 0
#define UE_AC_DO_TRACE_LIGHTS 1
#else
#define UE_AC_DO_TRACE_LIGHTS 0
#endif
// Scan all lights of this element
void FSyncDatabase::ScanLights(FElementID& InElementID)
{
GS::Int32 LightsCount = InElementID.GetElement3D().GetLightCount();
if (LightsCount > 0)
{
const API_Elem_Head& Header = InElementID.GetHeader();
#if UE_AC_DO_TRACE_LIGHTS
UE_AC_TraceF("%s", FElement2String::GetElementAsShortString(Header.guid).c_str());
UE_AC_TraceF("%s", FElement2String::GetParametersAsString(Header.guid).c_str());
UE_AC_TraceF("%s", F3DElement2String::ElementLight2String(InElementID.Element3D).c_str());
#endif
FSyncData::FLight::FLightGDLParameters LightGDLParameters(Header.guid, InElementID.GetLibPartInfo());
ModelerAPI::Light Light;
for (GS::Int32 LightIndex = 1; LightIndex <= LightsCount; ++LightIndex)
{
InElementID.GetElement3D().GetLight(LightIndex, &Light);
FSyncData::FLight::FLightData LightData(Light);
if (LightData.LightType == ModelerAPI::Light::DirectionLight ||
LightData.LightType == ModelerAPI::Light::SpotLight ||
LightData.LightType == ModelerAPI::Light::PointLight)
{
API_Guid LightId = CombineGuid(Header.guid, GuidFromMD5(LightIndex));
FSyncData*& SyncData = FSyncDatabase::GetSyncData(APIGuid2GSGuid(LightId));
if (SyncData == nullptr)
{
SyncData = new FSyncData::FLight(APIGuid2GSGuid(LightId), LightIndex);
SyncData->SetParent(InElementID.GetSyncData());
SyncData->MarkAsModified();
}
FSyncData::FLight& LightSyncData = static_cast<FSyncData::FLight&>(*SyncData);
LightSyncData.MarkAsExisting();
LightSyncData.SetLightData(LightData);
LightSyncData.SetValuesFromParameters(LightGDLParameters);
}
else
{
static std::set< ModelerAPI::Light::Type > SignaledTypes;
if (SignaledTypes.find(LightData.LightType) == SignaledTypes.end())
{
SignaledTypes.insert(LightData.LightType);
UE_AC_DebugF("FSyncDatabase::ScanLights - Unhandled LightType=%d, %s", LightData.LightType,
InElementID.GetElementName());
}
}
}
}
}
// Scan all cameras
void FSyncDatabase::ScanCameras(const FSyncContext& /* InSyncContext */)
{
GS::Array< API_Guid > ElemList;
GSErrCode GSErr = ACAPI_Element_GetElemList(API_CamSetID, &ElemList);
if (GSErr != NoError)
{
UE_AC_DebugF("FSyncDatabase::ScanCameras - ACAPI_Element_GetElemList return %d", GSErr);
return;
}
Int32 IndexCamera = 0;
API_Guid NextCamera = APINULLGuid;
for (API_Guid ElemGuid : ElemList)
{
// Get info on this element
API_Element cameraSet;
Zap(&cameraSet);
cameraSet.header.guid = ElemGuid;
GSErr = ACAPI_Element_Get(&cameraSet);
if (GSErr != NoError)
{
if (GSErr != APIERR_DELETED)
{
UE_AC_DebugF("FSyncDatabase::ScanCameras - ACAPI_Element_Get return %d", GSErr);
}
continue;
}
if (cameraSet.camset.firstCam == APINULLGuid)
{
continue;
}
FSyncData*& RefCameraSetSyncData = FSyncDatabase::GetSyncData(APIGuid2GSGuid(cameraSet.header.guid));
FSyncData* CameraSetSyncData = RefCameraSetSyncData;
if (CameraSetSyncData == nullptr)
{
GS::UniString CamSetName(cameraSet.camset.name);
CameraSetSyncData = new FSyncData::FCameraSet(APIGuid2GSGuid(cameraSet.header.guid), CamSetName,
cameraSet.camset.perspPars.openedPath);
RefCameraSetSyncData = CameraSetSyncData;
CameraSetSyncData->SetParent(&GetSceneSyncData());
}
CameraSetSyncData->MarkAsExisting();
IndexCamera = 0;
NextCamera = cameraSet.camset.firstCam;
while (GSErr == NoError && NextCamera != APINULLGuid)
{
API_Element camera;
Zap(&camera);
camera.header.guid = NextCamera;
GSErr = ACAPI_Element_Get(&camera);
if (GSErr == NoError)
{
FSyncData*& CameraSyncData = FSyncDatabase::GetSyncData(APIGuid2GSGuid(camera.header.guid));
if (CameraSyncData == nullptr)
{
CameraSyncData = new FSyncData::FCamera(APIGuid2GSGuid(camera.header.guid), ++IndexCamera);
CameraSyncData->SetParent(CameraSetSyncData);
}
CameraSyncData->MarkAsExisting();
CameraSyncData->CheckModificationStamp(camera.header.modiStamp);
}
NextCamera = camera.camera.perspCam.nextCam;
}
if (GSErr != NoError && GSErr != APIERR_DELETED)
{
UE_AC_DebugF("FSyncDatabase::ScanCameras - ACAPI_Element_Get return %d", GSErr);
}
}
}
END_NAMESPACE_UE_AC