You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Editor/GameEngine.cpp

920 lines
27 KiB
C++

/*
* 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 "GameEngine.h"
// Qt
#include <QMessageBox>
#include <QThread>
// AzCore
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/IO/IStreamer.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/Console/IConsole.h>
// AzFramework
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h> // for TerrainDataRequests
#include <AzFramework/Archive/IArchive.h>
// Editor
#include "IEditorImpl.h"
#include "CryEditDoc.h"
#include "Settings.h"
// CryCommon
#include <CryCommon/INavigationSystem.h>
#include <CryCommon/LyShine/ILyShine.h>
#include <CryCommon/MainThreadRenderRequestBus.h>
// Editor
#include "CryEdit.h"
#include "ViewManager.h"
#include "AnimationContext.h"
#include "UndoViewPosition.h"
#include "UndoViewRotation.h"
#include "MainWindow.h"
#include "Include/IObjectManager.h"
#include "ActionManager.h"
// Including this too early will result in a linker error
#include <CryCommon/CryLibrary.h>
// Implementation of System Callback structure.
struct SSystemUserCallback
: public ISystemUserCallback
{
SSystemUserCallback(IInitializeUIInfo* logo) : m_threadErrorHandler(this) { m_pLogo = logo; };
void OnSystemConnect(ISystem* pSystem) override
{
ModuleInitISystem(pSystem, "Editor");
}
bool OnError(const char* szErrorString) override
{
// since we show a message box, we have to use the GUI thread
if (QThread::currentThread() != qApp->thread())
{
bool result = false;
QMetaObject::invokeMethod(&m_threadErrorHandler, "OnError", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const char*, szErrorString));
return result;
}
if (szErrorString)
{
Log(szErrorString);
}
if (GetIEditor()->IsInTestMode())
{
exit(1);
}
char str[4096];
if (szErrorString)
{
azsnprintf(str, 4096, "%s\r\nSave Level Before Exiting the Editor?", szErrorString);
}
else
{
azsnprintf(str, 4096, "Unknown Error\r\nSave Level Before Exiting the Editor?");
}
int res = IDNO;
ICVar* pCVar = gEnv->pConsole ? gEnv->pConsole->GetCVar("sys_no_crash_dialog") : nullptr;
if (!pCVar || pCVar->GetIVal() == 0)
{
res = QMessageBox::critical(QApplication::activeWindow(), QObject::tr("Engine Error"), str, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
}
if (res == QMessageBox::Yes || res == QMessageBox::No)
{
if (res == QMessageBox::Yes)
{
if (GetIEditor()->SaveDocument())
{
QMessageBox::information(QApplication::activeWindow(), QObject::tr("Save"), QObject::tr("Level has been successfully saved!\r\nPress Ok to terminate Editor."));
}
}
}
return true;
}
bool OnSaveDocument() override
{
bool success = false;
if (GetIEditor())
{
// Turn off save backup as we force a backup before reaching this point
bool prevSaveBackup = gSettings.bBackupOnSave;
gSettings.bBackupOnSave = false;
success = GetIEditor()->SaveDocument();
gSettings.bBackupOnSave = prevSaveBackup;
}
return success;
}
bool OnBackupDocument() override
{
CCryEditDoc* level = GetIEditor() ? GetIEditor()->GetDocument() : nullptr;
if (level)
{
return level->BackupBeforeSave(true);
}
return false;
}
void OnProcessSwitch() override
{
if (GetIEditor()->IsInGameMode())
{
GetIEditor()->SetInGameMode(false);
}
}
void OnInitProgress(const char* sProgressMsg) override
{
if (m_pLogo)
{
m_pLogo->SetInfoText(sProgressMsg);
}
}
int ShowMessage(const char* text, const char* caption, unsigned int uType) override
{
if (CCryEditApp::instance()->IsInAutotestMode())
{
return IDOK;
}
const UINT kMessageBoxButtonMask = 0x000f;
if (!GetIEditor()->IsInGameMode() && (uType == 0 || uType == MB_OK || !(uType & kMessageBoxButtonMask)))
{
static_cast<CEditorImpl*>(GetIEditor())->AddErrorMessage(text, caption);
return IDOK;
}
return CryMessageBox(text, caption, uType);
}
void GetMemoryUsage(ICrySizer* pSizer) override
{
GetIEditor()->GetMemoryUsage(pSizer);
}
void OnSplashScreenDone()
{
m_pLogo = nullptr;
}
private:
IInitializeUIInfo* m_pLogo;
ThreadedOnErrorHandler m_threadErrorHandler;
};
ThreadedOnErrorHandler::ThreadedOnErrorHandler(ISystemUserCallback* callback)
: m_userCallback(callback)
{
moveToThread(qApp->thread());
}
ThreadedOnErrorHandler::~ThreadedOnErrorHandler()
{
}
bool ThreadedOnErrorHandler::OnError(const char* error)
{
return m_userCallback->OnError(error);
}
//! This class will be used by CSystem to find out whether the negotiation with the assetprocessor failed
class AssetProcessConnectionStatus
: public AzFramework::AssetSystemConnectionNotificationsBus::Handler
{
public:
AssetProcessConnectionStatus()
{
AzFramework::AssetSystemConnectionNotificationsBus::Handler::BusConnect();
};
~AssetProcessConnectionStatus() override
{
AzFramework::AssetSystemConnectionNotificationsBus::Handler::BusDisconnect();
}
//! Notifies listeners that connection to the Asset Processor failed
void ConnectionFailed() override
{
m_connectionFailed = true;
}
void NegotiationFailed() override
{
m_negotiationFailed = true;
}
bool CheckConnectionFailed()
{
return m_connectionFailed;
}
bool CheckNegotiationFailed()
{
return m_negotiationFailed;
}
private:
bool m_connectionFailed = false;
bool m_negotiationFailed = false;
};
AZ_PUSH_DISABLE_WARNING(4273, "-Wunknown-warning-option")
CGameEngine::CGameEngine()
: m_gameDll(nullptr)
, m_bIgnoreUpdates(false)
, m_ePendingGameMode(ePGM_NotPending)
, m_modalWindowDismisser(nullptr)
AZ_POP_DISABLE_WARNING
{
m_pISystem = nullptr;
m_bLevelLoaded = false;
m_bInGameMode = false;
m_bSimulationMode = false;
m_bSyncPlayerPosition = true;
m_hSystemHandle = nullptr;
m_bJustCreated = false;
m_levelName = "Untitled";
m_levelExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
m_playerViewTM.SetIdentity();
GetIEditor()->RegisterNotifyListener(this);
}
AZ_PUSH_DISABLE_WARNING(4273, "-Wunknown-warning-option")
CGameEngine::~CGameEngine()
{
AZ_POP_DISABLE_WARNING
GetIEditor()->UnregisterNotifyListener(this);
m_pISystem->GetIMovieSystem()->SetCallback(nullptr);
if (m_gameDll)
{
CryFreeLibrary(m_gameDll);
}
delete m_pISystem;
m_pISystem = nullptr;
if (m_hSystemHandle)
{
CryFreeLibrary(m_hSystemHandle);
}
delete m_pSystemUserCallback;
}
static int ed_killmemory_size;
static int ed_indexfiles;
void KillMemory(IConsoleCmdArgs* /* pArgs */)
{
while (true)
{
const int kLimit = 10000000;
int size;
if (ed_killmemory_size > 0)
{
size = ed_killmemory_size;
}
else
{
size = rand() * rand();
size = size > kLimit ? kLimit : size;
}
new uint8[size];
}
}
static void CmdGotoEditor(IConsoleCmdArgs* pArgs)
{
// feature is mostly useful for QA purposes, this works with the game "goto" command
// this console command actually is used by the game command, the editor command shouldn't be used by the user
int iArgCount = pArgs->GetArgCount();
CViewManager* pViewManager = GetIEditor()->GetViewManager();
CViewport* pRenderViewport = pViewManager->GetGameViewport();
if (!pRenderViewport)
{
return;
}
float x, y, z, wx, wy, wz;
if (iArgCount == 7
&& azsscanf(pArgs->GetArg(1), "%f", &x) == 1
&& azsscanf(pArgs->GetArg(2), "%f", &y) == 1
&& azsscanf(pArgs->GetArg(3), "%f", &z) == 1
&& azsscanf(pArgs->GetArg(4), "%f", &wx) == 1
&& azsscanf(pArgs->GetArg(5), "%f", &wy) == 1
&& azsscanf(pArgs->GetArg(6), "%f", &wz) == 1)
{
Matrix34 tm = pRenderViewport->GetViewTM();
tm.SetTranslation(Vec3(x, y, z));
tm.SetRotation33(Matrix33::CreateRotationXYZ(DEG2RAD(Ang3(wx, wy, wz))));
pRenderViewport->SetViewTM(tm);
}
}
AZ::Outcome<void, AZStd::string> CGameEngine::Init(
bool bPreviewMode,
bool bTestMode,
const char* sInCmdLine,
IInitializeUIInfo* logo,
HWND hwndForInputSystem)
{
m_pSystemUserCallback = new SSystemUserCallback(logo);
m_hSystemHandle = CryLoadLibraryDefName("CrySystem");
if (!m_hSystemHandle)
{
auto errorMessage = AZStd::string::format("%s Loading Failed", CryLibraryDefName("CrySystem"));
Error(errorMessage.c_str());
return AZ::Failure(errorMessage);
}
PFNCREATESYSTEMINTERFACE pfnCreateSystemInterface =
(PFNCREATESYSTEMINTERFACE)CryGetProcAddress(m_hSystemHandle, "CreateSystemInterface");
SSystemInitParams sip;
sip.bEditor = true;
sip.bDedicatedServer = false;
AZ::Interface<AZ::IConsole>::Get()->PerformCommand("sv_isDedicated false");
sip.bPreview = bPreviewMode;
sip.bTestMode = bTestMode;
sip.hInstance = nullptr;
sip.pSharedEnvironment = AZ::Environment::GetInstance();
#ifdef AZ_PLATFORM_MAC
// Create a hidden QWidget. Would show a black window on macOS otherwise.
auto window = new QWidget();
QObject::connect(qApp, &QApplication::lastWindowClosed, window, &QWidget::deleteLater);
sip.hWnd = (HWND)window->winId();
#else
sip.hWnd = hwndForInputSystem;
#endif
sip.pLogCallback = &m_logFile;
sip.sLogFileName = "@log@/Editor.log";
sip.pUserCallback = m_pSystemUserCallback;
if (sInCmdLine)
{
azstrncpy(sip.szSystemCmdLine, AZ_COMMAND_LINE_LEN, sInCmdLine, AZ_COMMAND_LINE_LEN);
if (strstr(sInCmdLine, "-export") || strstr(sInCmdLine, "/export") || strstr(sInCmdLine, "-autotest_mode"))
{
sip.bUnattendedMode = true;
}
}
if (sip.bUnattendedMode)
{
m_modalWindowDismisser = AZStd::make_unique<ModalWindowDismisser>();
}
AssetProcessConnectionStatus apConnectionStatus;
m_pISystem = pfnCreateSystemInterface(sip);
if (!gEnv)
{
gEnv = m_pISystem->GetGlobalEnvironment();
}
if (!m_pISystem)
{
AZStd::string errorMessage = "Could not initialize CSystem. View the logs for more details.";
gEnv = nullptr;
Error("CreateSystemInterface Failed");
return AZ::Failure(errorMessage);
}
if (apConnectionStatus.CheckNegotiationFailed())
{
auto errorMessage = AZStd::string::format("Negotiation with Asset Processor failed.\n"
"Please ensure the Asset Processor is running on the same branch and try again.");
gEnv = nullptr;
return AZ::Failure(errorMessage);
}
if (apConnectionStatus.CheckConnectionFailed())
{
AzFramework::AssetSystem::ConnectionSettings connectionSettings;
AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
auto errorMessage = AZStd::string::format("Unable to connect to the local Asset Processor.\n\n"
"The Asset Processor is either not running locally or not accepting connections on port %hu. "
"Check your remote_port settings in bootstrap.cfg or view the Asset Processor's \"Logs\" tab "
"for any errors.", connectionSettings.m_assetProcessorPort);
gEnv = nullptr;
return AZ::Failure(errorMessage);
}
SetEditorCoreEnvironment(gEnv);
if (gEnv && gEnv->pMovieSystem)
{
gEnv->pMovieSystem->EnablePhysicsEvents(m_bSimulationMode);
}
CLogFile::AboutSystem();
REGISTER_CVAR(ed_killmemory_size, -1, VF_DUMPTODISK, "Sets the testing allocation size. -1 for random");
REGISTER_CVAR(ed_indexfiles, 1, VF_DUMPTODISK, "Index game resource files, 0 - inactive, 1 - active");
REGISTER_COMMAND("ed_killmemory", KillMemory, VF_NULL, "");
REGISTER_COMMAND("ed_goto", CmdGotoEditor, VF_CHEAT, "Internal command, used by the 'GOTO' console command\n");
// The editor needs to handle the quit command differently
gEnv->pConsole->RemoveCommand("quit");
REGISTER_COMMAND("quit", CGameEngine::HandleQuitRequest, VF_RESTRICTEDMODE, "Quit/Shutdown the engine");
EBUS_EVENT(CrySystemEventBus, OnCryEditorInitialized);
return AZ::Success();
}
bool CGameEngine::InitGame(const char*)
{
m_pISystem->ExecuteCommandLine();
return true;
}
void CGameEngine::SetLevelPath(const QString& path)
{
QByteArray levelPath;
levelPath.reserve(AZ_MAX_PATH_LEN);
levelPath = Path::ToUnixPath(Path::RemoveBackslash(path)).toUtf8();
m_levelPath = levelPath;
m_levelName = m_levelPath.mid(m_levelPath.lastIndexOf('/') + 1);
const char* oldExtension = EditorUtils::LevelFile::GetOldCryFileExtension();
const char* defaultExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
// Store off if
if (QFileInfo(path + oldExtension).exists())
{
m_levelExtension = oldExtension;
}
else
{
m_levelExtension = defaultExtension;
}
}
bool CGameEngine::LoadLevel(
[[maybe_unused]] bool bDeleteAIGraph,
bool bReleaseResources)
{
m_bLevelLoaded = false;
CLogFile::FormatLine("Loading map '%s' into engine...", m_levelPath.toUtf8().data());
// Switch the current directory back to the Primary CD folder first.
// The engine might have trouble to find some files when the current
// directory is wrong
QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
if (!usePrefabSystemForLevels)
{
QString pakFile = m_levelPath + "/level.pak";
// Open Pak file for this level.
if (!m_pISystem->GetIPak()->OpenPack(m_levelPath.toUtf8().data(), pakFile.toUtf8().data()))
{
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, "Level Pack File %s Not Found", pakFile.toUtf8().data());
}
}
// Initialize physics grid.
if (bReleaseResources)
{
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
int physicsEntityGridSize = static_cast<int>(terrainAabb.GetXExtent());
//CryPhysics under performs if physicsEntityGridSize < nTerrainSize.
if (physicsEntityGridSize <= 0)
{
ICVar* pCvar = m_pISystem->GetIConsole()->GetCVar("e_PhysEntityGridSizeDefault");
physicsEntityGridSize = pCvar ? pCvar->GetIVal() : 4096;
}
}
// Audio: notify audio of level loading start?
GetIEditor()->GetObjectManager()->SendEvent(EVENT_REFRESH);
m_bLevelLoaded = true;
return true;
}
bool CGameEngine::ReloadLevel()
{
if (!LoadLevel(false, false))
{
return false;
}
return true;
}
void CGameEngine::SwitchToInGame()
{
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
if (streamer)
{
AZStd::binary_semaphore wait;
AZ::IO::FileRequestPtr flush = streamer->FlushCaches();
streamer->SetRequestCompleteCallback(flush, [&wait](AZ::IO::FileRequestHandle) { wait.release(); });
streamer->QueueRequest(flush);
wait.acquire();
}
GetIEditor()->Notify(eNotify_OnBeginGameMode);
m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(true);
m_bInGameMode = true;
// Disable accelerators.
GetIEditor()->EnableAcceleratos(false);
//! Send event to switch into game.
GetIEditor()->GetObjectManager()->SendEvent(EVENT_INGAME);
m_pISystem->GetIMovieSystem()->Reset(true, false);
// Transition to runtime entity context.
EBUS_EVENT(AzToolsFramework::EditorEntityContextRequestBus, StartPlayInEditor);
if (!CCryEditApp::instance()->IsInAutotestMode())
{
// Constrain and hide the system cursor (important to do this last)
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
AzFramework::SystemCursorState::ConstrainedAndHidden);
}
Log("Entered game mode");
}
void CGameEngine::SwitchToInEditor()
{
// Transition to editor entity context.
EBUS_EVENT(AzToolsFramework::EditorEntityContextRequestBus, StopPlayInEditor);
// Reset movie system
for (int i = m_pISystem->GetIMovieSystem()->GetNumPlayingSequences(); --i >= 0;)
{
m_pISystem->GetIMovieSystem()->GetPlayingSequence(i)->Deactivate();
}
m_pISystem->GetIMovieSystem()->Reset(false, false);
CViewport* pGameViewport = GetIEditor()->GetViewManager()->GetGameViewport();
m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(m_bSimulationMode);
// Enable accelerators.
GetIEditor()->EnableAcceleratos(true);
// reset UI system
if (gEnv->pLyShine)
{
gEnv->pLyShine->Reset();
}
// [Anton] - order changed, see comments for CGameEngine::SetSimulationMode
//! Send event to switch out of game.
GetIEditor()->GetObjectManager()->SendEvent(EVENT_OUTOFGAME);
m_bInGameMode = false;
// Out of game in Editor mode.
if (pGameViewport)
{
pGameViewport->SetViewTM(m_playerViewTM);
}
GetIEditor()->Notify(eNotify_OnEndGameMode);
// Unconstrain the system cursor and make it visible (important to do this last)
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
AzFramework::SystemCursorState::UnconstrainedAndVisible);
Log("Exited game mode");
}
void CGameEngine::HandleQuitRequest(IConsoleCmdArgs* /*args*/)
{
if (GetIEditor()->GetGameEngine()->IsInGameMode())
{
GetIEditor()->GetGameEngine()->RequestSetGameMode(false);
gEnv->pConsole->ShowConsole(false);
}
else
{
MainWindow::instance()->GetActionManager()->GetAction(ID_APP_EXIT)->trigger();
}
}
void CGameEngine::RequestSetGameMode(bool inGame)
{
m_ePendingGameMode = inGame ? ePGM_SwitchToInGame : ePGM_SwitchToInEditor;
if (m_ePendingGameMode == ePGM_SwitchToInGame)
{
AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
&AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStartGameModeRequest);
}
else if (m_ePendingGameMode == ePGM_SwitchToInEditor)
{
AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
&AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStopGameModeRequest);
}
}
void CGameEngine::SetGameMode(bool bInGame)
{
if (m_bInGameMode == bInGame)
{
return;
}
if (!GetIEditor()->GetDocument())
{
return;
}
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_START, bInGame, 0);
// Enables engine to know about that.
gEnv->SetIsEditorGameMode(bInGame);
// Ignore updates while changing in and out of game mode
m_bIgnoreUpdates = true;
// Switching modes will destroy the current AzFramework::EntityConext which may contain
// data the queued events hold on to, so execute all queued events before switching.
ExecuteQueuedEvents();
if (bInGame)
{
SwitchToInGame();
}
else
{
SwitchToInEditor();
}
GetIEditor()->GetObjectManager()->SendEvent(EVENT_PHYSICS_APPLYSTATE);
// Enables engine to know about that.
if (MainWindow::instance())
{
AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
MainWindow::instance()->setFocus();
}
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_GAME_MODE_CHANGED, bInGame, 0);
m_bIgnoreUpdates = false;
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_END, bInGame, 0);
}
void CGameEngine::SetSimulationMode(bool enabled, bool bOnlyPhysics)
{
if (m_bSimulationMode == enabled)
{
return;
}
m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(enabled);
if (enabled)
{
GetIEditor()->Notify(eNotify_OnBeginSimulationMode);
}
else
{
GetIEditor()->Notify(eNotify_OnEndSimulationMode);
}
m_bSimulationMode = enabled;
// Enables engine to know about simulation mode.
gEnv->SetIsEditorSimulationMode(enabled);
if (m_bSimulationMode)
{
// [Anton] the order of the next 3 calls changed, since, EVENT_INGAME loads physics state (if any),
// and Reset should be called before it
GetIEditor()->GetObjectManager()->SendEvent(EVENT_INGAME);
}
else
{
GetIEditor()->GetObjectManager()->SendEvent(EVENT_OUTOFGAME);
}
GetIEditor()->GetObjectManager()->SendEvent(EVENT_PHYSICS_APPLYSTATE);
// Execute all queued events before switching modes.
ExecuteQueuedEvents();
// Transition back to editor entity context.
// Symmetry is not critical. It's okay to call this even if we never called StartPlayInEditor
// (bOnlyPhysics was true when we entered simulation mode).
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StopPlayInEditor);
if (m_bSimulationMode && !bOnlyPhysics)
{
// Transition to runtime entity context.
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StartPlayInEditor);
}
AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
}
void CGameEngine::SetPlayerViewMatrix(const Matrix34& tm, [[maybe_unused]] bool bEyePos)
{
m_playerViewTM = tm;
}
void CGameEngine::SyncPlayerPosition(bool bEnable)
{
m_bSyncPlayerPosition = bEnable;
if (m_bSyncPlayerPosition)
{
SetPlayerViewMatrix(m_playerViewTM);
}
}
void CGameEngine::SetCurrentMOD(const char* sMod)
{
m_MOD = sMod;
}
QString CGameEngine::GetCurrentMOD() const
{
return m_MOD;
}
void CGameEngine::Update()
{
if (m_bIgnoreUpdates)
{
return;
}
switch (m_ePendingGameMode)
{
case ePGM_SwitchToInGame:
{
SetGameMode(true);
m_ePendingGameMode = ePGM_NotPending;
break;
}
case ePGM_SwitchToInEditor:
{
bool wasInSimulationMode = GetIEditor()->GetGameEngine()->GetSimulationMode();
if (wasInSimulationMode)
{
GetIEditor()->GetGameEngine()->SetSimulationMode(false);
}
SetGameMode(false);
if (wasInSimulationMode)
{
GetIEditor()->GetGameEngine()->SetSimulationMode(true);
}
m_ePendingGameMode = ePGM_NotPending;
break;
}
}
AZ::ComponentApplication* componentApplication = nullptr;
EBUS_EVENT_RESULT(componentApplication, AZ::ComponentApplicationBus, GetApplication);
if (m_bInGameMode)
{
if (gEnv->pSystem)
{
gEnv->pSystem->UpdatePreTickBus();
componentApplication->Tick(gEnv->pTimer->GetFrameTime(ITimer::ETIMER_GAME));
gEnv->pSystem->UpdatePostTickBus();
}
if (CViewport* pRenderViewport = GetIEditor()->GetViewManager()->GetGameViewport())
{
pRenderViewport->Update();
}
}
else
{
// [marco] check current sound and vis areas for music etc.
// but if in game mode, 'cos is already done in the above call to game->update()
unsigned int updateFlags = ESYSUPDATE_EDITOR;
GetIEditor()->GetAnimation()->Update();
GetIEditor()->GetSystem()->UpdatePreTickBus(updateFlags);
componentApplication->Tick(gEnv->pTimer->GetFrameTime(ITimer::ETIMER_GAME));
GetIEditor()->GetSystem()->UpdatePostTickBus(updateFlags);
}
}
void CGameEngine::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnSplashScreenDestroyed:
{
if (m_pSystemUserCallback != nullptr)
{
m_pSystemUserCallback->OnSplashScreenDone();
}
}
break;
}
}
void CGameEngine::OnTerrainModified(const Vec2& modPosition, float modAreaRadius, bool fullTerrain)
{
INavigationSystem* pNavigationSystem = nullptr; // INavigationSystem will be converted to an AZInterface (LY-111343)
if (pNavigationSystem)
{
// Only report local modifications, not a change in the full terrain (probably happening during initialization)
if (fullTerrain == false)
{
const Vec2 offset(modAreaRadius * 1.5f, modAreaRadius * 1.5f);
AABB updateBox;
updateBox.min = modPosition - offset;
updateBox.max = modPosition + offset;
AzFramework::Terrain::TerrainDataRequests* terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
AZ_Assert(terrain != nullptr, "Expecting a valid terrain handler when the terrain is modified");
const float terrainHeight1 = terrain->GetHeightFromFloats(updateBox.min.x, updateBox.min.y);
const float terrainHeight2 = terrain->GetHeightFromFloats(updateBox.max.x, updateBox.max.y);
const float terrainHeight3 = terrain->GetHeightFromFloats(modPosition.x, modPosition.y);
updateBox.min.z = min(terrainHeight1, min(terrainHeight2, terrainHeight3)) - (modAreaRadius * 2.0f);
updateBox.max.z = max(terrainHeight1, max(terrainHeight2, terrainHeight3)) + (modAreaRadius * 2.0f);
pNavigationSystem->WorldChanged(updateBox);
}
}
}
void CGameEngine::OnAreaModified(const AABB& modifiedArea)
{
INavigationSystem* pNavigationSystem = nullptr; // INavigationSystem will be converted to an AZInterface (LY-111343)
if (pNavigationSystem)
{
pNavigationSystem->WorldChanged(modifiedArea);
}
}
void CGameEngine::ExecuteQueuedEvents()
{
AZ::Data::AssetBus::ExecuteQueuedEvents();
AZ::TickBus::ExecuteQueuedEvents();
AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
}
#include <moc_GameEngine.cpp>