692 lines
18 KiB
C++
692 lines
18 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Synchronizer.h"
|
|
#include "MaterialsDatabase.h"
|
|
#include "Commander.h"
|
|
#ifdef DEBUG
|
|
#include "ISceneValidator.h"
|
|
#endif
|
|
#include "Utils/TimeStat.h"
|
|
#include "Utils/Error.h"
|
|
#include "Utils/CurrentOS.h"
|
|
|
|
#include "DatasmithDirectLink.h"
|
|
#include "DatasmithSceneExporter.h"
|
|
#include "DatasmithSceneXmlWriter.h"
|
|
|
|
#ifdef TicksPerSecond
|
|
#undef TicksPerSecond
|
|
#endif
|
|
|
|
#include "FileManager.h"
|
|
#include "Paths.h"
|
|
#include "Version.h"
|
|
|
|
BEGIN_NAMESPACE_UE_AC
|
|
|
|
// Do Direct Link snapshot update on a thread
|
|
class FThreadUpdateSnapshotRunner : public GS::Runnable
|
|
{
|
|
public:
|
|
// Constructor
|
|
FThreadUpdateSnapshotRunner(FSynchronizer* InSynchronizer)
|
|
: Synchronizer(InSynchronizer)
|
|
{
|
|
}
|
|
|
|
// The task code
|
|
void Run() override
|
|
{
|
|
TryFunctionCatchAndLog("FThreadUpdateSnapshotRunner::Run", [this]() -> GSErrCode {
|
|
#if PLATFORM_WINDOWS
|
|
SetThreadName("UpdateSceneRunner");
|
|
#else
|
|
pthread_setname_np("UpdateSceneRunner");
|
|
#endif
|
|
Synchronizer->DumpAndValidate();
|
|
Synchronizer->UpdateScene();
|
|
return NoError;
|
|
});
|
|
}
|
|
|
|
private:
|
|
// The synchronizer we update
|
|
FSynchronizer* Synchronizer;
|
|
};
|
|
|
|
// Constructor
|
|
FThreadUpdateSnapshot::FThreadUpdateSnapshot(FSynchronizer* InSynchronizer)
|
|
: RunnableTask(new FThreadUpdateSnapshotRunner(InSynchronizer))
|
|
, Thread(*this, GS::UniString("FThreadUpdateSnapshot"))
|
|
{
|
|
Thread.Start();
|
|
}
|
|
|
|
// Destructor
|
|
FThreadUpdateSnapshot::~FThreadUpdateSnapshot()
|
|
{
|
|
Thread.Join();
|
|
}
|
|
|
|
// Show progression while current snapshot is done
|
|
void FThreadUpdateSnapshot::Join(FProgression* IOProgression)
|
|
{
|
|
GS::UInt32 TimeOut = 10; // miliseconds
|
|
while (!Thread.Join(TimeOut))
|
|
{
|
|
if (IOProgression)
|
|
{
|
|
IOProgression->Update();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Class to process metadata as idle task (Only for Direct Link synchronization)
|
|
class FCountNeededMetadata : public FSyncData::FInterator
|
|
{
|
|
public:
|
|
GS::Int32 Count = 0;
|
|
|
|
FCountNeededMetadata(FSyncData* Root)
|
|
{
|
|
Start(Root);
|
|
ProcessAll();
|
|
}
|
|
|
|
// Call ProcessMetaData for the sync data
|
|
virtual EProcessControl Process(FSyncData* InCurrent) override
|
|
{
|
|
if (InCurrent == nullptr)
|
|
{
|
|
return kDone;
|
|
}
|
|
if (InCurrent->NeedTagsAndMetaDataUpdate())
|
|
{
|
|
++Count;
|
|
}
|
|
return kContinue;
|
|
}
|
|
};
|
|
|
|
#define UE_AC_FULL_TRACE 0
|
|
|
|
enum : GSType
|
|
{
|
|
DatasmithDynamicLink = 'DsDL'
|
|
}; // Can be called by another Add-on
|
|
|
|
// Add menu to the menu bar and also add an item to palette menu
|
|
GSErrCode FSynchronizer::Register()
|
|
{
|
|
return ACAPI_Register_SupportedService(DatasmithDynamicLink, 1L);
|
|
}
|
|
|
|
// Enable handlers of menu items
|
|
GSErrCode FSynchronizer::Initialize()
|
|
{
|
|
GSErrCode GSErr = ACAPI_Install_ModulCommandHandler(DatasmithDynamicLink, 1L, SyncCommandHandler);
|
|
if (GSErr != NoError)
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::Initialize - ACAPI_Install_ModulCommandHandler error=%s\n", GetErrorName(GSErr));
|
|
}
|
|
return GSErr;
|
|
}
|
|
|
|
// Intra add-ons command handler
|
|
#if AC_VERSION < 28
|
|
GSErrCode __ACENV_CALL FSynchronizer::SyncCommandHandler(GSHandle ParHdl, GSPtr /* ResultData */,
|
|
bool /* SilentMod */) noexcept
|
|
#else
|
|
GSErrCode FSynchronizer::SyncCommandHandler(GSHandle ParHdl, GSPtr /* ResultData */,
|
|
bool /* SilentMod */) noexcept
|
|
#endif
|
|
{
|
|
return TryFunctionCatchAndAlert("FSynchronizer::DoSyncCommand",
|
|
[ParHdl]() -> GSErrCode { return FSynchronizer::DoSyncCommand(ParHdl); });
|
|
}
|
|
|
|
static bool bPostSent = false;
|
|
|
|
// Process intra add-ons command
|
|
GSErrCode FSynchronizer::DoSyncCommand(GSHandle ParHdl)
|
|
{
|
|
GSErrCode GSErr = NoError;
|
|
|
|
if (ParHdl == nullptr)
|
|
{
|
|
return APIERR_GENERAL;
|
|
}
|
|
|
|
Int32 NbPars = 0;
|
|
GSErr = ACAPI_Goodies(APIAny_GetMDCLParameterNumID, ParHdl, &NbPars);
|
|
if (GSErr != NoError)
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::DoSyncCommand - APIAny_GetMDCLParameterNumID error %s\n", GetErrorName(GSErr));
|
|
return GSErr;
|
|
}
|
|
|
|
if (NbPars != 1)
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::DoSyncCommand - Invalid number of parameters %d\n", NbPars);
|
|
return APIERR_BADPARS;
|
|
}
|
|
|
|
API_MDCLParameter Param = {};
|
|
Param.index = 1;
|
|
GSErr = ACAPI_Goodies(APIAny_GetMDCLParameterID, ParHdl, &Param);
|
|
if (GSErr != NoError)
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::DoSyncCommand - APIAny_GetMDCLParameterID 1 error %s\n", GetErrorName(GSErr));
|
|
return GSErr;
|
|
}
|
|
#if AC_VERSION > 27
|
|
if (CHCompareCStrings(Param.name, "Reason", GS::CaseSensitive) != 0 || Param.type != MDCLPar_string)
|
|
#else
|
|
if (CHCompareCStrings(Param.name, "Reason", CS_CaseSensitive) != 0 || Param.type != MDCLPar_string)
|
|
#endif
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::DoSyncCommand - Invalid parameters (type=%d) %s\n", Param.type, Param.name);
|
|
return APIERR_BADPARS;
|
|
}
|
|
|
|
if (bPostSent == true)
|
|
{
|
|
bPostSent = false;
|
|
if (Is3DCurrenWindow() && (GetCurrent() == nullptr || !GetCurrent()->UpdateSceneInProgress()))
|
|
{
|
|
UE_AC_ReportF("Auto Sync for %s\n", Param.string_par);
|
|
FCommander::DoSnapshot();
|
|
}
|
|
else
|
|
{
|
|
PostDoSnapshot(Param.string_par);
|
|
}
|
|
}
|
|
|
|
return GSErr;
|
|
}
|
|
|
|
// Schedule a Auto Sync snapshot to be executed from the main thread event loop.
|
|
void FSynchronizer::PostDoSnapshot(const utf8_t* InReason)
|
|
{
|
|
if (bPostSent == false)
|
|
{
|
|
GSHandle ParHdl = nullptr;
|
|
GSErrCode GSErr = ACAPI_Goodies(APIAny_InitMDCLParameterListID, &ParHdl);
|
|
if (GSErr == NoError)
|
|
{
|
|
API_MDCLParameter Param;
|
|
Zap(&Param);
|
|
Param.name = "Reason";
|
|
Param.type = MDCLPar_string;
|
|
Param.string_par = InReason;
|
|
GSErr = ACAPI_Goodies(APIAny_AddMDCLParameterID, ParHdl, &Param);
|
|
if (GSErr == NoError)
|
|
{
|
|
API_ModulID mdid;
|
|
Zap(&mdid);
|
|
mdid.developerID = kEpicGamesDevId;
|
|
mdid.localID = kDatasmithExporterId;
|
|
GSErr = ACAPI_Command_CallFromEventLoop(&mdid, DatasmithDynamicLink, 1, ParHdl, false, nullptr);
|
|
if (GSErr == NoError)
|
|
{
|
|
ParHdl = nullptr;
|
|
bPostSent = true; // Only one post at a time
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::PostDoSnapshot - ACAPI_Command_CallFromEventLoop error %s\n",
|
|
GetErrorName(GSErr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::PostDoSnapshot - APIAny_AddMDCLParameterID error %s\n",
|
|
GetErrorName(GSErr));
|
|
}
|
|
|
|
if (ParHdl != nullptr)
|
|
{
|
|
GSErr = ACAPI_Goodies(APIAny_FreeMDCLParameterListID, &ParHdl);
|
|
if (GSErr != NoError)
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::PostDoSnapshot - APIAny_FreeMDCLParameterListID error %s\n",
|
|
GetErrorName(GSErr));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::PostDoSnapshot - APIAny_InitMDCLParameterListID error %s\n",
|
|
GetErrorName(GSErr));
|
|
}
|
|
}
|
|
}
|
|
|
|
static FSynchronizer* CurrentSynchonizer = nullptr;
|
|
|
|
// Return the synchronizer (create it if not already created)
|
|
FSynchronizer& FSynchronizer::Get()
|
|
{
|
|
if (CurrentSynchonizer == nullptr)
|
|
{
|
|
CurrentSynchonizer = new FSynchronizer();
|
|
}
|
|
|
|
return *CurrentSynchonizer;
|
|
}
|
|
|
|
// Return the current synchronizer if any
|
|
FSynchronizer* FSynchronizer::GetCurrent()
|
|
{
|
|
return CurrentSynchonizer;
|
|
}
|
|
|
|
// FreeData is called, so we must free all our stuff
|
|
void FSynchronizer::DeleteSingleton()
|
|
{
|
|
if (CurrentSynchonizer)
|
|
{
|
|
delete CurrentSynchonizer;
|
|
CurrentSynchonizer = nullptr;
|
|
}
|
|
}
|
|
|
|
// Constructor
|
|
FSynchronizer::FSynchronizer()
|
|
: DatasmithDirectLink(new FDatasmithDirectLink)
|
|
, ProcessMetadata(this)
|
|
{
|
|
}
|
|
|
|
// Destructor
|
|
FSynchronizer::~FSynchronizer()
|
|
{
|
|
Reset("Synchronizer deleted");
|
|
ThreadUpdateSnapshot.Reset();
|
|
DatasmithDirectLink.Reset();
|
|
}
|
|
|
|
// Return true if a snapshot update is in progress
|
|
bool FSynchronizer::UpdateSceneInProgress()
|
|
{
|
|
if (ThreadUpdateSnapshot.IsValid())
|
|
{
|
|
if (!ThreadUpdateSnapshot->IsFinished())
|
|
{
|
|
return true;
|
|
}
|
|
ThreadUpdateSnapshot.Reset();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Delete the database (Usualy because document has changed)
|
|
void FSynchronizer::Reset(const utf8_t* InReason)
|
|
{
|
|
if (FCommander::IsAutoSyncEnabled())
|
|
{
|
|
FCommander::ToggleAutoSync();
|
|
}
|
|
ProcessMetadata.Stop();
|
|
AttachObservers.Stop();
|
|
|
|
UE_AC_TraceF("FSynchronizer::Reset - %s\n", InReason);
|
|
SyncDatabase.Reset();
|
|
}
|
|
|
|
// Delete the database (Usualy because document has changed)
|
|
void FSynchronizer::ProjectOpen()
|
|
{
|
|
if (SyncDatabase.IsValid())
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::ProjectOpen - Previous project hasn't been closed before ???");
|
|
Reset("Project Open");
|
|
}
|
|
|
|
// Create a new synchronization database
|
|
GS::UniString ProjectPath;
|
|
GS::UniString ProjectName;
|
|
GetProjectPathAndName(&ProjectPath, &ProjectName);
|
|
SyncDatabase.Reset(new FSyncDatabase(GSStringToUE(ProjectPath), GSStringToUE(ProjectName),
|
|
GSStringToUE(FSyncDatabase::GetCachePath()), FSyncDatabase::GetCachePath()));
|
|
|
|
// Announce it to potential receivers
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION == 26
|
|
TSharedRef< IDatasmithScene > ToBuildWith_4_26(SyncDatabase->GetScene());
|
|
DatasmithDirectLink->InitializeForScene(ToBuildWith_4_26);
|
|
#else
|
|
DatasmithDirectLink->InitializeForScene(SyncDatabase->GetScene());
|
|
#endif
|
|
}
|
|
|
|
// Inform that current project has been save (maybe name changed)
|
|
void FSynchronizer::ProjectSave()
|
|
{
|
|
if (SyncDatabase.IsValid())
|
|
{
|
|
GS::UniString ProjectPath;
|
|
GetProjectPathAndName(&ProjectPath, nullptr);
|
|
|
|
FString SanitizedName(FDatasmithUtils::SanitizeObjectName(GSStringToUE(ProjectPath)));
|
|
if (FCString::Strcmp(*SanitizedName, SyncDatabase->GetScene()->GetName()) == 0)
|
|
{
|
|
// Name is the same
|
|
return;
|
|
}
|
|
|
|
UE_AC_TraceF("FSynchronizer::ProjectSave - Project saved under a new name");
|
|
Reset("Project Renamed"); // There's no way to change to rename DirecLink connection
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("FSynchronizer::ProjectSave - Project hasn't been open before ???");
|
|
}
|
|
|
|
ProjectOpen();
|
|
}
|
|
|
|
// Inform that the project has been closed
|
|
void FSynchronizer::ProjectClosed()
|
|
{
|
|
Reset("Project Closed");
|
|
}
|
|
|
|
// Do a snapshot of the model 3D data
|
|
void FSynchronizer::DoSnapshot(const ModelerAPI::Model& InModel)
|
|
{
|
|
// Setup our progression
|
|
bool OutUserCancelled = false;
|
|
int NbPhases = kSyncWaitPreviousSync - kCommonProjectInfos + 1;
|
|
#if defined(DEBUG)
|
|
++NbPhases;
|
|
#endif
|
|
FProgression Progression(kStrListProgression, kSyncTitle, NbPhases, FProgression::kSetFlags, &OutUserCancelled);
|
|
|
|
GS::UniString ExportPath = FSyncDatabase::GetCachePath();
|
|
|
|
// If we have a sync database validate it use the ExportPath
|
|
if (SyncDatabase.IsValid())
|
|
{
|
|
if (FCString::Strcmp(GSStringToUE(ExportPath), SyncDatabase->GetAssetsFolderPath()) != 0)
|
|
{
|
|
Reset("ExportPath changed");
|
|
}
|
|
}
|
|
|
|
// Insure we have a sync database and a snapshot scene
|
|
if (!SyncDatabase.IsValid())
|
|
{
|
|
GS::UniString ProjectPath;
|
|
GS::UniString ProjectName;
|
|
GetProjectPathAndName(&ProjectPath, &ProjectName);
|
|
|
|
SyncDatabase.Reset(new FSyncDatabase(GSStringToUE(ProjectPath), GSStringToUE(ProjectName),
|
|
GSStringToUE(ExportPath), ExportPath));
|
|
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION == 26
|
|
TSharedRef< IDatasmithScene > ToBuildWith_4_26(SyncDatabase->GetScene());
|
|
DatasmithDirectLink->InitializeForScene(ToBuildWith_4_26);
|
|
#else
|
|
DatasmithDirectLink->InitializeForScene(SyncDatabase->GetScene());
|
|
#endif
|
|
}
|
|
// Synchronisation context
|
|
FSyncContext SyncContext(true, InModel, *SyncDatabase, &Progression);
|
|
|
|
// If there a pending update
|
|
if (ThreadUpdateSnapshot.IsValid())
|
|
{
|
|
SyncContext.NewPhase(kSyncWaitPreviousSync);
|
|
ThreadUpdateSnapshot->Join(&Progression);
|
|
ThreadUpdateSnapshot.Reset();
|
|
if (OutUserCancelled)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
FTimeStat DoSnapshotStart;
|
|
|
|
ViewState = FViewState();
|
|
|
|
SyncDatabase->SetSceneInfo();
|
|
|
|
SyncDatabase->Synchronize(SyncContext);
|
|
|
|
SyncDatabase->GetMaterialsDatabase().UpdateModified(SyncContext);
|
|
|
|
FTimeStat DoSynchronizeEnd;
|
|
|
|
// Try to process meta data now, so if it take less than 10 seconds we can have only one sync
|
|
SyncContext.NewPhase(kCommonCollectMetaDatas, FCountNeededMetadata(&SyncDatabase->GetSceneSyncData()).Count);
|
|
ProcessMetadata.Start(&SyncDatabase->GetSceneSyncData());
|
|
double EndSyncData = FTimeStat::RealTimeClock() + 10; // seconds
|
|
while (FTimeStat::RealTimeClock() < EndSyncData &&
|
|
ProcessMetadata.ProcessUntil(FTimeStat::RealTimeClock() + 1.0 / 3.0) == FSyncData::FInterator::kContinue)
|
|
{
|
|
// Update progression
|
|
SyncContext.NewCurrentValue(ProcessMetadata.GetProcessedCount());
|
|
}
|
|
ProcessMetadata.CleardMetadataUpdated();
|
|
FTimeStat DoMetadataEnd;
|
|
|
|
#if DIRECTLINK_THREAD_UPDATE
|
|
UE_AC_Assert(!ThreadUpdateSnapshot.IsValid());
|
|
ThreadUpdateSnapshot.Reset(new FThreadUpdateSnapshot(this));
|
|
#else
|
|
SyncContext.NewPhase(kDebugSaveScene);
|
|
DumpAndValidate();
|
|
SyncContext.NewPhase(kSyncSnapshot);
|
|
UpdateScene();
|
|
#endif
|
|
|
|
SyncContext.Stats.Print();
|
|
FTimeStat DoSnapshotEnd;
|
|
DoSynchronizeEnd.PrintDiff("Synchronization", DoSnapshotStart);
|
|
DoMetadataEnd.PrintDiff("Metadata", DoSynchronizeEnd);
|
|
DoSnapshotEnd.PrintDiff("Total DoSnapshot", DoSnapshotStart);
|
|
|
|
AttachObservers.Start(&SyncDatabase->GetSceneSyncData());
|
|
SyncDatabase->GetMeshIndexor().SaveToFile();
|
|
}
|
|
|
|
// Dump updated scene to a file
|
|
void FSynchronizer::DumpAndValidate()
|
|
{
|
|
#ifdef DEBUG
|
|
if (!FCommander::IsAutoSyncEnabled()) // In Auto Sync mode we don't do scene dump or validation
|
|
{
|
|
FTimeStat DumpAndValidateStart;
|
|
DumpScene(SyncDatabase->GetScene());
|
|
TSharedRef< Validator::ISceneValidator > Validator = Validator::ISceneValidator::CreateForScene(SyncDatabase->GetScene());
|
|
Validator->CheckElementsName();
|
|
Validator->CheckDependances();
|
|
Validator->CheckTexturesFiles();
|
|
Validator->CheckMeshFiles();
|
|
FString Reports = Validator->GetReports(Validator::ISceneValidator::kVerbose);
|
|
if (!Reports.IsEmpty())
|
|
{
|
|
UE_AC_TraceF("%s", TCHAR_TO_UTF8(*Reports));
|
|
}
|
|
FTimeStat DumpAndValidateEnd;
|
|
DumpAndValidateEnd.PrintDiff("FSynchronizer::DumpAndValidate", DumpAndValidateStart);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Update Direct Link snapshot
|
|
void FSynchronizer::UpdateScene()
|
|
{
|
|
FTimeStat UpdateSceneStart;
|
|
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION == 26
|
|
TSharedRef< IDatasmithScene > ToBuildWith_4_26(SyncDatabase->GetScene());
|
|
DatasmithDirectLink->UpdateScene(ToBuildWith_4_26);
|
|
#else
|
|
DatasmithDirectLink->UpdateScene(SyncDatabase->GetScene());
|
|
#endif
|
|
FTimeStat UpdateSceneEnd;
|
|
UpdateSceneEnd.PrintDiff("DirectLink UpdateScene", UpdateSceneStart);
|
|
}
|
|
|
|
// Process idle (To implement AutoSync)
|
|
void FSynchronizer::DoIdle(int* IOCount)
|
|
{
|
|
// If we wait for a snapshoot to be processed
|
|
if (bPostSent)
|
|
{
|
|
// We do nothing until we have processed the pending request
|
|
return;
|
|
}
|
|
|
|
// If we need to schedule an Auto Sync
|
|
if (FCommander::IsAutoSyncEnabled() && NeedAutoSyncUpdate())
|
|
{
|
|
PostDoSnapshot("View or material modified");
|
|
return;
|
|
}
|
|
|
|
// Process meta data in priority and attach observers after
|
|
if (ProcessMetadata.NeedProcess())
|
|
{
|
|
// While matadata processing isn't finish
|
|
if (ProcessMetadata.ProcessUntil(FTimeStat::RealTimeClock() + 1.0 / 3.0) == FSyncData::FInterator::kContinue)
|
|
{
|
|
*IOCount = 2;
|
|
return;
|
|
}
|
|
// If we must have meta data to sync ?
|
|
if (ProcessMetadata.HasMetadataUpdated())
|
|
{
|
|
UE_AC_ReportF("Metadata update completed in %.2lgs\n", ProcessMetadata.GetProcessedTime());
|
|
PostDoSnapshot("Update MetaData");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we need to schedule an Auto Sync
|
|
if (FCommander::IsAutoSyncEnabled() &&
|
|
AttachObservers.ProcessAttachUntil(FTimeStat::RealTimeClock() + 1.0 / 3.0))
|
|
{
|
|
if (FCommander::IsAutoSyncEnabled())
|
|
{
|
|
PostDoSnapshot("Process detect modification");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we need to process more
|
|
if (FCommander::IsAutoSyncEnabled() && AttachObservers.NeedProcess())
|
|
{
|
|
*IOCount = 2;
|
|
}
|
|
}
|
|
|
|
// Auto Sync related: If view changed shedule an update
|
|
bool FSynchronizer::NeedAutoSyncUpdate() const
|
|
{
|
|
FViewState CurrentViewState;
|
|
if (!(ViewState == CurrentViewState))
|
|
{
|
|
return true;
|
|
};
|
|
if (SyncDatabase.IsValid() && SyncDatabase->GetMaterialsDatabase().CheckModify())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return project's file info (if not a unsaved new project)
|
|
void FSynchronizer::GetProjectPathAndName(GS::UniString* OutPath, GS::UniString* OutName)
|
|
{
|
|
API_ProjectInfo ProjectInfo;
|
|
#if AC_VERSION < 26
|
|
Zap(&ProjectInfo);
|
|
#endif
|
|
GSErrCode GSErr = ACAPI_Environment(APIEnv_ProjectID, &ProjectInfo);
|
|
if (GSErr == NoError)
|
|
{
|
|
if (ProjectInfo.location != nullptr)
|
|
{
|
|
if (OutPath != nullptr)
|
|
{
|
|
ProjectInfo.location->ToPath(OutPath);
|
|
}
|
|
if (OutName != nullptr)
|
|
{
|
|
IO::Name ProjectName;
|
|
ProjectInfo.location->GetLastLocalName(&ProjectName);
|
|
ProjectName.DeleteExtension();
|
|
*OutName = ProjectName.ToString();
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Maybe ArchiCAD is running as demo
|
|
UE_AC_DebugF("CIdentity::GetFromProjectInfo - No project locations\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("CIdentity::GetFromProjectInfo - Error(%d) when accessing project info\n", GSErr);
|
|
}
|
|
|
|
if (OutPath != nullptr)
|
|
{
|
|
*OutPath = "Nameless";
|
|
}
|
|
|
|
if (OutName != nullptr)
|
|
{
|
|
*OutName = "Nameless";
|
|
}
|
|
}
|
|
|
|
// Dump the scene to a file
|
|
void FSynchronizer::DumpScene(const TSharedRef< IDatasmithScene >& InScene)
|
|
{
|
|
static bool bDoDump = false;
|
|
if (!bDoDump) // To active dump with recompiling, set sDoDump to true with the debugger
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Define a directory same name as scene.
|
|
FString sceneName(InScene->GetName());
|
|
if (sceneName.IsEmpty())
|
|
{
|
|
sceneName = TEXT("Unnamed");
|
|
}
|
|
FString FolderPath(FPaths::Combine(GSStringToUE(GetAddonDataDirectory()), *(FString("Dumps ") + sceneName)));
|
|
|
|
// If we change scene, we delete and recreate the folder
|
|
static int NbDumps = 0;
|
|
static FString PreviousFolderPath;
|
|
if (!FolderPath.Equals(PreviousFolderPath))
|
|
{
|
|
NbDumps = 0;
|
|
PreviousFolderPath = FolderPath;
|
|
IFileManager::Get().DeleteDirectory(*FolderPath, false, true);
|
|
IFileManager::Get().MakeDirectory(*FolderPath);
|
|
}
|
|
|
|
// Create dump file (starting from 0)
|
|
FString ArchiveName = FPaths::Combine(*FolderPath, *FString::Printf(TEXT("Dump %d.xml"), NbDumps++));
|
|
UE_AC_TraceF("Dump scene ---> %s\n", TCHAR_TO_UTF8(*ArchiveName));
|
|
TUniquePtr< FArchive > archive(IFileManager::Get().CreateFileWriter(*ArchiveName));
|
|
if (archive.IsValid())
|
|
{
|
|
FDatasmithSceneXmlWriter().Serialize(InScene, *archive);
|
|
}
|
|
else
|
|
{
|
|
UE_AC_DebugF("Dump scene Error can create archive file %s\n", TCHAR_TO_UTF8(*ArchiveName));
|
|
}
|
|
}
|
|
|
|
END_NAMESPACE_UE_AC
|