Browse Source

Implemented a way to send notifications/data from any System/Scene/Object to Engine (used for changing EngineStates, reloading, etc)
Changed the way EngineStates are being created, initialized and loaded. They can now be deconstructed and constructed any time
Added an EngineChangeData container, storing information about a notification sent to Engine
Added a way to get the UniversalScene change controller from an EngineState to Engine, needed for getting the notifications sent to Engine
Added processing of notifications in Engine
Added a way to postpone loading of a scene file after initialization in EngineState
Added an option to load and reload a scene in EditorWindow
Fixed a bug of a system not setting a SystemScene pointer to nullptr after deleting a scene, in every System

Paul A 2 years ago
parent
commit
b909cd7f31

+ 1 - 0
Praxis3D/Praxis3D.vcxproj

@@ -144,6 +144,7 @@
     <ClCompile Include="Source\AtmScatteringModel.cpp" />
     <ClCompile Include="Source\AtmScatteringPass.cpp" />
     <ClCompile Include="Source\AudioScene.cpp" />
+    <ClCompile Include="Source\AudioSystem.cpp" />
     <ClCompile Include="Source\AudioTask.cpp" />
     <ClCompile Include="Source\BaseGraphicsComponent.cpp" />
     <ClCompile Include="Source\BaseGraphicsObjects.cpp" />

+ 3 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -444,6 +444,9 @@
     <ClCompile Include="..\Dependencies\include\imgui\imgui_stdlib.cpp">
       <Filter>3rd Party\imgui\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\AudioSystem.cpp">
+      <Filter>Audio\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Source\ErrorCodes.h">

+ 1 - 1
Praxis3D/Source/AudioScene.h

@@ -128,7 +128,7 @@ public:
 	BitMask getPotentialSystemChanges() { return Systems::Changes::None; }
 
 private:
-	// Returns true if the operation was successful; returns falls if operation failed and logs an error
+	// Returns true if the operation was successful; returns false if operation failed and logs an error
 	const inline bool fmodErrorLog(const FMOD_RESULT p_fmodResult, const std::string &p_objectName = "", const ErrorType p_errorType = ErrorType::Warning, const ErrorSource p_errorSource = ErrorSource::Source_AudioScene) const noexcept
 	{
 		// Check if the operation result is OK

+ 10 - 0
Praxis3D/Source/AudioSystem.cpp

@@ -0,0 +1,10 @@
+#include "AudioSystem.h"
+
+ErrorCode AudioSystem::init()
+{
+	ErrorCode returnCode = ErrorCode::Success;
+
+	ErrHandlerLoc::get().log(ErrorCode::Initialize_success, ErrorSource::Source_AudioSystem);
+
+	return returnCode;
+}

+ 4 - 8
Praxis3D/Source/AudioSystem.h

@@ -21,14 +21,7 @@ public:
 				delete m_audioScenes[i];
 	}
 
-	ErrorCode init()
-	{
-		ErrorCode returnCode = ErrorCode::Success;
-
-		ErrHandlerLoc::get().log(ErrorCode::Initialize_success, ErrorSource::Source_AudioSystem);
-
-		return returnCode;
-	}
+	ErrorCode init();
 
 	ErrorCode setup(const PropertySet &p_properties)
 	{
@@ -73,7 +66,10 @@ public:
 	void deleteScene(EngineStateType p_engineState)
 	{
 		if(m_audioScenes[p_engineState] != nullptr)
+		{
 			delete m_audioScenes[p_engineState];
+			m_audioScenes[p_engineState] = nullptr;
+		}
 	}
 
 protected:

+ 8 - 0
Praxis3D/Source/CommonDefinitions.h

@@ -11,6 +11,14 @@ typedef std::vector<std::function<void()>> Functors;
 
 constexpr EntityID NULL_ENTITY_ID = std::numeric_limits<EntityID>::max();
 
+enum EngineChangeType : unsigned int
+{
+	EngineChangeType_None = 0,
+	EngineChangeType_SceneFilename,
+	EngineChangeType_SceneLoad,
+	EngineChangeType_SceneReload,
+	EngineChangeType_StateChange
+};
 enum EngineStateType : unsigned int
 {
 	EngineStateType_Default = 0,

+ 1 - 0
Praxis3D/Source/Config.h

@@ -561,6 +561,7 @@ class Config
 	friend class DeferredRenderer;
 	friend class EditorState;
 	friend class EditorWindow;
+	friend class Engine;
 	friend class ErrorHandler;
 	friend class LuaScript;
 	friend class RendererFrontend;

+ 18 - 0
Praxis3D/Source/Containers.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <string>
+
 #include "CommonDefinitions.h"
 #include "Math.h"
 
@@ -165,4 +167,20 @@ struct DoubleBufferedContainer
 	bool m_swapFlag;
 
 	T_Object m_buffers[2];
+};
+
+// Stores an engine change type and all associated data needed for that change
+struct EngineChangeData
+{
+	EngineChangeData() : m_changeType(EngineChangeType::EngineChangeType_None), m_engineStateType(EngineStateType::EngineStateType_Default) { }
+	EngineChangeData(EngineChangeType p_changeType, EngineStateType p_engineStateType = EngineStateType::EngineStateType_Default, std::string p_filename = "") : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_filename(p_filename) { }
+	~EngineChangeData() { }
+
+	void setChangeType(const EngineChangeType p_changeType) { m_changeType = p_changeType; }
+	void setEngineStateType(const EngineStateType p_engineStateType) { m_engineStateType = p_engineStateType; }
+	void setFilename(const std::string &p_filename) { m_filename = p_filename; }
+
+	EngineChangeType m_changeType;
+	EngineStateType m_engineStateType;
+	std::string m_filename;
 };

+ 13 - 10
Praxis3D/Source/EditorState.cpp

@@ -9,27 +9,30 @@
 
 EditorState::EditorState(Engine &p_engine) : EngineState(p_engine, EngineStateType::EngineStateType_Editor)
 {
+	m_sceneFilename = Config::gameplayVar().play_map;
 }
 
 EditorState::~EditorState()
 {
 }
 
-ErrorCode EditorState::init(TaskManager *p_taskManager)
+ErrorCode EditorState::load()
 {
-	ErrorCode returnError = EngineState::init(p_taskManager);
+	ErrorCode returnError = ErrorCode::Initialize_failure;
 
-	if(returnError == ErrorCode::Success)
+	if(m_initialized)
 	{
-		// Load the default map, and log an error if it wasn't successful
-		returnError = m_sceneLoader.loadFromFile(Config::gameplayVar().play_map);
-		
+		// Load the scene map, and log an error if it wasn't successful
+		returnError = m_sceneLoader.loadFromFile(m_sceneFilename);
 		if(returnError != ErrorCode::Success)
+		{
 			ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
-
-		m_sceneLoader.getChangeController()->sendData(static_cast<GUIScene *>(m_sceneLoader.getSystemScene(Systems::GUI)), DataType::DataType_EditorWindow, (void *)&m_editorWindowSettings);
-
-		m_initialized = true;
+		}
+		else
+		{
+			// Tell the GUI scene to create the editor window
+			m_sceneLoader.getChangeController()->sendData(static_cast<GUIScene *>(m_sceneLoader.getSystemScene(Systems::GUI)), DataType::DataType_EditorWindow, (void *)&m_editorWindowSettings);
+		}
 	}
 
 	return returnError;

+ 2 - 1
Praxis3D/Source/EditorState.h

@@ -11,7 +11,8 @@ public:
 	EditorState(Engine &p_engine);
 	~EditorState();
 
-	ErrorCode init(TaskManager *p_taskManager);
+	ErrorCode load();
+
 	void update(Engine &p_engine);
 
 	void activate();

+ 15 - 2
Praxis3D/Source/EditorWindow.cpp

@@ -29,8 +29,12 @@
 
 EditorWindow::~EditorWindow()
 {
-    // Tell the renderer to draw the scene to the screen
-    m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_RenderToTexture, (void *)false);
+    // If engine is still running
+    if(Config::engineVar().running == true)
+    {
+        // Tell the renderer to draw the scene to the screen
+        //m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_RenderToTexture, (void *)false);
+    }
 }
 
 ErrorCode EditorWindow::init()
@@ -194,6 +198,11 @@ void EditorWindow::update(const float p_deltaTime)
                     m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
                 }
             }
+            if(ImGui::MenuItem("Reload scene")) 
+            {
+                // Send a notification to the engine to reload the current engine state
+                m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneReload));
+            }
 
             ImGui::Separator();
 
@@ -1381,6 +1390,10 @@ void EditorWindow::update(const float p_deltaTime)
                     if(m_fileBrowserDialog.m_success)
                     {
                         //m_systemScene->getSceneLoader()->loadFromFile(m_fileBrowserDialog.m_filename);
+                        // Send a notification to the engine to reload the current engine state
+                        m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneFilename, EngineStateType::EngineStateType_Editor, m_fileBrowserDialog.m_filename));
+                        m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneReload));
+
                     }
 
                     // Reset the file browser and mark the file browser as not opened

+ 84 - 13
Praxis3D/Source/Engine.cpp

@@ -21,7 +21,7 @@ int Engine::m_instances = 0;
 
 #define ENUMTOSTRING(ENUM) #ENUM
 
-Engine::Engine() : m_mainMenuState(*this), m_playstate(*this), m_editorState(*this)
+Engine::Engine() //m_mainMenuState(*this), m_playstate(*this), m_editorState(*this)
 {
 	m_instances++;
 	m_initialized = false;
@@ -34,12 +34,21 @@ Engine::Engine() : m_mainMenuState(*this), m_playstate(*this), m_editorState(*th
 	m_changeCtrlScene = nullptr;
 	m_sceneChangeController = nullptr;
 	m_objectChangeController = nullptr;
-	m_currentState = nullptr;
 	m_currentStateType = Config::engineVar().engineState;
+
+	for(unsigned int i = 0; i < EngineStateType::EngineStateType_NumOfTypes; i++)
+		m_engineStates[i] = nullptr;
+
+	for(unsigned int i = 0; i < Systems::NumberOfSystems; i++)
+		m_systems[i] = nullptr;
 }
 
 Engine::~Engine()
 {
+	for(unsigned int i = 0; i < EngineStateType::EngineStateType_NumOfTypes; i++)
+		if(m_engineStates[i] != nullptr)
+			delete m_engineStates[i];
+
 	// Delete systems
 	delete m_taskManager;
 	delete m_errorHandler;
@@ -71,8 +80,8 @@ ErrorCode Engine::init()
 	// |	ENGINE STATE INITIALIZATION	   |
 	// |___________________________________|
 	// Set and initialize the current engine state
-	setCurrentStateType();
-	if(m_currentState == nullptr || !m_currentState->isInitialized())
+	setCurrentStateType(m_currentStateType);
+	if(m_engineStates[m_currentStateType] == nullptr || !m_engineStates[m_currentStateType]->isInitialized())
 		return ErrorCode::Failure;
 
 	// If this point is reached, all initializations passed, mark the engine as initialized
@@ -99,15 +108,17 @@ void Engine::run()
 		// If engine is still running
 		if(Config::engineVar().running == true)
 		{
+			processEngineChanges();
+
 			// Load a different engine state, if it has been changed
-			if(m_currentStateType != Config::engineVar().engineState)
-			{
-				m_currentStateType = Config::engineVar().engineState;
-				setCurrentStateType();
-			}
+			//if(m_currentStateType != Config::engineVar().engineState)
+			//{
+			//	m_currentStateType = Config::engineVar().engineState;
+			//	setCurrentStateType();
+			//}
 
 			// Call update on the current engine state
-			m_currentState->update(*this);
+			m_engineStates[m_currentStateType]->update(*this);
 
 			// Swap buffers. If v-sync is enabled, this call should halt for appropriate time
 			m_window->swapBuffers();
@@ -123,6 +134,64 @@ void Engine::run()
 	shutdown();
 }
 
+void Engine::processEngineChanges()
+{
+	// Check if there are any engine changes
+	auto changeControllerScene = m_engineStates[m_currentStateType]->getChangeControllerScene();
+	if(changeControllerScene->getEngineChangePending())
+	{
+		// Go over each engine change
+		auto &engineChanges = changeControllerScene->getEngineChangeQueue();
+		for(auto const &change : engineChanges)
+		{
+			switch(change.m_changeType)
+			{
+				case EngineChangeType_SceneFilename:
+					{
+						m_engineStates[change.m_engineStateType]->setSceneFilename(change.m_filename);
+					}
+					break;
+				case EngineChangeType_SceneLoad:
+					{
+						if(m_engineStates[change.m_engineStateType] != nullptr)
+						{
+							// Load the scene
+							ErrorCode loadError = m_engineStates[change.m_engineStateType]->load();
+
+							// If it failed to load, log an error
+							if(loadError != ErrorCode::Success)
+								ErrHandlerLoc::get().log(loadError, getEngineStateTypeString(change.m_engineStateType), ErrorSource::Source_Engine);
+						}
+						else
+							ErrHandlerLoc::get().log(ErrorCode::Initialize_failure, getEngineStateTypeString(change.m_engineStateType), ErrorSource::Source_Engine);
+					}
+					break;
+				case EngineChangeType_SceneReload:
+					{
+						// Delete the current scene
+						if(m_engineStates[m_currentStateType] != nullptr)
+						{
+							delete m_engineStates[m_currentStateType];
+							m_engineStates[m_currentStateType] = nullptr;
+						}
+
+						setCurrentStateType(m_currentStateType);
+						return;
+					}
+					break;
+				case EngineChangeType_StateChange:
+					{
+						setCurrentStateType(change.m_engineStateType);
+					}
+					break;
+			}
+		}
+
+		// Mark engine changes as being processed by clearing the queue
+		changeControllerScene->clearEngineChangeQueue();
+	}
+}
+
 ErrorCode Engine::initServices()
 {
 	ErrorCode returnError = ErrorCode::Success;
@@ -152,10 +221,10 @@ ErrorCode Engine::initServices()
 		ErrHandlerLoc::provide(m_errorHandler);
 	}
 	else
-		printf("Error: Error handler has failed to initialize. Error code: %i\n", errHandlerError);
+		printf("Error: Error handler has failed to initialize. Error code: %s\n", GetString(errHandlerError));
 
 	//  ___________________________________
-	// |								   |
+	// |								   |n
 	// |    SET CONFIGURATION VARIABLES    |
 	// |___________________________________|
 	// Initialize configuration variables
@@ -386,7 +455,9 @@ ErrorCode Engine::initSystems()
 void Engine::shutdown()
 {
 	// Shutdown engine states
-	m_playstate.shutdown();
+	for(unsigned int i = 0; i < EngineStateType::EngineStateType_NumOfTypes; i++)
+		if(m_engineStates[i] != nullptr)
+			m_engineStates[i]->shutdown();
 
 	// Cancel all the tasks in background threads
 	m_taskManager->cancelBackgroundThreads();

+ 75 - 35
Praxis3D/Source/Engine.h

@@ -26,57 +26,58 @@ public:
 	void run();
 
 private:
+	// Gets engine changes (if there are any) from the Universal scene of the current Engine State and process them
+	void processEngineChanges();
+
 	// Get all engine systems. Return a pointer to an array the size of Systems::NumberOfSystems
 	SystemBase **getSystems() { return m_systems; }
 
 	// Sets which engine state is currently active
-	void setCurrentStateType()
+	void setCurrentStateType(EngineStateType p_newStateType)
 	{
-		EngineState *previousState = m_currentState;
-
-		// Set the current state
-		switch(m_currentStateType)
-		{
-		case EngineStateType_MainMenu:
-			m_currentState = &m_mainMenuState;
-			break;
+		// Track whether the state failed to change
+		bool changeState = true;
 
-		case EngineStateType_Play:
-			m_currentState = &m_playstate;
-			break;
-
-		case EngineStateType_Editor:
-			m_currentState = &m_editorState;
-			break;
-
-		default:
-			m_currentStateType = m_currentState->getEngineStateType();
-			break;
-		}
+		// Create the state if it hasn't been created already
+		if(m_engineStates[p_newStateType] == nullptr)
+			createState(&m_engineStates[p_newStateType], p_newStateType);
 
-		if(!m_currentState->isInitialized())
+		if(!m_engineStates[p_newStateType]->isInitialized())
 		{
 			// Initialize the current state
-			ErrorCode stateInitError = m_currentState->init(m_taskManager);
+			ErrorCode stateInitError = m_engineStates[p_newStateType]->init(m_taskManager);
 
 			// If it failed to initialize, return to the previous state and log an error
 			if(stateInitError != ErrorCode::Success)
 			{
-				m_currentState = previousState;
-				if(m_currentState != nullptr)
-					m_currentStateType = m_currentState->getEngineStateType();
+				changeState = false;
 				ErrHandlerLoc::get().log(stateInitError, ErrorSource::Source_Engine);
 			}
+			else
+			{
+				// Load the scene
+				ErrorCode loadError = m_engineStates[p_newStateType]->load();
+
+				// If it failed to load, log an error
+				if(loadError != ErrorCode::Success)
+				{
+					changeState = false;
+					ErrHandlerLoc::get().log(loadError, getEngineStateTypeString(m_currentStateType), ErrorSource::Source_Engine);
+				}
+			}
 		}
 
 		// If the state changed, deactivate the previous one, and activate the new one
-		if(m_currentState != previousState)
+		if(changeState)
 		{
-			if(previousState != nullptr)
-				previousState->deactivate();
+			if(m_engineStates[m_currentStateType] != nullptr)
+				m_engineStates[m_currentStateType]->deactivate();
+
+			if(m_engineStates[p_newStateType] != nullptr)
+				m_engineStates[p_newStateType]->activate();
 
-			if(m_currentState != nullptr)
-				m_currentState->activate();
+			m_currentStateType = p_newStateType;
+			Config::m_engineVar.engineState = m_currentStateType;
 		}
 	}
 
@@ -89,14 +90,53 @@ private:
 	// Shuts all the systems, etc. down, called before returning from run()
 	void shutdown();
 
+	// Creates an engine state of the given type
+	void createState(EngineState **p_currentState, EngineStateType p_engineStateType)
+	{
+		if(*p_currentState == nullptr)
+			switch(p_engineStateType)
+			{
+				case EngineStateType_MainMenu:
+					*p_currentState = new MainMenuState(*this);
+					break;
+
+				case EngineStateType_Play:
+					*p_currentState = new PlayState(*this);
+					break;
+
+				case EngineStateType_Editor:
+					*p_currentState = new EditorState(*this);
+					break;
+			}
+	}
+
+	// Converts EngineStateType into a text string
+	const inline std::string getEngineStateTypeString(EngineStateType p_engineStateType) const
+	{
+		std::string returnString = "Invalid engine state";
+
+		switch(p_engineStateType)
+		{
+			case EngineStateType_MainMenu:
+				returnString = "Main menu state";
+				break;
+			case EngineStateType_Play:
+				returnString = "Play state";
+				break;
+			case EngineStateType_Editor:
+				returnString = "Editor state";
+				break;
+		}
+
+		return returnString;
+	}
+
 	// Currently being executed state
-	EngineState *m_currentState;
+	//EngineState *m_currentState;
 	EngineStateType m_currentStateType;
 
 	// Different execution states
-	MainMenuState m_mainMenuState;
-	PlayState m_playstate;
-	EditorState m_editorState;
+	EngineState *m_engineStates[EngineStateType::EngineStateType_NumOfTypes];
 
 	// All engine systems
 	SystemBase *m_systems[Systems::NumberOfSystems];

+ 9 - 5
Praxis3D/Source/EngineState.cpp

@@ -10,14 +10,11 @@
 
 EngineState::EngineState(Engine &p_engine, EngineStateType p_engineState) : m_engine(p_engine), m_engineStateType(p_engineState), m_initialized(false)
 {
-	m_sceneChangeController = new ChangeController();
-	m_objectChangeController = new ChangeController();
+	m_sceneChangeController = nullptr;
+	m_objectChangeController = nullptr;
 
 	m_scheduler = nullptr;
 	m_changeCtrlScene = nullptr;
-
-	//for(int i = 0; i < Systems::NumberOfSystems; i++)
-	//	m_systems[i] = nullptr;
 }
 
 EngineState::~EngineState()
@@ -36,6 +33,10 @@ ErrorCode EngineState::init(TaskManager *p_taskManager)
 	if(m_initialized)
 		shutdown();
 
+	// Create change controllers
+	m_sceneChangeController = new ChangeController();
+	m_objectChangeController = new ChangeController();
+
 	// Get all engine systems
 	auto systems = m_engine.getSystems();
 
@@ -70,7 +71,10 @@ ErrorCode EngineState::init(TaskManager *p_taskManager)
 		returnError = m_objectChangeController->setTaskManager(p_taskManager);
 
 		if(returnError == ErrorCode::Success)
+		{
 			returnError = m_sceneChangeController->setTaskManager(p_taskManager);
+			m_initialized = true;
+		}
 	}
 
 	return returnError;

+ 8 - 0
Praxis3D/Source/EngineState.h

@@ -18,6 +18,8 @@ public:
 
 	virtual ErrorCode init(TaskManager *p_taskManager);
 
+	virtual ErrorCode load() = 0;
+
 	virtual void update(Engine &p_engine) = 0;
 
 	virtual void activate();
@@ -28,8 +30,12 @@ public:
 
 	const inline EngineStateType getEngineStateType() { return m_engineStateType; }
 
+	inline UniversalScene *getChangeControllerScene() { return m_changeCtrlScene; }
+
 	const inline bool isInitialized() const { return m_initialized; }
 
+	inline void setSceneFilename(const std::string &p_filename) { m_sceneFilename = p_filename; }
+
 protected:
 	bool m_initialized;
 	EngineStateType m_engineStateType;
@@ -47,5 +53,7 @@ protected:
 	// Subject - observer messaging systems
 	ChangeController *m_sceneChangeController;
 	ChangeController *m_objectChangeController;
+
+	std::string m_sceneFilename;
 };
 

+ 3 - 0
Praxis3D/Source/GUISystem.h

@@ -73,7 +73,10 @@ public:
 	void deleteScene(EngineStateType p_engineState)
 	{
 		if(m_GUIScenes[p_engineState] != nullptr)
+		{
 			delete m_GUIScenes[p_engineState];
+			m_GUIScenes[p_engineState] = nullptr;
+		}
 	}
 
 protected:

+ 1 - 1
Praxis3D/Source/LuaScript.cpp

@@ -254,7 +254,7 @@ void LuaScript::setFunctions()
 
 	// Engine functions
 	m_luaState.set_function("setEngineRunning", [](const bool p_v1) -> const void {Config::m_engineVar.running = p_v1; });
-	m_luaState.set_function("setEngineState", [](const EngineStateType p_v1) -> const void {Config::m_engineVar.engineState = p_v1; });
+	m_luaState.set_function("setEngineState", [this](const EngineStateType p_v1) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_StateChange, p_v1)); });
 
 	// GUI functions
 	auto GUITable = m_luaState.create_table("GUI");

+ 1 - 17
Praxis3D/Source/MainMenuState.cpp

@@ -10,29 +10,13 @@
 
 MainMenuState::MainMenuState(Engine &p_engine) : EngineState(p_engine, EngineStateType::EngineStateType_MainMenu)
 {
+	m_sceneFilename = Config::gameplayVar().main_menu_map;
 }
 
 MainMenuState::~MainMenuState()
 {
 }
 
-ErrorCode MainMenuState::init(TaskManager *p_taskManager)
-{
-	ErrorCode returnError = EngineState::init(p_taskManager);
-
-	if(returnError == ErrorCode::Success)
-	{
-		// Load the default map, and log an error if it wasn't successful
-		returnError = m_sceneLoader.loadFromFile(Config::gameplayVar().main_menu_map);
-		if(returnError != ErrorCode::Success)
-			ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
-
-		m_initialized = true;
-	}
-
-	return returnError;
-}
-
 void MainMenuState::update(Engine &p_engine)
 {
 	m_scheduler->execute(ClockLocator::get().getDeltaSecondsF());

+ 15 - 1
Praxis3D/Source/MainMenuState.h

@@ -11,7 +11,21 @@ public:
 	MainMenuState(Engine &p_engine);
 	~MainMenuState();
 
-	ErrorCode init(TaskManager *p_taskManager);
+	ErrorCode load()
+	{
+		ErrorCode returnError = ErrorCode::Initialize_failure;
+
+		if(m_initialized)
+		{
+			// Load the scene map, and log an error if it wasn't successful
+			returnError = m_sceneLoader.loadFromFile(m_sceneFilename);
+			if(returnError != ErrorCode::Success)
+				ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
+		}
+
+		return returnError;
+	}
+
 	void update(Engine &p_engine);
 
 private:

+ 3 - 0
Praxis3D/Source/PhysicsSystem.h

@@ -70,7 +70,10 @@ public:
 	void deleteScene(EngineStateType p_engineState)
 	{
 		if(m_physicsScenes[p_engineState] != nullptr)
+		{
 			delete m_physicsScenes[p_engineState];
+			m_physicsScenes[p_engineState] = nullptr;
+		}
 	}
 
 protected:

+ 1 - 17
Praxis3D/Source/PlayState.cpp

@@ -10,29 +10,13 @@
 
 PlayState::PlayState(Engine &p_engine) : EngineState(p_engine, EngineStateType::EngineStateType_Play)
 {
+	m_sceneFilename = Config::gameplayVar().play_map;
 }
 
 PlayState::~PlayState()
 {
 }
 
-ErrorCode PlayState::init(TaskManager *p_taskManager)
-{
-	ErrorCode returnError = EngineState::init(p_taskManager);
-	
-	if(returnError == ErrorCode::Success)
-	{
-		// Load the default map, and log an error if it wasn't successful
-		returnError = m_sceneLoader.loadFromFile(Config::gameplayVar().play_map);
-		if(returnError != ErrorCode::Success)
-			ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
-
-		m_initialized = true;
-	}
-
-	return returnError;
-}
-
 void PlayState::update(Engine &p_engine)
 {
 	m_scheduler->execute<>(std::function<void(SystemTask *, float)>(&SystemTask::update), ClockLocator::get().getDeltaSecondsF());

+ 15 - 1
Praxis3D/Source/PlayState.h

@@ -11,7 +11,21 @@ public:
 	PlayState(Engine &p_engine);
 	~PlayState();
 
-	ErrorCode init(TaskManager *p_taskManager);
+	ErrorCode load()
+	{
+		ErrorCode returnError = ErrorCode::Initialize_failure;
+
+		if(m_initialized)
+		{
+			// Load the scene map, and log an error if it wasn't successful
+			returnError = m_sceneLoader.loadFromFile(m_sceneFilename);
+			if(returnError != ErrorCode::Success)
+				ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
+		}
+
+		return returnError;
+	}
+
 	void update(Engine &p_engine);
 
 private:

+ 3 - 0
Praxis3D/Source/RendererSystem.cpp

@@ -171,5 +171,8 @@ SystemScene *RendererSystem::getScene(EngineStateType p_engineState)
 void RendererSystem::deleteScene(EngineStateType p_engineState)
 {
 	if(m_rendererScenes[p_engineState] != nullptr)
+	{
 		delete m_rendererScenes[p_engineState];
+		m_rendererScenes[p_engineState] = nullptr;
+	}
 }

+ 3 - 0
Praxis3D/Source/SceneLoader.h

@@ -51,6 +51,9 @@ public:
 	// Returns the last loaded scene filename
 	const std::string &getSceneFilename() const { return m_filename; }
 
+	// Set the scene filename that is used when loading from file
+	inline void setSceneFilename(const std::string &p_filename) { m_filename = p_filename; }
+
 private:
 	ErrorCode importFromFile(ComponentsConstructionInfo &p_constructionInfo, const std::string &p_filename);
 	void importFromProperties(ComponentsConstructionInfo &p_constructionInfo, const PropertySet &p_properties);

+ 3 - 0
Praxis3D/Source/ScriptSystem.cpp

@@ -71,5 +71,8 @@ SystemScene *ScriptSystem::getScene(EngineStateType p_engineState)
 void ScriptSystem::deleteScene(EngineStateType p_engineState)
 {
 	if(m_scriptingScenes[p_engineState] != nullptr)
+	{
 		delete m_scriptingScenes[p_engineState];
+		m_scriptingScenes[p_engineState] = nullptr;
+	}
 }

+ 12 - 1
Praxis3D/Source/Universal.cpp

@@ -4,7 +4,8 @@
 UniversalScene::UniversalScene(ChangeController *p_sceneChangeController, ChangeController *p_objectChangeController) : 
 								Observer(Properties::PropertyID::UniversalScene),
 								m_sceneChangeController(p_sceneChangeController), 
-								m_objectChangeController(p_objectChangeController)
+								m_objectChangeController(p_objectChangeController),
+								m_engineChangePending(false)
 {
 
 }
@@ -226,6 +227,16 @@ void UniversalScene::removeObjectLink(ObservedSubject *p_subject, SystemObject *
 	m_objectChangeController->unregisterSubject(p_subject, p_observer);
 }
 
+void UniversalScene::sendEngineChange(EngineChangeData &p_engineChangeData)
+{
+	// Make sure calls from other threads are locked, while current call is in progress
+	// This is needed so the changes list isn't being written to simultaneously from different threads
+	SpinWait::Lock lock(m_mutex);
+
+	m_engineChangeQueue.push_back(p_engineChangeData);
+	m_engineChangePending = true;
+}
+
 void UniversalScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changes)
 {
 	switch(p_changes)

+ 19 - 2
Praxis3D/Source/Universal.h

@@ -6,6 +6,7 @@
 
 #include "ChangeController.h"
 #include "ObserverBase.h"
+#include "SpinWait.h"
 #include "System.h"
 
 class UniversalObject;
@@ -31,6 +32,9 @@ public:
 	// Removes the link between the subject and the observer, so that the observer will no longer be notified of any data changes within the subject
 	void removeObjectLink(ObservedSubject *p_subject, SystemObject *p_observer);
 
+	// Queues an engine change that is processed before the next frame by the Engine
+	void sendEngineChange(EngineChangeData &p_engineChangeData);
+
 	// Sends a one-off notification about a change, without requiring the object linking
 	void sendChange(SystemObject *p_subject, SystemObject *p_observer, BitMask p_changedBits)
 	{
@@ -47,8 +51,16 @@ public:
 
 	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changes);
 
-	const std::list<UniversalObject*> &getObjects() const { return m_objects; }
-	const std::map<BitMask, SystemScene*> &getSystemScenes() const { return m_systemScenes; }
+	const inline std::list<UniversalObject*> &getObjects() const { return m_objects; }
+	const inline std::map<BitMask, SystemScene*> &getSystemScenes() const { return m_systemScenes; }
+	const inline std::list<EngineChangeData> &getEngineChangeQueue() const { return m_engineChangeQueue; }
+	const inline bool getEngineChangePending() const { return m_engineChangePending; }
+
+	void inline clearEngineChangeQueue()
+	{
+		m_engineChangeQueue.clear();
+		m_engineChangePending = false;
+	}
 
 	typedef std::map<BitMask, SystemScene*> SystemSceneMap;
 	typedef std::list<UniversalObject*>		UniversalObjectList;
@@ -71,6 +83,11 @@ protected:
 	ObjectLinkList		m_objectLinks;
 	UniversalObjectList	m_objects;
 
+	SpinWait m_mutex;
+
+	bool m_engineChangePending;
+	std::list<EngineChangeData> m_engineChangeQueue;
+
 public:
 	const ObjectLinkList &getObjectLinksList() { return m_objectLinks; }
 };

+ 3 - 0
Praxis3D/Source/WorldSystem.h

@@ -74,7 +74,10 @@ public:
 	void deleteScene(EngineStateType p_engineState)
 	{
 		if(m_worldScenes[p_engineState] != nullptr)
+		{
 			delete m_worldScenes[p_engineState];
+			m_worldScenes[p_engineState] = nullptr;
+		}
 	}
 
 protected:

+ 1 - 1
Praxis3D/imgui.ini

@@ -4,7 +4,7 @@ Size=400,400
 Collapsed=0
 
 [Window][Dear ImGui Demo]
-Pos=465,156
+Pos=85,275
 Size=507,676
 Collapsed=0