12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "CryEditDoc.h"
- // Qt
- #include <QDateTime>
- #include <QDialogButtonBox>
- // AzCore
- #include <AzCore/Component/TransformBus.h>
- #include <AzCore/Asset/AssetManager.h>
- #include <AzCore/Interface/Interface.h>
- #include <AzCore/Utils/Utils.h>
- #include <MathConversion.h>
- // AzFramework
- #include <AzFramework/Archive/IArchive.h>
- #include <AzFramework/API/ApplicationAPI.h>
- // AzToolsFramework
- #include <AzToolsFramework/Slice/SliceUtilities.h>
- #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
- #include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
- #include <AzToolsFramework/API/EditorLevelNotificationBus.h>
- // Editor
- #include "Settings.h"
- #include "PluginManager.h"
- #include "ViewManager.h"
- #include "DisplaySettings.h"
- #include "GameEngine.h"
- #include "CryEdit.h"
- #include "ActionManager.h"
- #include "Include/IObjectManager.h"
- #include "ErrorReportDialog.h"
- #include "SurfaceTypeValidator.h"
- #include "Util/AutoLogTime.h"
- #include "CheckOutDialog.h"
- #include "GameExporter.h"
- #include "MainWindow.h"
- #include "LevelFileDialog.h"
- #include "StatObjBus.h"
- #include "Undo/Undo.h"
- #include <Atom/RPI.Public/ViewportContext.h>
- #include <Atom/RPI.Public/ViewportContextBus.h>
- // LmbrCentral
- #include <LmbrCentral/Audio/AudioSystemComponentBus.h>
- #include <LmbrCentral/Rendering/EditorLightComponentBus.h> // for LmbrCentral::EditorLightComponentRequestBus
- //#define PROFILE_LOADING_WITH_VTUNE
- // profilers api.
- //#include "pure.h"
- #ifdef PROFILE_LOADING_WITH_VTUNE
- #include "C:\Program Files\Intel\Vtune\Analyzer\Include\VTuneApi.h"
- #pragma comment(lib,"C:\\Program Files\\Intel\\Vtune\\Analyzer\\Lib\\VTuneApi.lib")
- #endif
- static const char* kAutoBackupFolder = "_autobackup";
- static const char* kHoldFolder = "$tmp_hold"; // conform to the ignored file types $tmp[0-9]*_ regex
- static const char* kSaveBackupFolder = "_savebackup";
- static const char* kResizeTempFolder = "$tmp_resize"; // conform to the ignored file types $tmp[0-9]*_ regex
- static const char* kBackupOrTempFolders[] =
- {
- kAutoBackupFolder,
- kHoldFolder,
- kSaveBackupFolder,
- kResizeTempFolder,
- "_hold", // legacy name
- "_tmpresize", // legacy name
- };
- static const char* kLevelPathForSliceEditing = "EngineAssets/LevelForSliceEditing/LevelForSliceEditing.ly";
- static bool IsSliceFile(const QString& filePath)
- {
- return filePath.endsWith(AzToolsFramework::SliceUtilities::GetSliceFileExtension().c_str(), Qt::CaseInsensitive);
- }
- namespace Internal
- {
- bool SaveLevel()
- {
- if (!GetIEditor()->GetDocument()->DoSave(GetIEditor()->GetDocument()->GetActivePathName(), true))
- {
- return false;
- }
- return true;
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // CCryEditDoc construction/destruction
- CCryEditDoc::CCryEditDoc()
- : doc_validate_surface_types(nullptr)
- , m_modifiedModuleFlags(eModifiedNothing)
- {
- ////////////////////////////////////////////////////////////////////////
- // Set member variables to initial values
- ////////////////////////////////////////////////////////////////////////
- m_fogTemplate = GetIEditor()->FindTemplate("Fog");
- m_environmentTemplate = GetIEditor()->FindTemplate("Environment");
- if (m_environmentTemplate)
- {
- m_fogTemplate = m_environmentTemplate->findChild("Fog");
- }
- else
- {
- m_environmentTemplate = XmlHelpers::CreateXmlNode("Environment");
- }
- GetIEditor()->SetDocument(this);
- CLogFile::WriteLine("Document created");
- RegisterConsoleVariables();
- MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_FILE_SAVE_AS, this, &CCryEditDoc::OnFileSaveAs);
- bool isPrefabSystemEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- if (isPrefabSystemEnabled)
- {
- m_prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
- AZ_Assert(m_prefabSystemComponentInterface, "PrefabSystemComponentInterface is not found.");
- m_prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
- AZ_Assert(m_prefabEditorEntityOwnershipInterface, "PrefabEditorEntityOwnershipInterface is not found.");
- m_prefabLoaderInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
- AZ_Assert(m_prefabLoaderInterface, "PrefabLoaderInterface is not found.");
- m_prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
- AZ_Assert(m_prefabIntegrationInterface, "PrefabIntegrationInterface is not found.");
- }
- }
- CCryEditDoc::~CCryEditDoc()
- {
- GetIEditor()->SetDocument(nullptr);
- CLogFile::WriteLine("Document destroyed");
- AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
- }
- bool CCryEditDoc::IsModified() const
- {
- return m_modified;
- }
- void CCryEditDoc::SetModifiedFlag(bool modified)
- {
- m_modified = modified;
- }
- QString CCryEditDoc::GetLevelPathName() const
- {
- return m_pathName;
- }
- void CCryEditDoc::SetPathName(const QString& pathName)
- {
- if (IsSliceFile(pathName))
- {
- m_pathName = kLevelPathForSliceEditing;
- m_slicePathName = pathName;
- }
- else
- {
- m_pathName = pathName;
- m_slicePathName.clear();
- }
- SetTitle(pathName.isEmpty() ? tr("Untitled") : PathUtil::GetFileName(pathName.toUtf8().data()).c_str());
- }
- QString CCryEditDoc::GetSlicePathName() const
- {
- return m_slicePathName;
- }
- CCryEditDoc::DocumentEditingMode CCryEditDoc::GetEditMode() const
- {
- return m_slicePathName.isEmpty() ? CCryEditDoc::DocumentEditingMode::LevelEdit : CCryEditDoc::DocumentEditingMode::SliceEdit;
- }
- QString CCryEditDoc::GetActivePathName() const
- {
- return GetEditMode() == CCryEditDoc::DocumentEditingMode::SliceEdit ? GetSlicePathName() : GetLevelPathName();
- }
- QString CCryEditDoc::GetTitle() const
- {
- return m_title;
- }
- void CCryEditDoc::SetTitle(const QString& title)
- {
- m_title = title;
- }
- bool CCryEditDoc::IsBackupOrTempLevelSubdirectory(const QString& folderName)
- {
- for (const char* backupOrTempFolderName : kBackupOrTempFolders)
- {
- if (!folderName.compare(backupOrTempFolderName, Qt::CaseInsensitive))
- {
- return true;
- }
- }
- return false;
- }
- bool CCryEditDoc::DoSave(const QString& pathName, bool replace)
- {
- if (!OnSaveDocument(pathName.isEmpty() ? GetActivePathName() : pathName))
- {
- return false;
- }
- if (replace)
- {
- SetPathName(pathName);
- }
- return true;
- }
- bool CCryEditDoc::Save()
- {
- return OnSaveDocument(GetActivePathName());
- }
- void CCryEditDoc::DeleteContents()
- {
- m_hasErrors = false;
- SetDocumentReady(false);
- GetIEditor()->Notify(eNotify_OnCloseScene);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorCloseScene);
- EBUS_EVENT(AzToolsFramework::EditorEntityContextRequestBus, ResetEditorContext);
- // [LY-90904] move this to the EditorVegetationManager component
- InstanceStatObjEventBus::Broadcast(&InstanceStatObjEventBus::Events::ReleaseData);
- //////////////////////////////////////////////////////////////////////////
- // Clear all undo info.
- //////////////////////////////////////////////////////////////////////////
- GetIEditor()->FlushUndo();
- // Notify listeners.
- for (IDocListener* listener : m_listeners)
- {
- listener->OnCloseDocument();
- }
- GetIEditor()->ResetViews();
- // Delete all objects from Object Manager.
- GetIEditor()->GetObjectManager()->DeleteAllObjects();
- // Load scripts data
- SetModifiedFlag(false);
- SetModifiedModules(eModifiedNothing);
- // Clear error reports if open.
- CErrorReportDialog::Clear();
- // Unload level specific audio binary data.
- LmbrCentral::AudioSystemComponentRequestBus::Broadcast(&LmbrCentral::AudioSystemComponentRequestBus::Events::LevelUnloadAudio);
- GetIEditor()->Notify(eNotify_OnSceneClosed);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorSceneClosed);
- }
- void CCryEditDoc::Save(CXmlArchive& xmlAr)
- {
- TDocMultiArchive arrXmlAr;
- FillXmlArArray(arrXmlAr, &xmlAr);
- Save(arrXmlAr);
- }
- void CCryEditDoc::Save(TDocMultiArchive& arrXmlAr)
- {
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- if (!isPrefabEnabled)
- {
- CAutoDocNotReady autoDocNotReady;
- if (arrXmlAr[DMAS_GENERAL] != nullptr)
- {
- (*arrXmlAr[DMAS_GENERAL]).root = XmlHelpers::CreateXmlNode("Level");
- (*arrXmlAr[DMAS_GENERAL]).root->setAttr("WaterColor", m_waterColor);
- char version[50];
- GetIEditor()->GetFileVersion().ToString(version, AZ_ARRAY_SIZE(version));
- (*arrXmlAr[DMAS_GENERAL]).root->setAttr("SandboxVersion", version);
- SerializeViewSettings((*arrXmlAr[DMAS_GENERAL]));
- // Fog settings ///////////////////////////////////////////////////////
- SerializeFogSettings((*arrXmlAr[DMAS_GENERAL]));
- SerializeNameSelection((*arrXmlAr[DMAS_GENERAL]));
- }
- }
- AfterSave();
- }
- void CCryEditDoc::Load(CXmlArchive& xmlAr, const QString& szFilename)
- {
- TDocMultiArchive arrXmlAr;
- FillXmlArArray(arrXmlAr, &xmlAr);
- CCryEditDoc::Load(arrXmlAr, szFilename);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
- {
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- m_hasErrors = false;
- // Register a unique load event
- QString fileName = Path::GetFileName(szFilename);
- QString levelHash;
- if (!isPrefabEnabled)
- {
- levelHash = GetIEditor()->GetSettingsManager()->GenerateContentHash(arrXmlAr[DMAS_GENERAL]->root, fileName);
- }
- else
- {
- levelHash = szFilename;
- }
- SEventLog loadEvent("Level_" + Path::GetFileName(fileName), "", levelHash);
- // Register this level and its content hash as version
- GetIEditor()->GetSettingsManager()->AddToolVersion(fileName, levelHash);
- GetIEditor()->GetSettingsManager()->RegisterEvent(loadEvent);
- CAutoDocNotReady autoDocNotReady;
- HEAP_CHECK
- CLogFile::FormatLine("Loading from %s...", szFilename.toUtf8().data());
- QString szLevelPath = Path::GetPath(szFilename);
- {
- // Set game g_levelname variable to the name of current level.
- QString szGameLevelName = Path::GetFileName(szFilename);
- ICVar* sv_map = gEnv->pConsole->GetCVar("sv_map");
- if (sv_map)
- {
- sv_map->Set(szGameLevelName.toUtf8().data());
- }
- }
- // Starts recording the opening of files using the level category
- if (auto archive = AZ::Interface<AZ::IO::IArchive>::Get(); archive && archive->GetRecordFileOpenList() == AZ::IO::IArchive::RFOM_EngineStartup)
- {
- archive->RecordFileOpen(AZ::IO::IArchive::RFOM_Level);
- }
- GetIEditor()->Notify(eNotify_OnBeginSceneOpen);
- GetIEditor()->GetMovieSystem()->RemoveAllSequences();
- {
- // Start recording errors
- const ICVar* pShowErrorDialogOnLoad = gEnv->pConsole->GetCVar("ed_showErrorDialogOnLoad");
- CErrorsRecorder errorsRecorder(pShowErrorDialogOnLoad && (pShowErrorDialogOnLoad->GetIVal() != 0));
- bool usePrefabSystemForLevels = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
- if (!usePrefabSystemForLevels)
- {
- AZStd::string levelPakPath;
- if (AzFramework::StringFunc::Path::ConstructFull(szLevelPath.toUtf8().data(), "level", "pak", levelPakPath, true))
- {
- // Check whether level.pak is present
- if (!gEnv->pFileIO->Exists(levelPakPath.c_str()))
- {
- CryWarning(
- VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING,
- "level.pak is missing. This will cause other errors. To fix this, re-export the level.");
- }
- }
- }
- int t0 = GetTickCount();
- #ifdef PROFILE_LOADING_WITH_VTUNE
- VTResume();
- #endif
- // Load level-specific audio data.
- AZStd::string levelFileName{ fileName.toUtf8().constData() };
- AZStd::to_lower(levelFileName.begin(), levelFileName.end());
- LmbrCentral::AudioSystemComponentRequestBus::Broadcast(
- &LmbrCentral::AudioSystemComponentRequestBus::Events::LevelLoadAudio, AZStd::string_view{ levelFileName });
- {
- CAutoLogTime logtime("Game Engine level load");
- GetIEditor()->GetGameEngine()->LoadLevel(true, true);
- }
- if (!isPrefabEnabled)
- {
- //////////////////////////////////////////////////////////////////////////
- // Load water color.
- //////////////////////////////////////////////////////////////////////////
- (*arrXmlAr[DMAS_GENERAL]).root->getAttr("WaterColor", m_waterColor);
- //////////////////////////////////////////////////////////////////////////
- // Load View Settings
- //////////////////////////////////////////////////////////////////////////
- SerializeViewSettings((*arrXmlAr[DMAS_GENERAL]));
- //////////////////////////////////////////////////////////////////////////
- // Fog settings
- //////////////////////////////////////////////////////////////////////////
- SerializeFogSettings((*arrXmlAr[DMAS_GENERAL]));
- }
- if (!isPrefabEnabled)
- {
- // Serialize Shader Cache.
- CAutoLogTime logtime("Load Level Shader Cache");
- }
- {
- // support old version of sequences
- IMovieSystem* pMs = GetIEditor()->GetMovieSystem();
- if (pMs)
- {
- for (int k = 0; k < pMs->GetNumSequences(); ++k)
- {
- IAnimSequence* seq = pMs->GetSequence(k);
- QString fullname = seq->GetName();
- CBaseObject* pObj = GetIEditor()->GetObjectManager()->FindObject(fullname);
- if (!pObj)
- {
- pObj = GetIEditor()->GetObjectManager()->NewObject("SequenceObject", nullptr, fullname);
- }
- }
- }
- }
- if (!isPrefabEnabled)
- {
- // Name Selection groups
- SerializeNameSelection((*arrXmlAr[DMAS_GENERAL]));
- }
- {
- CAutoLogTime logtime("Post Load");
- // Notify listeners.
- for (IDocListener* listener : m_listeners)
- {
- listener->OnLoadDocument();
- }
- }
- CSurfaceTypeValidator().Validate();
- #ifdef PROFILE_LOADING_WITH_VTUNE
- VTPause();
- #endif
- LogLoadTime(GetTickCount() - t0);
- // Loaded with success, remove event from log file
- GetIEditor()->GetSettingsManager()->UnregisterEvent(loadEvent);
- }
- GetIEditor()->Notify(eNotify_OnEndSceneOpen);
- }
- void CCryEditDoc::AfterSave()
- {
- // When saving level also save editor settings
- // Save settings
- gSettings.Save();
- GetIEditor()->GetDisplaySettings()->SaveRegistry();
- MainWindow::instance()->SaveConfig();
- }
- void CCryEditDoc::SerializeViewSettings(CXmlArchive& xmlAr)
- {
- // Load or restore the viewer settings from an XML
- if (xmlAr.bLoading)
- {
- bool useOldViewFormat = false;
- // Loading
- CLogFile::WriteLine("Loading View settings...");
- int numberOfGameViewports = GetIEditor()->GetViewManager()->GetNumberOfGameViewports();
- for (int i = 0; i < numberOfGameViewports; i++)
- {
- XmlNodeRef view;
- Vec3 vp(0.0f, 0.0f, 256.0f);
- Ang3 va(ZERO);
- auto viewName = QString("View%1").arg(i);
- view = xmlAr.root->findChild(viewName.toUtf8().constData());
- if (!view)
- {
- view = xmlAr.root->findChild("View");
- if (view)
- {
- useOldViewFormat = true;
- }
- }
- if (view)
- {
- auto viewerPosName = QString("ViewerPos%1").arg(useOldViewFormat ? "" : QString::number(i));
- view->getAttr(viewerPosName.toUtf8().constData(), vp);
- auto viewerAnglesName = QString("ViewerAngles%1").arg(useOldViewFormat ? "" : QString::number(i));
- view->getAttr(viewerAnglesName.toUtf8().constData(), va);
- }
- Matrix34 tm = Matrix34::CreateRotationXYZ(va);
- tm.SetTranslation(vp);
- auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
- if (auto viewportContext = viewportContextManager->GetViewportContextById(i))
- {
- viewportContext->SetCameraTransform(LYTransformToAZTransform(tm));
- }
- }
- }
- else
- {
- // Storing
- CLogFile::WriteLine("Storing View settings...");
- int numberOfGameViewports = GetIEditor()->GetViewManager()->GetNumberOfGameViewports();
- for (int i = 0; i < numberOfGameViewports; i++)
- {
- auto viewName = QString("View%1").arg(i);
- XmlNodeRef view = xmlAr.root->newChild(viewName.toUtf8().constData());
- CViewport* pVP = GetIEditor()->GetViewManager()->GetView(i);
- if (pVP)
- {
- Vec3 pos = pVP->GetViewTM().GetTranslation();
- Ang3 angles = Ang3::GetAnglesXYZ(Matrix33(pVP->GetViewTM()));
- auto viewerPosName = QString("ViewerPos%1").arg(i);
- view->setAttr(viewerPosName.toUtf8().constData(), pos);
- auto viewerAnglesName = QString("ViewerAngles%1").arg(i);
- view->setAttr(viewerAnglesName.toUtf8().constData(), angles);
- }
- }
- }
- }
- void CCryEditDoc::SerializeFogSettings(CXmlArchive& xmlAr)
- {
- if (xmlAr.bLoading)
- {
- CLogFile::WriteLine("Loading Fog settings...");
- XmlNodeRef fog = xmlAr.root->findChild("Fog");
- if (!fog)
- {
- return;
- }
- if (m_fogTemplate)
- {
- CXmlTemplate::GetValues(m_fogTemplate, fog);
- }
- }
- else
- {
- CLogFile::WriteLine("Storing Fog settings...");
- XmlNodeRef fog = xmlAr.root->newChild("Fog");
- if (m_fogTemplate)
- {
- CXmlTemplate::SetValues(m_fogTemplate, fog);
- }
- }
- }
- void CCryEditDoc::SerializeNameSelection(CXmlArchive& xmlAr)
- {
- IObjectManager* pObjManager = GetIEditor()->GetObjectManager();
- if (pObjManager)
- {
- pObjManager->SerializeNameSelection(xmlAr.root, xmlAr.bLoading);
- }
- }
- void CCryEditDoc::SetModifiedModules(EModifiedModule eModifiedModule, bool boSet)
- {
- if (!boSet)
- {
- m_modifiedModuleFlags &= ~eModifiedModule;
- }
- else
- {
- if (eModifiedModule == eModifiedNothing)
- {
- m_modifiedModuleFlags = eModifiedNothing;
- }
- else
- {
- m_modifiedModuleFlags |= eModifiedModule;
- }
- }
- }
- int CCryEditDoc::GetModifiedModule()
- {
- return m_modifiedModuleFlags;
- }
- bool CCryEditDoc::CanCloseFrame()
- {
- // Ask the base class to ask for saving, which also includes the save
- // status of the plugins. Additionaly we query if all the plugins can exit
- // now. A reason for a failure might be that one of the plugins isn't
- // currently processing data or has other unsaved information which
- // are not serialized in the project file
- if (!SaveModified())
- {
- return false;
- }
- if (!GetIEditor()->GetPluginManager()->CanAllPluginsExitNow())
- {
- return false;
- }
- // If there is an export in process, exiting will corrupt it
- if (CGameExporter::GetCurrentExporter() != nullptr)
- {
- return false;
- }
- return true;
- }
- bool CCryEditDoc::SaveModified()
- {
- if (!IsModified())
- {
- return true;
- }
- bool usePrefabSystemForLevels = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
- if (!usePrefabSystemForLevels)
- {
- QMessageBox saveModifiedMessageBox(AzToolsFramework::GetActiveWindow());
- saveModifiedMessageBox.setText(QString("Save changes to %1?").arg(GetTitle()));
- saveModifiedMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
- saveModifiedMessageBox.setIcon(QMessageBox::Icon::Question);
- auto button = QMessageBox::question(
- AzToolsFramework::GetActiveWindow(), QString(), tr("Save changes to %1?").arg(GetTitle()),
- QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
- switch (button)
- {
- case QMessageBox::Cancel:
- return false;
- case QMessageBox::Yes:
- return DoFileSave();
- case QMessageBox::No:
- SetModifiedFlag(false);
- return true;
- }
- Q_UNREACHABLE();
- }
- else
- {
- AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = m_prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
- if (!m_prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId))
- {
- return true;
- }
- int prefabSaveSelection = m_prefabIntegrationInterface->ExecuteClosePrefabDialog(rootPrefabTemplateId);
- // In order to get the accept and reject codes of QDialog and QDialogButtonBox aligned, we do (1-prefabSaveSelection) here.
- // For example, QDialog::Rejected(0) is emitted when dialog is closed. But the int value corresponds to
- // QDialogButtonBox::AcceptRole(0).
- switch (1 - prefabSaveSelection)
- {
- case QDialogButtonBox::AcceptRole:
- return true;
- case QDialogButtonBox::RejectRole:
- return false;
- case QDialogButtonBox::InvalidRole:
- SetModifiedFlag(false);
- return true;
- }
- Q_UNREACHABLE();
- }
- }
- void CCryEditDoc::OnFileSaveAs()
- {
- CLevelFileDialog levelFileDialog(false);
- levelFileDialog.show();
- levelFileDialog.adjustSize();
- if (levelFileDialog.exec() == QDialog::Accepted)
- {
- if (OnSaveDocument(levelFileDialog.GetFileName()))
- {
- CCryEditApp::instance()->AddToRecentFileList(levelFileDialog.GetFileName());
- bool usePrefabSystemForLevels = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
- if (usePrefabSystemForLevels)
- {
- AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId =
- m_prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
- SetModifiedFlag(m_prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId));
- }
- }
- }
- }
- bool CCryEditDoc::OnOpenDocument(const QString& lpszPathName)
- {
- TOpenDocContext context;
- if (!BeforeOpenDocument(lpszPathName, context))
- {
- return false;
- }
- return DoOpenDocument(context);
- }
- bool CCryEditDoc::BeforeOpenDocument(const QString& lpszPathName, TOpenDocContext& context)
- {
- CTimeValue loading_start_time = gEnv->pTimer->GetAsyncTime();
- bool usePrefabSystemForLevels = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
- if (!usePrefabSystemForLevels)
- {
- // ensure we close any open packs
- if (!GetIEditor()->GetLevelFolder().isEmpty())
- {
- GetIEditor()->GetSystem()->GetIPak()->ClosePack((GetIEditor()->GetLevelFolder() + "\\level.pak").toUtf8().data());
- }
- }
- // restore directory to root.
- QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
- QString absolutePath = lpszPathName;
- QFileInfo fileInfo(absolutePath);
- QString friendlyDisplayName = Path::GetRelativePath(absolutePath, true);
- CLogFile::FormatLine("Opening level %s", friendlyDisplayName.toUtf8().data());
- // normalize the file path.
- absolutePath = Path::ToUnixPath(QFileInfo(absolutePath).canonicalFilePath());
- context.loading_start_time = loading_start_time;
- if (IsSliceFile(absolutePath))
- {
- context.absoluteLevelPath = Path::GamePathToFullPath(kLevelPathForSliceEditing);
- context.absoluteSlicePath = absolutePath;
- }
- else
- {
- context.absoluteLevelPath = absolutePath;
- context.absoluteSlicePath = "";
- }
- return true;
- }
- bool CCryEditDoc::DoOpenDocument(TOpenDocContext& context)
- {
- CTimeValue& loading_start_time = context.loading_start_time;
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- // normalize the path so that its the same in all following calls:
- QString levelFilePath = QFileInfo(context.absoluteLevelPath).absoluteFilePath();
- context.absoluteLevelPath = levelFilePath;
- m_bLoadFailed = false;
- auto pIPak = GetIEditor()->GetSystem()->GetIPak();
- QString levelFolderAbsolutePath = QFileInfo(context.absoluteLevelPath).absolutePath();
- if (!isPrefabEnabled)
- {
- // if the level pack exists, open that, too:
- QString levelPackFileAbsolutePath = QDir(levelFolderAbsolutePath).absoluteFilePath("level.pak");
- // we mount the pack (level.pak) using the folder its sitting in as the mountpoint (first parameter)
- pIPak->OpenPack(levelFolderAbsolutePath.toUtf8().constData(), levelPackFileAbsolutePath.toUtf8().constData());
- }
- TDocMultiArchive arrXmlAr = {};
- if (!isPrefabEnabled)
- {
- if (!LoadXmlArchiveArray(arrXmlAr, levelFilePath, levelFolderAbsolutePath))
- {
- m_bLoadFailed = true;
- return false;
- }
- }
- if (!LoadLevel(arrXmlAr, context.absoluteLevelPath))
- {
- m_bLoadFailed = true;
- }
- ReleaseXmlArchiveArray(arrXmlAr);
- if (m_bLoadFailed)
- {
- return false;
- }
- // Load AZ entities for the editor.
- if (context.absoluteSlicePath.isEmpty())
- {
- if (!LoadEntitiesFromLevel(context.absoluteLevelPath))
- {
- m_bLoadFailed = true;
- }
- }
- else
- {
- if (!LoadEntitiesFromSlice(context.absoluteSlicePath))
- {
- m_bLoadFailed = true;
- }
- }
- if (m_bLoadFailed)
- {
- return false;
- }
- StartStreamingLoad();
- CTimeValue loading_end_time = gEnv->pTimer->GetAsyncTime();
- CLogFile::FormatLine("-----------------------------------------------------------");
- CLogFile::FormatLine("Successfully opened document %s", context.absoluteLevelPath.toUtf8().data());
- CLogFile::FormatLine("Level loading time: %.2f seconds", (loading_end_time - loading_start_time).GetSeconds());
- CLogFile::FormatLine("-----------------------------------------------------------");
- // It assumes loaded levels have already been exported. Can be a big fat lie, though.
- // The right way would require us to save to the level folder the export status of the
- // level.
- SetLevelExported(true);
- return true;
- }
- bool CCryEditDoc::OnNewDocument()
- {
- DeleteContents();
- m_pathName.clear();
- m_slicePathName.clear();
- SetModifiedFlag(false);
- return true;
- }
- bool CCryEditDoc::OnSaveDocument(const QString& lpszPathName)
- {
- bool saveSuccess = false;
- bool shouldSaveLevel = true;
- if (gEnv->IsEditorSimulationMode())
- {
- // Don't allow saving in AI/Physics mode.
- // Prompt the user to exit Simulation Mode (aka AI/Phyics mode) before saving.
- QWidget* mainWindow = nullptr;
- EBUS_EVENT_RESULT(mainWindow, AzToolsFramework::EditorRequests::Bus, GetMainWindow);
- QMessageBox msgBox(mainWindow);
- msgBox.setText(tr("You must exit AI/Physics mode before saving."));
- msgBox.setInformativeText(tr("The level will not be saved."));
- msgBox.setIcon(QMessageBox::Warning);
- msgBox.exec();
- }
- else
- {
- if (m_hasErrors || m_bLoadFailed)
- {
- QWidget* mainWindow = nullptr;
- AzToolsFramework::EditorRequests::Bus::BroadcastResult(
- mainWindow,
- &AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow);
- // Prompt the user that saving may result in data loss. Most of the time this is not desired
- // (which is why 'cancel' is the default interaction), but this does provide users a way to still
- // save their level if this is the only way they can solve the erroneous data.
- QMessageBox msgBox(mainWindow);
- msgBox.setText(tr("Your level loaded with errors, you may lose work if you save."));
- msgBox.setInformativeText(tr("Do you want to save your changes?"));
- msgBox.setIcon(QMessageBox::Warning);
- msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
- msgBox.setDefaultButton(QMessageBox::Cancel);
- int result = msgBox.exec();
- switch (result)
- {
- case QMessageBox::Save:
- // The user wishes to save, so don't bail.
- break;
- case QMessageBox::Cancel:
- // The user is canceling the save operation, so stop any saving from occuring.
- shouldSaveLevel = false;
- break;
- }
- }
- TSaveDocContext context;
- if (shouldSaveLevel && BeforeSaveDocument(lpszPathName, context))
- {
- DoSaveDocument(lpszPathName, context);
- saveSuccess = AfterSaveDocument(lpszPathName, context);
- }
- }
- return saveSuccess;
- }
- bool CCryEditDoc::BeforeSaveDocument(const QString& lpszPathName, TSaveDocContext& context)
- {
- // Don't save level data if any conflict exists
- if (HasLayerNameConflicts())
- {
- return false;
- }
- // Restore directory to root.
- QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
- // If we do not have a level loaded, we will also have an empty path, and that will
- // cause problems later in the save process. Early out here if that's the case
- QString levelFriendlyName = QFileInfo(lpszPathName).fileName();
- if (levelFriendlyName.isEmpty())
- {
- return false;
- }
- CryLog("Saving to %s...", levelFriendlyName.toUtf8().data());
- GetIEditor()->Notify(eNotify_OnBeginSceneSave);
- bool bSaved(true);
- context.bSaved = bSaved;
- return true;
- }
- bool CCryEditDoc::HasLayerNameConflicts() const
- {
- AZStd::vector<AZ::Entity*> editorEntities;
- AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
- &AzToolsFramework::EditorEntityContextRequestBus::Events::GetLooseEditorEntities,
- editorEntities);
- AZStd::unordered_map<AZStd::string, int> nameConflictMapping;
- for (AZ::Entity* entity : editorEntities)
- {
- AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
- entity->GetId(),
- &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::UpdateLayerNameConflictMapping,
- nameConflictMapping);
- }
- if (!nameConflictMapping.empty())
- {
- AzToolsFramework::Layers::NameConflictWarning* nameConflictWarning = new AzToolsFramework::Layers::NameConflictWarning(
- MainWindow::instance(),
- nameConflictMapping);
- nameConflictWarning->exec();
- return true;
- }
- return false;
- }
- bool CCryEditDoc::DoSaveDocument(const QString& filename, TSaveDocContext& context)
- {
- bool& bSaved = context.bSaved;
- if (!bSaved)
- {
- return false;
- }
- // Paranoia - we shouldn't get this far into the save routine without a level loaded (empty levelPath)
- // If nothing is loaded, we don't need to save anything
- if (filename.isEmpty())
- {
- bSaved = false;
- return false;
- }
- // Save Tag Point locations to file if auto save of tag points disabled
- if (!gSettings.bAutoSaveTagPoints)
- {
- CCryEditApp::instance()->SaveTagLocations();
- }
- QString normalizedPath = Path::ToUnixPath(filename);
- if (IsSliceFile(normalizedPath))
- {
- bSaved = SaveSlice(normalizedPath);
- }
- else
- {
- bSaved = SaveLevel(normalizedPath);
- }
- // Changes filename for this document.
- SetPathName(normalizedPath);
- return bSaved;
- }
- bool CCryEditDoc::AfterSaveDocument([[maybe_unused]] const QString& lpszPathName, TSaveDocContext& context, bool bShowPrompt)
- {
- bool bSaved = context.bSaved;
- GetIEditor()->Notify(eNotify_OnEndSceneSave);
- if (!bSaved)
- {
- if (bShowPrompt)
- {
- QMessageBox::warning(QApplication::activeWindow(), QString(), QObject::tr("Save Failed"), QMessageBox::Ok);
- }
- CLogFile::WriteLine("$4Document saving has failed.");
- }
- else
- {
- CLogFile::WriteLine("$3Document successfully saved");
- SetModifiedFlag(false);
- SetModifiedModules(eModifiedNothing);
- MainWindow::instance()->ResetAutoSaveTimers();
- }
- return bSaved;
- }
- static bool TryRenameFile(const QString& oldPath, const QString& newPath, int retryAttempts=10)
- {
- QFile(newPath).setPermissions(QFile::ReadOther | QFile::WriteOther);
- QFile::remove(newPath);
- // try a few times, something can lock the file (such as virus scanner, etc).
- for (int attempts = 0; attempts < retryAttempts; ++attempts)
- {
- if (QFile::rename(oldPath, newPath))
- {
- return true;
- }
- AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
- }
- return false;
- }
- bool CCryEditDoc::SaveLevel(const QString& filename)
- {
- AZ_PROFILE_FUNCTION(Editor);
- QWaitCursor wait;
- CAutoCheckOutDialogEnableForAll enableForAll;
- QString fullPathName = Path::ToUnixPath(filename);
- QString originaLevelFilename = Path::GetFile(m_pathName);
- if (QFileInfo(filename).isRelative())
- {
- // Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
- fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@projectroot@"))).absoluteFilePath(fullPathName));
- }
- if (!CFileUtil::OverwriteFile(fullPathName))
- {
- return false;
- }
- {
- AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel BackupBeforeSave");
- BackupBeforeSave();
- }
- // need to copy existing level data before saving to different folder
- const QString oldLevelFolder = Path::GetPath(GetLevelPathName()); // get just the folder name
- QString newLevelFolder = Path::GetPath(fullPathName);
- CFileUtil::CreateDirectory(newLevelFolder.toUtf8().data());
- GetIEditor()->GetGameEngine()->SetLevelPath(newLevelFolder);
- // QFileInfo operator== takes care of many side cases and will return true
- // if the folder is the same folder, even if other things (like slashes, etc) are wrong
- if (QFileInfo(oldLevelFolder) != QFileInfo(newLevelFolder))
- {
- // if we're saving to a new folder, we need to copy the old folder tree.
- auto pIPak = GetIEditor()->GetSystem()->GetIPak();
- const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
- const QString oldLevelName = Path::GetFile(GetLevelPathName());
- const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
- AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
- if (findHandle)
- {
- do
- {
- const QString sourceName{ QString::fromUtf8(findHandle.m_filename.data(), aznumeric_cast<int>(findHandle.m_filename.size())) };
- if ((findHandle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
- {
- // we only end up here if sourceName is a folder name.
- bool skipDir = sourceName == "." || sourceName == "..";
- skipDir |= IsBackupOrTempLevelSubdirectory(sourceName);
- skipDir |= sourceName == "Layers"; // layers folder will be created and written out as part of saving
- if (!skipDir)
- {
- QString oldFolderName = QDir(oldLevelFolder).absoluteFilePath(sourceName);
- QString newFolderName = QDir(newLevelFolder).absoluteFilePath(sourceName);
- CFileUtil::CreateDirectory(newFolderName.toUtf8().data());
- CFileUtil::CopyTree(oldFolderName, newFolderName);
- }
- continue;
- }
- bool skipFile = sourceName.endsWith(".cry", Qt::CaseInsensitive) ||
- sourceName.endsWith(".ly", Qt::CaseInsensitive) ||
- sourceName == originaLevelFilename; // level file will be written out by saving, ignore the source one
- if (skipFile)
- {
- continue;
- }
- // close any paks in the source folder so that when the paks are re-opened there is
- // no stale cached metadata in the pak system
- if (sourceName.endsWith(".pak", Qt::CaseInsensitive))
- {
- QString oldPackName = QDir(oldLevelFolder).absoluteFilePath(sourceName);
- pIPak->ClosePack(oldPackName.toUtf8().constData());
- }
- QString destName = sourceName;
- // copy oldLevel.xml -> newLevel.xml
- if (sourceName.compare(oldLevelXml, Qt::CaseInsensitive) == 0)
- {
- destName = Path::ReplaceExtension(Path::GetFile(fullPathName), "xml");
- }
- QString oldFilePath = QDir(oldLevelFolder).absoluteFilePath(sourceName);
- QString newFilePath = QDir(newLevelFolder).absoluteFilePath(destName);
- CFileUtil::CopyFile(oldFilePath, newFilePath);
- } while ((findHandle = pIPak->FindNext(findHandle)));
- pIPak->FindClose(findHandle);
- }
- // ensure that copied files are not read-only
- CFileUtil::ForEach(newLevelFolder, [](const QString& filePath)
- {
- QFile(filePath).setPermissions(QFile::ReadOther | QFile::WriteOther);
- });
- }
- // Save level to XML archive.
- CXmlArchive xmlAr;
- Save(xmlAr);
- // temp files (to be ignored by AssetProcessor take the form $tmp[0-9]*_...). we will conform
- // to that to make this file invisible to AP until it has been written completely.
- QString tempSaveFile = QDir(newLevelFolder).absoluteFilePath("$tmp_levelSave.tmp");
- QFile(tempSaveFile).setPermissions(QFile::ReadOther | QFile::WriteOther);
- QFile::remove(tempSaveFile);
- // Save AZ entities to the editor level.
- bool contentsAllSaved = false; // abort level save if anything within it fails
- auto tempFilenameStrData = tempSaveFile.toStdString();
- auto filenameStrData = fullPathName.toStdString();
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- if (!isPrefabEnabled)
- {
- AZStd::vector<char> entitySaveBuffer;
- bool savedEntities = false;
- CPakFile pakFile;
- {
- AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Open PakFile");
- if (!pakFile.Open(tempSaveFile.toUtf8().data(), false))
- {
- gEnv->pLog->LogWarning("Unable to open pack file %s for writing", tempSaveFile.toUtf8().data());
- return false;
- }
- }
- AZStd::vector<AZ::Entity*> editorEntities;
- AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
- &AzToolsFramework::EditorEntityContextRequestBus::Events::GetLooseEditorEntities,
- editorEntities);
- AZStd::vector<AZ::Entity*> layerEntities;
- AZ::SliceComponent::SliceReferenceToInstancePtrs instancesInLayers;
- for (AZ::Entity* entity : editorEntities)
- {
- AzToolsFramework::Layers::LayerResult layerSaveResult(AzToolsFramework::Layers::LayerResult::CreateSuccess());
- AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
- layerSaveResult,
- entity->GetId(),
- &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::WriteLayerAndGetEntities,
- newLevelFolder,
- layerEntities,
- instancesInLayers);
- layerSaveResult.MessageResult();
- }
- AZ::IO::ByteContainerStream<AZStd::vector<char>> entitySaveStream(&entitySaveBuffer);
- {
- AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Save Entities To Stream");
- EBUS_EVENT_RESULT(
- savedEntities, AzToolsFramework::EditorEntityContextRequestBus, SaveToStreamForEditor, entitySaveStream, layerEntities,
- instancesInLayers);
- }
- for (AZ::Entity* entity : editorEntities)
- {
- AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
- entity->GetId(), &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::RestoreEditorData);
- }
- if (savedEntities)
- {
- AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Updated PakFile levelEntities.editor_xml");
- pakFile.UpdateFile("LevelEntities.editor_xml", entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
- // Save XML archive to pak file.
- bool bSaved = xmlAr.SaveToPak(Path::GetPath(tempSaveFile), pakFile);
- if (bSaved)
- {
- contentsAllSaved = true;
- }
- else
- {
- gEnv->pLog->LogWarning("Unable to write the level data to file %s", tempSaveFile.toUtf8().data());
- }
- }
- else
- {
- gEnv->pLog->LogWarning("Unable to generate entity data for level save %s", tempSaveFile.toUtf8().data());
- }
- pakFile.Close();
- }
- else
- {
- if (m_prefabEditorEntityOwnershipInterface)
- {
- AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
- AZ_Assert(fileIO, "No File IO implementation available");
- AZ::IO::HandleType tempSaveFileHandle;
- AZ::IO::Result openResult = fileIO->Open(tempFilenameStrData.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, tempSaveFileHandle);
- contentsAllSaved = openResult;
- if (openResult)
- {
- AZ::IO::FileIOStream stream(tempSaveFileHandle, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, false);
- contentsAllSaved = m_prefabEditorEntityOwnershipInterface->SaveToStream(stream, AZStd::string_view(filenameStrData.data(), filenameStrData.size()));
- stream.Close();
- }
- }
- }
- if (!contentsAllSaved)
- {
- AZ_Error("Editor", false, "Error when writing level '%s' into tmpfile '%s'", filenameStrData.c_str(), tempFilenameStrData.c_str());
- QFile::remove(tempSaveFile);
- return false;
- }
- if (!TryRenameFile(tempSaveFile, fullPathName))
- {
- gEnv->pLog->LogWarning("Unable to move file %s to %s when saving", tempSaveFile.toUtf8().data(), fullPathName.toUtf8().data());
- return false;
- }
- // Commit changes to the disk.
- _flushall();
- AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::OnSaveLevel);
- return true;
- }
- bool CCryEditDoc::SaveSlice(const QString& filename)
- {
- using namespace AzToolsFramework::SliceUtilities;
- // Gather entities from live slice in memory
- AZ::SliceComponent* liveSlice = nullptr;
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(liveSlice,
- &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
- if (!liveSlice)
- {
- gEnv->pLog->LogWarning("Slice data not found.");
- return false;
- }
- AZStd::unordered_set<AZ::EntityId> liveEntityIds;
- if (!liveSlice->GetEntityIds(liveEntityIds))
- {
- gEnv->pLog->LogWarning("Error getting entities from slice.");
- return false;
- }
- // Prevent save when there are multiple root entities.
- bool foundRootEntity = false;
- for (AZ::EntityId entityId : liveEntityIds)
- {
- AZ::EntityId parentId;
- AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
- if (!parentId.IsValid())
- {
- if (foundRootEntity)
- {
- gEnv->pLog->LogWarning("Cannot save a slice with multiple root entities.");
- return false;
- }
- foundRootEntity = true;
- }
- }
- // Find target slice asset, and check if it's the same asset we opened
- AZ::Data::AssetId targetAssetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(targetAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, filename.toUtf8().data(), azrtti_typeid<AZ::SliceAsset>(), false);
- QString openedFilepath = Path::ToUnixPath(Path::GetRelativePath(m_slicePathName, true));
- AZ::Data::AssetId openedAssetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(openedAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, openedFilepath.toUtf8().data(), azrtti_typeid<AZ::SliceAsset>(), false);
- if (!targetAssetId.IsValid() || openedAssetId != targetAssetId)
- {
- gEnv->pLog->LogWarning("Slice editor can only modify existing slices. 'New Slice' and 'Save As' are not currently supported.");
- return false;
- }
- AZ::Data::Asset<AZ::SliceAsset> sliceAssetRef = AZ::Data::AssetManager::Instance().GetAsset<AZ::SliceAsset>(targetAssetId, AZ::Data::AssetLoadBehavior::Default);
- sliceAssetRef.BlockUntilLoadComplete();
- if (!sliceAssetRef)
- {
- gEnv->pLog->LogWarning("Error loading slice: %s", filename.toUtf8().data());
- return false;
- }
- // Get entities from target slice asset.
- AZ::SliceComponent* assetSlice = sliceAssetRef.Get()->GetComponent();
- if (!assetSlice)
- {
- gEnv->pLog->LogWarning("Error reading slice: %s", filename.toUtf8().data());
- return false;
- }
- AZStd::unordered_set<AZ::EntityId> assetEntityIds;
- if (!assetSlice->GetEntityIds(assetEntityIds))
- {
- gEnv->pLog->LogWarning("Error getting entities from slice: %s", filename.toUtf8().data());
- return false;
- }
- AZStd::unordered_set<AZ::EntityId> entityAdds;
- AZStd::unordered_set<AZ::EntityId> entityUpdates;
- AZStd::unordered_set<AZ::EntityId> entityRemovals = assetEntityIds;
- for (AZ::EntityId liveEntityId : liveEntityIds)
- {
- entityRemovals.erase(liveEntityId);
- if (assetEntityIds.find(liveEntityId) != assetEntityIds.end())
- {
- entityUpdates.insert(liveEntityId);
- }
- else
- {
- entityAdds.insert(liveEntityId);
- }
- }
- // Make a transaction targeting the specified slice
- SliceTransaction::TransactionPtr transaction = SliceTransaction::BeginSlicePush(sliceAssetRef);
- if (!transaction)
- {
- gEnv->pLog->LogWarning("Unable to update slice: %s", filename.toUtf8().data());
- return false;
- }
- // Tell the transaction about all adds/updates/removals
- for (AZ::EntityId id : entityAdds)
- {
- SliceTransaction::Result result = transaction->AddEntity(id);
- if (!result)
- {
- gEnv->pLog->LogWarning("Error adding entity with ID %s to slice: %s\n\n%s",
- id.ToString().c_str(), filename.toUtf8().data(), result.GetError().c_str());
- return false;
- }
- }
- for (AZ::EntityId id : entityRemovals)
- {
- SliceTransaction::Result result = transaction->RemoveEntity(id);
- if (!result)
- {
- gEnv->pLog->LogWarning("Error removing entity with ID %s from slice: %s\n\n%s",
- id.ToString().c_str(), filename.toUtf8().data(), result.GetError().c_str());
- return false;
- }
- }
- for (AZ::EntityId id : entityUpdates)
- {
- SliceTransaction::Result result = transaction->UpdateEntity(id);
- if (!result)
- {
- gEnv->pLog->LogWarning("Error updating entity with ID %s in slice: %s\n\n%s",
- id.ToString().c_str(), filename.toUtf8().data(), result.GetError().c_str());
- return false;
- }
- }
- // Commit
- SliceTransaction::Result commitResult = transaction->Commit(
- targetAssetId,
- SlicePreSaveCallbackForWorldEntities,
- nullptr,
- SliceTransaction::SliceCommitFlags::DisableUndoCapture);
- if (!commitResult)
- {
- gEnv->pLog->LogWarning("Failed to to save slice \"%s\".\n\nError:\n%s",
- filename.toUtf8().data(), commitResult.GetError().c_str());
- return false;
- }
- return true;
- }
- bool CCryEditDoc::LoadEntitiesFromLevel(const QString& levelPakFile)
- {
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- bool loadedSuccessfully = false;
- if (!isPrefabEnabled)
- {
- auto pakSystem = GetIEditor()->GetSystem()->GetIPak();
- bool pakOpened = pakSystem->OpenPack(levelPakFile.toUtf8().data());
- if (pakOpened)
- {
- const QString entityFilename = Path::GetPath(levelPakFile) + "LevelEntities.editor_xml";
- CCryFile entitiesFile;
- if (entitiesFile.Open(entityFilename.toUtf8().data(), "rt"))
- {
- AZStd::vector<char> fileBuffer;
- fileBuffer.resize(entitiesFile.GetLength());
- if (!fileBuffer.empty())
- {
- if (fileBuffer.size() == entitiesFile.ReadRaw(fileBuffer.begin(), fileBuffer.size()))
- {
- AZ::IO::ByteContainerStream<AZStd::vector<char>> fileStream(&fileBuffer);
- EBUS_EVENT_RESULT(
- loadedSuccessfully, AzToolsFramework::EditorEntityContextRequestBus, LoadFromStreamWithLayers, fileStream,
- levelPakFile);
- }
- else
- {
- AZ_Error(
- "Editor", false, "Failed to load level entities because the file \"%s\" could not be read.",
- entityFilename.toUtf8().data());
- }
- }
- else
- {
- AZ_Error(
- "Editor", false, "Failed to load level entities because the file \"%s\" is empty.", entityFilename.toUtf8().data());
- }
- entitiesFile.Close();
- }
- else
- {
- AZ_Error(
- "Editor", false, "Failed to load level entities because the file \"%s\" was not found.",
- entityFilename.toUtf8().data());
- }
- pakSystem->ClosePack(levelPakFile.toUtf8().data());
- }
- }
- else
- {
- AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
- AZ_Assert(fileIO, "No File IO implementation available");
- AZ::IO::HandleType fileHandle;
- AZ::IO::Result openResult = fileIO->Open(levelPakFile.toUtf8().data(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, fileHandle);
- if (openResult)
- {
- AZ::IO::FileIOStream stream(fileHandle, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, false);
- AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
- loadedSuccessfully, &AzToolsFramework::EditorEntityContextRequests::LoadFromStreamWithLayers, stream, levelPakFile);
- stream.Close();
- }
- }
- return loadedSuccessfully;
- }
- bool CCryEditDoc::LoadEntitiesFromSlice(const QString& sliceFile)
- {
- bool sliceLoaded = false;
- {
- AZ::IO::FileIOStream sliceFileStream(sliceFile.toUtf8().data(), AZ::IO::OpenMode::ModeRead);
- if (!sliceFileStream.IsOpen())
- {
- AZ_Error("Editor", false, "Failed to load entities because the file \"%s\" could not be read.", sliceFile.toUtf8().data());
- return false;
- }
- AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(sliceLoaded, &AzToolsFramework::EditorEntityContextRequestBus::Events::LoadFromStream, sliceFileStream);
- }
- if (!sliceLoaded)
- {
- AZ_Error("Editor", false, "Failed to load entities from slice file \"%s\"", sliceFile.toUtf8().data());
- return false;
- }
- return true;
- }
- bool CCryEditDoc::LoadLevel(TDocMultiArchive& arrXmlAr, const QString& absoluteCryFilePath)
- {
- bool isPrefabEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- auto pIPak = GetIEditor()->GetSystem()->GetIPak();
- QString folderPath = QFileInfo(absoluteCryFilePath).absolutePath();
- OnStartLevelResourceList();
- // Load next level resource list.
- if (!isPrefabEnabled)
- {
- pIPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Load(Path::Make(folderPath, "resourcelist.txt").toUtf8().data());
- }
- GetIEditor()->Notify(eNotify_OnBeginLoad);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorBeginLoad);
- //GetISystem()->GetISystemEventDispatcher()->OnSystemEvent( ESYSTEM_EVENT_LEVEL_LOAD_START,0,0 );
- DeleteContents();
- // Set level path directly *after* DeleteContents(), since that will unload the previous level and clear the level path.
- GetIEditor()->GetGameEngine()->SetLevelPath(folderPath);
- SetModifiedFlag(true); // dirty during de-serialize
- SetModifiedModules(eModifiedAll);
- Load(arrXmlAr, absoluteCryFilePath);
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
- // We don't need next level resource list anymore.
- if (!isPrefabEnabled)
- {
- pIPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Clear();
- }
- SetModifiedFlag(false); // start off with unmodified
- SetModifiedModules(eModifiedNothing);
- SetDocumentReady(true);
- GetIEditor()->Notify(eNotify_OnEndLoad);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorEndLoad);
- GetIEditor()->SetStatusText("Ready");
- return true;
- }
- void CCryEditDoc::Hold(const QString& holdName)
- {
- Hold(holdName, holdName);
- }
- void CCryEditDoc::Hold(const QString& holdName, const QString& relativeHoldPath)
- {
- if (!IsDocumentReady() || GetEditMode() == CCryEditDoc::DocumentEditingMode::SliceEdit)
- {
- return;
- }
- QString levelPath = GetIEditor()->GetGameEngine()->GetLevelPath();
- char resolvedLevelPath[AZ_MAX_PATH_LEN] = { 0 };
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(levelPath.toUtf8().data(), resolvedLevelPath, AZ_MAX_PATH_LEN);
- QString holdPath = QString::fromUtf8(resolvedLevelPath) + "/" + relativeHoldPath + "/";
- QString holdFilename = holdPath + holdName + GetIEditor()->GetGameEngine()->GetLevelExtension();
- // never auto-backup while we're trying to hold.
- bool oldBackup = gSettings.bBackupOnSave;
- gSettings.bBackupOnSave = false;
- SaveLevel(holdFilename);
- gSettings.bBackupOnSave = oldBackup;
- GetIEditor()->GetGameEngine()->SetLevelPath(levelPath);
- }
- void CCryEditDoc::Fetch(const QString& relativeHoldPath, bool bShowMessages, bool bDelHoldFolder)
- {
- Fetch(relativeHoldPath, relativeHoldPath, bShowMessages, bDelHoldFolder ? FetchPolicy::DELETE_FOLDER : FetchPolicy::PRESERVE);
- }
- void CCryEditDoc::Fetch(const QString& holdName, const QString& relativeHoldPath, bool bShowMessages, FetchPolicy policy)
- {
- if (!IsDocumentReady() || GetEditMode() == CCryEditDoc::DocumentEditingMode::SliceEdit)
- {
- return;
- }
- QString levelPath = GetIEditor()->GetGameEngine()->GetLevelPath();
- char resolvedLevelPath[AZ_MAX_PATH_LEN] = { 0 };
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(levelPath.toUtf8().data(), resolvedLevelPath, AZ_MAX_PATH_LEN);
- QString holdPath = QString::fromUtf8(resolvedLevelPath) + "/" + relativeHoldPath + "/";
- QString holdFilename = holdPath + holdName + GetIEditor()->GetGameEngine()->GetLevelExtension();
- {
- QFile cFile(holdFilename);
- // Open the file for writing, create it if needed
- if (!cFile.open(QFile::ReadOnly))
- {
- if (bShowMessages)
- {
- QMessageBox::information(QApplication::activeWindow(), QString(), QObject::tr("You have to use 'Hold' before you can fetch!"));
- }
- return;
- }
- }
- // Does the document contain unsaved data ?
- if (bShowMessages && IsModified() &&
- QMessageBox::question(QApplication::activeWindow(), QString(), QObject::tr("The document contains unsaved data, it will be lost if fetched.\r\nReally fetch old state?")) != QMessageBox::Yes)
- {
- return;
- }
- GetIEditor()->FlushUndo();
- TDocMultiArchive arrXmlAr = {};
- if (!LoadXmlArchiveArray(arrXmlAr, holdFilename, holdPath))
- {
- QMessageBox::critical(QApplication::activeWindow(), "Error", "The temporary 'Hold' level failed to load successfully. Your level might be corrupted, you should restart the Editor.", QMessageBox::Ok);
- AZ_Error("EditDoc", false, "Fetch failed to load the Xml Archive");
- return;
- }
- // Load the state
- LoadLevel(arrXmlAr, holdFilename);
- // Load AZ entities for the editor.
- LoadEntitiesFromLevel(holdFilename);
- GetIEditor()->GetGameEngine()->SetLevelPath(levelPath);
- GetIEditor()->FlushUndo();
- switch (policy)
- {
- case FetchPolicy::DELETE_FOLDER:
- CFileUtil::Deltree(holdPath.toUtf8().data(), true);
- break;
- case FetchPolicy::DELETE_LY_FILE:
- CFileUtil::DeleteFile(holdFilename);
- break;
- default:
- break;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- namespace {
- struct SFolderTime
- {
- QString folder;
- time_t creationTime;
- };
- bool SortByCreationTime(SFolderTime& a, SFolderTime& b)
- {
- return a.creationTime < b.creationTime;
- }
- // This function, given a source folder to scan, returns all folders within that folder
- // non-recursively. They will be sorted by time, with most recent first, and oldest last.
- void CollectAllFoldersByTime(const char* sourceFolder, std::vector<SFolderTime>& outputFolders)
- {
- QString folderMask(sourceFolder);
- AZ::IO::ArchiveFileIterator handle = gEnv->pCryPak->FindFirst((folderMask + "/*").toUtf8().data());
- if (handle)
- {
- do
- {
- if (handle.m_filename.front() == '.')
- {
- continue;
- }
- if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
- {
- SFolderTime ft;
- ft.folder = QString::fromUtf8(handle.m_filename.data(), aznumeric_cast<int>(handle.m_filename.size()));
- ft.creationTime = handle.m_fileDesc.tCreate;
- outputFolders.push_back(ft);
- }
- } while (handle = gEnv->pCryPak->FindNext(handle));
- gEnv->pCryPak->FindClose(handle);
- }
- std::sort(outputFolders.begin(), outputFolders.end(), SortByCreationTime);
- }
- }
- bool CCryEditDoc::BackupBeforeSave(bool force)
- {
- // This function will copy the contents of an entire level folder to a backup folder
- // and delete older ones based on user preferences.
- if (!force && !gSettings.bBackupOnSave)
- {
- return true; // not an error
- }
- QString levelPath = GetIEditor()->GetGameEngine()->GetLevelPath();
- if (levelPath.isEmpty())
- {
- return false;
- }
- char resolvedLevelPath[AZ_MAX_PATH_LEN] = { 0 };
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(levelPath.toUtf8().data(), resolvedLevelPath, AZ_MAX_PATH_LEN);
- QWaitCursor wait;
- QString saveBackupPath = QString::fromUtf8(resolvedLevelPath) + "/" + kSaveBackupFolder;
- std::vector<SFolderTime> folders;
- CollectAllFoldersByTime(saveBackupPath.toUtf8().data(), folders);
- for (int i = int(folders.size()) - gSettings.backupOnSaveMaxCount; i >= 0; --i)
- {
- CFileUtil::Deltree(QStringLiteral("%1/%2/").arg(saveBackupPath, folders[i].folder).toUtf8().data(), true);
- }
- QDateTime theTime = QDateTime::currentDateTime();
- QString subFolder = theTime.toString("yyyy-MM-dd [HH.mm.ss]");
- QString levelName = GetIEditor()->GetGameEngine()->GetLevelName();
- QString backupPath = saveBackupPath + "/" + subFolder;
- AZ::IO::FileIOBase::GetDirectInstance()->CreatePath(backupPath.toUtf8().data());
- QString sourcePath = QString::fromUtf8(resolvedLevelPath) + "/";
- QString ignoredFiles;
- for (const char* backupOrTempFolderName : kBackupOrTempFolders)
- {
- if (!ignoredFiles.isEmpty())
- {
- ignoredFiles += "|";
- }
- ignoredFiles += QString::fromUtf8(backupOrTempFolderName);
- }
- // copy that whole tree:
- AZ_TracePrintf("Editor", "Saving level backup to '%s'...\n", backupPath.toUtf8().data());
- if (IFileUtil::ETREECOPYOK != CFileUtil::CopyTree(sourcePath, backupPath, true, false, ignoredFiles.toUtf8().data()))
- {
- gEnv->pLog->LogWarning("Attempting to save backup to %s before saving, but could not write all files.", backupPath.toUtf8().data());
- return false;
- }
- return true;
- }
- void CCryEditDoc::SaveAutoBackup(bool bForce)
- {
- if (!bForce && (!gSettings.autoBackupEnabled || GetIEditor()->IsInGameMode()))
- {
- return;
- }
- QString levelPath = GetIEditor()->GetGameEngine()->GetLevelPath();
- if (levelPath.isEmpty())
- {
- return;
- }
- static bool isInProgress = false;
- if (isInProgress)
- {
- return;
- }
- isInProgress = true;
- QWaitCursor wait;
- QString autoBackupPath = levelPath + "/" + kAutoBackupFolder;
- // collect all subfolders
- std::vector<SFolderTime> folders;
- CollectAllFoldersByTime(autoBackupPath.toUtf8().data(), folders);
- for (int i = int(folders.size()) - gSettings.autoBackupMaxCount; i >= 0; --i)
- {
- CFileUtil::Deltree(QStringLiteral("%1/%2/").arg(autoBackupPath, folders[i].folder).toUtf8().data(), true);
- }
- // save new backup
- QDateTime theTime = QDateTime::currentDateTime();
- QString subFolder = theTime.toString(QStringLiteral("yyyy-MM-dd [HH.mm.ss]"));
- QString levelName = GetIEditor()->GetGameEngine()->GetLevelName();
- QString filename = autoBackupPath + "/" + subFolder + "/" + levelName + "/" + levelName + GetIEditor()->GetGameEngine()->GetLevelExtension();
- SaveLevel(filename);
- GetIEditor()->GetGameEngine()->SetLevelPath(levelPath);
- isInProgress = false;
- }
- bool CCryEditDoc::IsLevelExported() const
- {
- return m_boLevelExported;
- }
- void CCryEditDoc::SetLevelExported(bool boExported)
- {
- m_boLevelExported = boExported;
- }
- void CCryEditDoc::RegisterListener(IDocListener* listener)
- {
- if (listener == nullptr)
- {
- return;
- }
- if (std::find(m_listeners.begin(), m_listeners.end(), listener) == m_listeners.end())
- {
- m_listeners.push_back(listener);
- }
- }
- void CCryEditDoc::UnregisterListener(IDocListener* listener)
- {
- m_listeners.remove(listener);
- }
- void CCryEditDoc::LogLoadTime(int time) const
- {
- QString appFilePath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
- QString exePath = Path::GetPath(appFilePath);
- QString filename = Path::Make(exePath, "LevelLoadTime.log");
- QString level = GetIEditor()->GetGameEngine()->GetLevelPath();
- CLogFile::FormatLine("[LevelLoadTime] Level %s loaded in %d seconds", level.toUtf8().data(), time / 1000);
- #if defined(AZ_PLATFORM_WINDOWS)
- SetFileAttributesW(filename.toStdWString().c_str(), FILE_ATTRIBUTE_ARCHIVE);
- #endif
- QFile file(filename);
- if (!file.open(QFile::Append | QFile::Text))
- {
- return;
- }
- char version[50];
- GetIEditor()->GetFileVersion().ToShortString(version, AZ_ARRAY_SIZE(version));
- time = time / 1000;
- QString text = QStringLiteral("\n[%1] Level %2 loaded in %3 seconds").arg(version, level).arg(time);
- file.write(text.toUtf8());
- }
- void CCryEditDoc::SetDocumentReady(bool bReady)
- {
- m_bDocumentReady = bReady;
- }
- void CCryEditDoc::RegisterConsoleVariables()
- {
- doc_validate_surface_types = gEnv->pConsole->GetCVar("doc_validate_surface_types");
- if (!doc_validate_surface_types)
- {
- doc_validate_surface_types = REGISTER_INT_CB("doc_validate_surface_types", 0, 0,
- "Flag indicating whether icons are displayed on the animation graph.\n"
- "Default is 1.\n",
- OnValidateSurfaceTypesChanged);
- }
- }
- void CCryEditDoc::OnValidateSurfaceTypesChanged(ICVar*)
- {
- CErrorsRecorder errorsRecorder(GetIEditor());
- CSurfaceTypeValidator().Validate();
- }
- void CCryEditDoc::OnStartLevelResourceList()
- {
- // after loading another level we clear the RFOM_Level list, the first time the list should be empty
- static bool bFirstTime = true;
- if (bFirstTime)
- {
- const char* pResFilename = gEnv->pCryPak->GetResourceList(AZ::IO::IArchive::RFOM_Level)->GetFirst();
- while (pResFilename)
- {
- // This should be fixed because ExecuteCommandLine is executed right after engine init as we assume the
- // engine already has all data loaded an is initialized to process commands. Loading data afterwards means
- // some init was done later which can cause problems when running in the engine batch mode (executing console commands).
- gEnv->pLog->LogError("'%s' was loaded after engine init but before level load/new (should be fixed)", pResFilename);
- pResFilename = gEnv->pCryPak->GetResourceList(AZ::IO::IArchive::RFOM_Level)->GetNext();
- }
- bFirstTime = false;
- }
- gEnv->pCryPak->GetResourceList(AZ::IO::IArchive::RFOM_Level)->Clear();
- }
- bool CCryEditDoc::DoFileSave()
- {
- if (GetEditMode() == CCryEditDoc::DocumentEditingMode::LevelEdit)
- {
- // If the file to save is the temporary level it should 'save as' since temporary levels will get deleted
- const char* temporaryLevelName = GetTemporaryLevelName();
- if (QString::compare(GetIEditor()->GetLevelName(), temporaryLevelName) == 0)
- {
- QString filename;
- if (CCryEditApp::instance()->GetDocManager()->DoPromptFileName(filename, ID_FILE_SAVE_AS, 0, false, nullptr)
- && !filename.isEmpty() && !QFileInfo(filename).exists())
- {
- if (SaveLevel(filename))
- {
- DeleteTemporaryLevel();
- QString newLevelPath = filename.left(filename.lastIndexOf('/') + 1);
- GetIEditor()->GetDocument()->SetPathName(filename);
- GetIEditor()->GetGameEngine()->SetLevelPath(newLevelPath);
- return true;
- }
- }
- return false;
- }
- }
- if (!IsDocumentReady())
- {
- return false;
- }
- return Internal::SaveLevel();
- }
- const char* CCryEditDoc::GetTemporaryLevelName() const
- {
- return gEnv->pConsole->GetCVar("g_TemporaryLevelName")->GetString();
- }
- void CCryEditDoc::DeleteTemporaryLevel()
- {
- QString tempLevelPath = (Path::GetEditingGameDataFolder() + "/Levels/" + GetTemporaryLevelName()).c_str();
- GetIEditor()->GetSystem()->GetIPak()->ClosePacks(tempLevelPath.toUtf8().data());
- CFileUtil::Deltree(tempLevelPath.toUtf8().data(), true);
- }
- void CCryEditDoc::InitEmptyLevel(int /*resolution*/, int /*unitSize*/, bool /*bUseTerrain*/)
- {
- GetIEditor()->SetStatusText("Initializing Level...");
- OnStartLevelResourceList();
- GetIEditor()->Notify(eNotify_OnBeginNewScene);
- CLogFile::WriteLine("Preparing new document...");
- //cleanup resources!
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0);
- //////////////////////////////////////////////////////////////////////////
- // Initialize defaults.
- //////////////////////////////////////////////////////////////////////////
- if (!GetIEditor()->IsInPreviewMode())
- {
- GetIEditor()->ReloadTemplates();
- m_environmentTemplate = GetIEditor()->FindTemplate("Environment");
- GetIEditor()->GetGameEngine()->SetLevelCreated(true);
- GetIEditor()->GetGameEngine()->SetLevelCreated(false);
- }
- {
- // Notify listeners.
- std::list<IDocListener*> listeners = m_listeners;
- for (IDocListener* listener : listeners)
- {
- listener->OnNewDocument();
- }
- }
- // Tell the system that the level has been created/loaded.
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
- GetIEditor()->Notify(eNotify_OnEndNewScene);
- SetModifiedFlag(false);
- SetLevelExported(false);
- SetModifiedModules(eModifiedNothing);
- GetIEditor()->SetStatusText("Ready");
- }
- void CCryEditDoc::CreateDefaultLevelAssets([[maybe_unused]] int resolution, [[maybe_unused]] int unitSize)
- {
- AzToolsFramework::EditorLevelNotificationBus::Broadcast(&AzToolsFramework::EditorLevelNotificationBus::Events::OnNewLevelCreated);
- }
- void CCryEditDoc::OnEnvironmentPropertyChanged(IVariable* pVar)
- {
- if (pVar == nullptr)
- {
- return;
- }
- XmlNodeRef node = GetEnvironmentTemplate();
- if (node == nullptr)
- {
- return;
- }
- // QVariant will not convert a void * to int, so do it manually.
- int nKey = static_cast<int>(reinterpret_cast<intptr_t>(pVar->GetUserData().value<void*>()));
- int nGroup = (nKey & 0xFFFF0000) >> 16;
- int nChild = (nKey & 0x0000FFFF);
- if (nGroup < 0 || nGroup >= node->getChildCount())
- {
- return;
- }
- XmlNodeRef groupNode = node->getChild(nGroup);
- if (groupNode == nullptr)
- {
- return;
- }
- if (nChild < 0 || nChild >= groupNode->getChildCount())
- {
- return;
- }
- XmlNodeRef childNode = groupNode->getChild(nChild);
- if (childNode == nullptr)
- {
- return;
- }
- QString childValue;
- if (pVar->GetDataType() == IVariable::DT_COLOR)
- {
- Vec3 value;
- pVar->Get(value);
- QColor gammaColor = ColorLinearToGamma(ColorF(value.x, value.y, value.z));
- childValue = QStringLiteral("%1,%2,%3").arg(gammaColor.red()).arg(gammaColor.green()).arg(gammaColor.blue());
- }
- else
- {
- pVar->Get(childValue);
- }
- childNode->setAttr("value", childValue.toUtf8().data());
- }
- QString CCryEditDoc::GetCryIndexPath(const char* levelFilePath) const
- {
- QString levelPath = Path::GetPath(levelFilePath);
- QString levelName = Path::GetFileName(levelFilePath);
- return Path::AddPathSlash(levelPath + levelName + "_editor");
- }
- bool CCryEditDoc::LoadXmlArchiveArray(TDocMultiArchive& arrXmlAr, const QString& absoluteLevelPath, const QString& levelPath)
- {
- auto pIPak = GetIEditor()->GetSystem()->GetIPak();
- //if (m_pSWDoc->IsNull())
- {
- CXmlArchive* pXmlAr = new CXmlArchive();
- if (!pXmlAr)
- {
- return false;
- }
- CXmlArchive& xmlAr = *pXmlAr;
- xmlAr.bLoading = true;
- // bound to the level folder, as if it were the assets folder.
- // this mounts (whateverlevelname.ly) as @products@/Levels/whateverlevelname/ and thus it works...
- bool openLevelPakFileSuccess = pIPak->OpenPack(levelPath.toUtf8().data(), absoluteLevelPath.toUtf8().data());
- if (!openLevelPakFileSuccess)
- {
- return false;
- }
- CPakFile pakFile;
- bool loadFromPakSuccess = xmlAr.LoadFromPak(levelPath, pakFile);
- pIPak->ClosePack(absoluteLevelPath.toUtf8().data());
- if (!loadFromPakSuccess)
- {
- return false;
- }
- FillXmlArArray(arrXmlAr, &xmlAr);
- }
- return true;
- }
- void CCryEditDoc::ReleaseXmlArchiveArray(TDocMultiArchive& arrXmlAr)
- {
- SAFE_DELETE(arrXmlAr[0]);
- }
- //////////////////////////////////////////////////////////////////////////
- // AzToolsFramework::EditorEntityContextNotificationBus interface implementation
- void CCryEditDoc::OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/)
- {
- if (m_envProbeSliceAssetId == sliceAssetId)
- {
- const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
- const AZ::Uuid editorEnvProbeComponentId("{8DBD6035-583E-409F-AFD9-F36829A0655D}");
- AzToolsFramework::EntityIdList entityIds;
- entityIds.reserve(entities.size());
- for (const AZ::Entity* entity : entities)
- {
- if (entity->FindComponent(editorEnvProbeComponentId))
- {
- // Update Probe Area size to cover the whole terrain
- LmbrCentral::EditorLightComponentRequestBus::Event(entity->GetId(), &LmbrCentral::EditorLightComponentRequests::SetProbeAreaDimensions, AZ::Vector3(m_terrainSize, m_terrainSize, m_envProbeHeight));
- // Force update the light to apply cubemap
- LmbrCentral::EditorLightComponentRequestBus::Event(entity->GetId(), &LmbrCentral::EditorLightComponentRequests::RefreshLight);
- }
- entityIds.push_back(entity->GetId());
- }
- //Detach instantiated env probe entities from engine slice
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
- &AzToolsFramework::SliceEditorEntityOwnershipServiceRequests::DetachSliceEntities, entityIds);
- sliceAddress.SetInstance(nullptr);
- sliceAddress.SetReference(nullptr);
- SetModifiedFlag(true);
- SetModifiedModules(eModifiedEntities);
- AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
- //save after level default slice fully instantiated
- Save();
- }
- GetIEditor()->ResumeUndo();
- }
- void CCryEditDoc::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId, const AzFramework::SliceInstantiationTicket& /*ticket*/)
- {
- if (m_envProbeSliceAssetId == sliceAssetId)
- {
- AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
- AZ_Warning("Editor", false, "Failed to instantiate default environment probe slice.");
- }
- GetIEditor()->ResumeUndo();
- }
- //////////////////////////////////////////////////////////////////////////
- namespace AzToolsFramework
- {
- void CryEditDocFuncsHandler::Reflect(AZ::ReflectContext* context)
- {
- if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
- {
- // this will put these methods into the 'azlmbr.legacy.general' module
- auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
- {
- methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
- ->Attribute(AZ::Script::Attributes::Module, "legacy.general");
- };
- addLegacyGeneral(behaviorContext->Method("save_level", ::Internal::SaveLevel, nullptr, "Saves the current level."));
- }
- }
- }
- #include <moc_CryEditDoc.cpp>
|