Browse Source

Implemented new scene creation in EditorWindow
Added rendering pass exporting to RendererScene
Added manual texture entry wrapping in asset window in EditorWindow
Added models and shaders in asset window in EditorWindow
Added scene settings container and configuration in EditorWindow
Added save scene functionality in EditorWindow
Added audio bank file browser in EditorWindow
Added volume change handling in AudioScene and EditorWindow
Added gravity change handling in PhysicsScene
Added separate methods for initialize and load engine states in Engine
Added loaded audio bank tracking in AudioSystem
Added loaded event description tracking in AudioSystem
Added render pass type strings in CommonDefinitions
Added an assignment operator for PropertySet so it can be copied
Added a way to load a scene using PropertySet instead of just the scene filename in EditorState and SceneLoader
Added a way to get and set the load-in-background flag in SceneLoader
Added a templated method for sending engine changes in Universal
Added scene properties data in EngineChangeData container in Containers
Added sendEngineChange callback and Engine Change Type enums for lua scripts in LuaScript
Added conversions between Quaternion and Vec4 in Math
Added lua scripts for editor camera and sun movement
Added more config variables in Config
Added more change types in Config
Fixed a bug of trying to set a filename of an uninitialized engine state in Engine
Fixed a bug of setting quaternion components (.w) in the wrong order when converting from Vec4 to Quaternion in Math
Fixed a bug of wrongly using the Rendering propertyID instead of Graphics for graphics components in SceneLoader

Paul A 2 years ago
parent
commit
574f1dcaf7
43 changed files with 1816 additions and 470 deletions
  1. 11 11
      Praxis3D/Data/Maps/componentTest.pmap
  2. 1 15
      Praxis3D/Data/Prefabs/cube1.prefab
  3. 1 1
      Praxis3D/Data/Prefabs/cubeMetalPaintedChips.prefab
  4. 120 0
      Praxis3D/Data/Scripts/Editor_free_camera.lua
  5. 10 4
      Praxis3D/Data/Scripts/MainMenu_image_buttons.lua
  6. 46 0
      Praxis3D/Data/Scripts/Sun_move.lua
  7. 96 67
      Praxis3D/Source/AudioScene.cpp
  8. 5 6
      Praxis3D/Source/AudioScene.h
  9. 37 10
      Praxis3D/Source/AudioSystem.cpp
  10. 23 5
      Praxis3D/Source/AudioSystem.h
  11. 1 0
      Praxis3D/Source/CommonDefinitions.cpp
  12. 15 16
      Praxis3D/Source/CommonDefinitions.h
  13. 8 2
      Praxis3D/Source/Config.cpp
  14. 25 5
      Praxis3D/Source/Config.h
  15. 5 2
      Praxis3D/Source/Containers.h
  16. 26 2
      Praxis3D/Source/EditorState.cpp
  17. 2 0
      Praxis3D/Source/EditorState.h
  18. 751 20
      Praxis3D/Source/EditorWindow.cpp
  19. 114 4
      Praxis3D/Source/EditorWindow.h
  20. 38 14
      Praxis3D/Source/Engine.cpp
  21. 80 0
      Praxis3D/Source/Engine.h
  22. 1 1
      Praxis3D/Source/EngineState.cpp
  23. 3 0
      Praxis3D/Source/EngineState.h
  24. 1 0
      Praxis3D/Source/Filesystem.h
  25. 26 0
      Praxis3D/Source/GUIScene.h
  26. 11 0
      Praxis3D/Source/LuaScript.cpp
  27. 21 2
      Praxis3D/Source/MainMenuState.h
  28. 3 4
      Praxis3D/Source/Math.h
  29. 8 0
      Praxis3D/Source/PhysicsScene.cpp
  30. 3 1
      Praxis3D/Source/PhysicsScene.h
  31. 21 2
      Praxis3D/Source/PlayState.h
  32. 25 2
      Praxis3D/Source/PropertySet.h
  33. 0 94
      Praxis3D/Source/RendererFrontend.cpp
  34. 3 1
      Praxis3D/Source/RendererScene.cpp
  35. 48 1
      Praxis3D/Source/RendererScene.h
  36. 0 38
      Praxis3D/Source/RendererSystem.cpp
  37. 79 110
      Praxis3D/Source/SceneLoader.cpp
  38. 9 0
      Praxis3D/Source/SceneLoader.h
  39. 3 1
      Praxis3D/Source/ShaderLoader.h
  40. 0 10
      Praxis3D/Source/Universal.cpp
  41. 23 3
      Praxis3D/Source/Universal.h
  42. 1 1
      Praxis3D/Source/Utilities.h
  43. 112 15
      Praxis3D/imgui.ini

+ 11 - 11
Praxis3D/Data/Maps/componentTest.pmap

@@ -109,7 +109,7 @@
 					"Mass": "0.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"ModelComponent":
 				{
@@ -255,7 +255,7 @@
 					"LocalScale": "0.05f, 0.05f, 0.05f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"ModelComponent":
 				{
@@ -317,7 +317,7 @@
 					"LocalRotation": "0.0f, 0.0f, 0.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"ModelComponent":
 				{
@@ -364,7 +364,7 @@
 					"LocalScale": "4.0f, 4.0f, 4.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"ModelComponent":
 				{
@@ -411,7 +411,7 @@
 					"LocalScale": "4.0f, 4.0f, 4.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"ModelComponent":
 				{
@@ -464,7 +464,7 @@
 					"LocalRotation": "30.0f, 0.0f, 0.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"CameraComponent":
 				{
@@ -501,7 +501,7 @@
 					"LocalRotation": "-40.0f, 0.0f, 0.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"LightComponent":
 				{
@@ -529,7 +529,7 @@
 					"LocalPosition": "20.0f, 2.0f, 20.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"LightComponent":
 				{
@@ -550,7 +550,7 @@
 					"LocalPosition": "-20.0f, 2.0f, 20.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"LightComponent":
 				{
@@ -571,7 +571,7 @@
 					"LocalPosition": "20.0f, 2.0f, -20.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"LightComponent":
 				{
@@ -592,7 +592,7 @@
 					"LocalPosition": "-20.0f, 2.0f, -20.0f"
 				}
 			},
-			"Rendering":
+			"Graphics":
 			{
 				"LightComponent":
 				{

+ 1 - 15
Praxis3D/Data/Prefabs/cube1.prefab

@@ -7,7 +7,7 @@
 			"Type": "Wood"
 		}
 	},
-	"Rendering":
+	"Graphics":
 	{
 		"ModelComponent":
 		{
@@ -49,19 +49,5 @@
 				}
 			]
 		}
-	},
-	"Physics":
-	{
-		"RigidBodyComponent":
-		{
-			"Mass": "10.0f",
-			"Restitution": "0.5f",
-			"Friction": "0.5f",
-			"CollisionShape":
-			{
-				"Type": "Box",
-				"Size": "1.0f, 1.0f, 1.0f"
-			}
-		}
 	}
 }

+ 1 - 1
Praxis3D/Data/Prefabs/cubeMetalPaintedChips.prefab

@@ -14,7 +14,7 @@
 			"LocalScale": "2.0f, 2.0f, 2.0f"
 		}
 	},
-	"Rendering":
+	"Graphics":
 	{
 		"ModelComponent":
 		{

+ 120 - 0
Praxis3D/Data/Scripts/Editor_free_camera.lua

@@ -0,0 +1,120 @@
+
+function init ()
+	-- Create needed variables
+	create(Types.GameplayVariables, 'gameplayVariables');
+	create(Types.InputVariables, 'inputVariables');
+	create(Types.SpatialDataManager, 'spatialData');
+	
+	-- Create key commands, used to track pressed keys
+	create(Types.KeyCommand, 'forwardKey')
+	create(Types.KeyCommand, 'backwardKey')
+	create(Types.KeyCommand, 'leftKey')
+	create(Types.KeyCommand, 'rightKey')
+	create(Types.KeyCommand, 'upKey')
+	create(Types.KeyCommand, 'downKey')
+	create(Types.KeyCommand, 'sprintKey')
+	create(Types.KeyCommand, 'mouseLeftKey')
+	create(Types.KeyCommand, 'mouseRightKey')
+	
+	-- Bind keys to their corresponding buttons on the keyboard
+	forwardKey:bind(inputVariables.forward_key)
+	backwardKey:bind(inputVariables.backward_key)
+	leftKey:bind(inputVariables.left_strafe_key)
+	rightKey:bind(inputVariables.right_strafe_key)
+	upKey:bind(inputVariables.up_key)
+	downKey:bind(inputVariables.down_key)
+	sprintKey:bind(inputVariables.sprint_key)
+	mouseLeftKey:bindByName('Mouse_left')
+	mouseRightKey:bindByName('Mouse_right')
+	
+	-- Get the camera movement speed
+	if cameraSpeed then 
+		movementSpeedF = cameraSpeed
+	else
+		movementSpeedF = gameplayVariables.camera_freelook_speed
+	end
+	
+	-- Get the camera movement speed multiplier
+	if cameraSpeedMultiplier then 
+		movementSpeedMultF = cameraSpeedMultiplier
+	else
+		movementSpeedMultF = 1.0
+	end
+	
+	ErrHandlerLoc.logErrorCode(ErrorCode.Initialize_success, getLuaFilename())
+end
+
+function update (p_deltaTime)
+	-- Get current mouse data
+	mouseData = getMouseInfo()
+	
+	-- Get current spatial data and its inverse
+	localTransformMat4 = spatialData:getLocalTransform()
+	localTransformInverseMat4 = localTransformMat4:inverse()
+	
+	-- Extract position from spatial data
+	positionVec3 = localTransformMat4:getPosVec3()
+	
+	-- Calculate new view angles based on mouse movement
+	horizontalAngleF = mouseData.m_movementX * inputVariables.mouse_jaw * inputVariables.mouse_sensitivity
+	verticalAngleF = mouseData.m_movementY * inputVariables.mouse_pitch * inputVariables.mouse_sensitivity
+	
+	-- Rotate the camera matrix by the view angles
+	-- Perform rotations only if the mouse is captured inside the game window
+	if mouseCaptured() or mouseRightKey:isActivated() then
+		-- Rotate camera up/down based on the X direction (left/right) of the view matrix (camera's inverse matrix)
+		localTransformMat4 = localTransformMat4:rotate(toRadianF(verticalAngleF), localTransformInverseMat4:getRotXVec3())
+		-- Rotate camera left/right on a fixed Y direction (up/down) to not introduce any roll
+		localTransformMat4 = localTransformMat4:rotate(toRadianF(horizontalAngleF), Vec3.new(0.0, 1.0, 0.0))
+	end
+	
+	-- Get the view direction that is facing forward
+	forwardDirectionVec3 = localTransformInverseMat4:getRotZVec3()
+	forwardDirectionVec3 = forwardDirectionVec3:normalize()
+	
+	-- Get the view direction that is facing to the right
+	rightDirectionVec3 = localTransformInverseMat4:getRotXVec3()
+	rightDirectionVec3 = rightDirectionVec3:normalize()
+	
+	-- Get the view direction that is facing up
+	upDirectionVec3 = localTransformInverseMat4:getRotYVec3()
+	upDirectionVec3 = upDirectionVec3:normalize()
+	
+	-- Increase movement speed if the sprint key is pressed
+	if sprintKey:isActivated() then
+		finalMovementSpeedF = movementSpeedF * movementSpeedMultF
+	else
+		finalMovementSpeedF = movementSpeedF
+	end
+	
+	-- Adjust camera position based on key presses and view direction
+	-- Forwards / backwards - Z direction
+	if forwardKey:isActivated() then
+		positionVec3 = positionVec3 - forwardDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end	
+	if backwardKey:isActivated() then
+		positionVec3 = positionVec3 + forwardDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end
+	
+	-- Left / right - X direction
+	if rightKey:isActivated() then
+		positionVec3 = positionVec3 + rightDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end	
+	if leftKey:isActivated() then
+		positionVec3 = positionVec3 - rightDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end	
+	
+	-- Up / down - Y direction
+	if upKey:isActivated() then
+		positionVec3 = positionVec3 + upDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end	
+	if downKey:isActivated() then
+		positionVec3 = positionVec3 - upDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
+	end
+	
+	-- Set the new position of the camera, and keep the W variable the same
+	localTransformMat4:setPosVec4(Vec4.new(positionVec3, localTransformMat4:getPosVec4().w))
+	
+	-- Update the camera with the new matrix
+	spatialData:setLocalTransform(localTransformMat4)
+end

+ 10 - 4
Praxis3D/Data/Scripts/MainMenu_image_buttons.lua

@@ -245,7 +245,7 @@ function update (p_deltaTime)
 	
 	-- Check if the EXIT button is pressed; close the engine if it is
 	if exitButtonPressed:isChecked() then
-		print('Exit called from MainMenu.lua')
+		ErrHandlerLoc.logErrorType(ErrorType.Info, 'Exit called from MainMenu.lua')
 		setEngineRunning(false)
 	end
 	
@@ -261,13 +261,13 @@ function update (p_deltaTime)
 	
 	-- Check if the EDITOR button is pressed; change the current engine state to EDITOR, if it is
 	if editorButtonPressed:isChecked() then
-		setEngineState(EngineStateType.Editor)
+		sendEngineChange(EngineChangeType.StateChange, EngineStateType.Editor)
 		--setMouseCapture(true)
 	end
 	
 	-- Check if the PLAY button is pressed; change the current engine state to PLAY, if it is
 	if playButtonPressed:isChecked() then
-		setEngineState(EngineStateType.Play)
+		sendEngineChange(EngineChangeType.StateChange, EngineStateType.Play)
 		setMouseCapture(true)
 	end
 	
@@ -281,7 +281,13 @@ function update (p_deltaTime)
 	
 	if fileBrowserLoadMap.m_closed then
 		if fileBrowserLoadMap.m_success then
-			print(fileBrowserLoadMap.m_filename)
+			infoText = 'Loading map: "' .. fileBrowserLoadMap.m_filename .. '"'
+			ErrHandlerLoc.logErrorType(ErrorType.Info, infoText)
+			
+			sendEngineChange(EngineChangeType.SceneFilename, EngineStateType.Play, fileBrowserLoadMap.m_filename)
+			sendEngineChange(EngineChangeType.StateChange, EngineStateType.Play)
+			
+			setMouseCapture(true)
 		end
 		fileBrowserLoadMapOpened = false
 		fileBrowserLoadMap:reset()

+ 46 - 0
Praxis3D/Data/Scripts/Sun_move.lua

@@ -0,0 +1,46 @@
+
+function init ()
+	-- Create needed variables
+	create(Types.InputVariables, 'inputVariables');
+	create(Types.SpatialDataManager, 'spatialData');
+		
+	enabled = false
+	moveSpeed = 2.0
+	
+	create(Types.KeyCommand, 'forwardKey')
+	create(Types.KeyCommand, 'backwardKey')
+	
+	forwardKey:bind(inputVariables.debug_1_key)
+	backwardKey:bind(inputVariables.debug_2_key)
+	
+	ErrHandlerLoc.logErrorCode(ErrorCode.Initialize_success, getLuaFilename())
+end
+	
+function update (p_deltaTime)
+	angleChanged = false
+	
+	spatialData:calculateLocalRotationEuler()
+	eulerAngle = spatialData:getLocalSpaceData().m_spatialData.m_rotationEuler
+	
+	if forwardKey:isActivated() then
+		eulerAngle.x = eulerAngle.x + (moveSpeed * p_deltaTime)
+		angleChanged = true
+	end	
+	
+	if backwardKey:isActivated() then
+		eulerAngle.x = eulerAngle.x - (moveSpeed * p_deltaTime)
+		angleChanged = true
+	end	
+	
+	if angleChanged then
+		spatialData:setLocalRotationEuler(eulerAngle)
+		--spatialData:update()
+		--spatialData:calculateLocalRotationQuaternion()
+	end
+	
+	--print(eulerAngle.x)
+	
+	--postChanges(Changes.GUI.Sequence)
+	
+	--print('test')
+end

+ 96 - 67
Praxis3D/Source/AudioScene.cpp

@@ -13,10 +13,10 @@ AudioScene::AudioScene(SystemBase *p_system, SceneLoader *p_sceneLoader) : Syste
 	m_coreSystem = nullptr;
 	m_studioSystem = nullptr;
 
-	m_volumeAmbient = Config::audioVar().volume_ambient;
-	m_volumeMaster = Config::audioVar().volume_master;
-	m_volumeMusic = Config::audioVar().volume_music;
-	m_volumeSoundEffects = Config::audioVar().volume_sfx;
+	m_volume[AudioBusType::AudioBusType_Ambient] = Config::audioVar().volume_ambient;
+	m_volume[AudioBusType::AudioBusType_Master] = Config::audioVar().volume_master;
+	m_volume[AudioBusType::AudioBusType_Music] = Config::audioVar().volume_music;
+	m_volume[AudioBusType::AudioBusType_SFX] = Config::audioVar().volume_sfx;
 
 	for(unsigned int i = 0; i < ObjectMaterialType::NumberOfMaterialTypes; i++)
 		m_impactEvents[i] = nullptr;
@@ -66,23 +66,22 @@ ErrorCode AudioScene::setup(const PropertySet &p_properties)
 	auto const &banksProperty = p_properties.getPropertySetByID(Properties::Banks);
 	if(banksProperty)
 	{
-		// Iterate over all game objects
+		// Iterate over all audio banks
 		for(decltype(banksProperty.getNumPropertySets()) objIndex = 0, objSize = banksProperty.getNumPropertySets(); objIndex < objSize; objIndex++)
 		{
+			// Get audio bank filename property
 			auto const &filenameProperty = banksProperty.getPropertySetUnsafe(objIndex).getPropertyByID(Properties::Filename);
 			if(filenameProperty)
 			{
+				// Get audio bank filename
 				auto filename = filenameProperty.getString();
 
-				FMOD::Studio::Bank *soundBank = nullptr;
-
-				if(AudioSystem::fmodErrorLog(m_studioSystem->loadBankFile((Config::filepathVar().sound_path + filename).c_str(), FMOD_STUDIO_LOAD_BANK_NORMAL, &soundBank), filename))
-				{
-					if(AudioSystem::fmodErrorLog(soundBank->loadSampleData(), filename))
-						m_audioSystem->addImpactSoundBank(soundBank);
+				// Try to load the audio bank
+				FMOD::Studio::Bank *soundBank = m_audioSystem->loadBankFile(Config::filepathVar().sound_path + filename, FMOD_STUDIO_LOAD_BANK_NORMAL);
 
+				// If the audio bank was loaded, add it to the banks-and-filenames array
+				if(soundBank != nullptr)
 					m_bankFilenames.push_back(std::make_pair(filename, soundBank));
-				}
 			}
 		}
 	}
@@ -96,50 +95,41 @@ ErrorCode AudioScene::setup(const PropertySet &p_properties)
 			switch(volumeProperty[i].getPropertyID())
 			{
 				case Properties::Ambient:
-					m_volumeAmbient = volumeProperty[i].getFloat();
-					Config::m_audioVar.volume_ambient = m_volumeAmbient;
+					m_volume[AudioBusType::AudioBusType_Ambient] = volumeProperty[i].getFloat();
+					Config::m_audioVar.volume_ambient = m_volume[AudioBusType::AudioBusType_Ambient];
 					break;
 				case Properties::Master:
-					m_volumeMaster = volumeProperty[i].getFloat();
-					Config::m_audioVar.volume_master = m_volumeMaster;
+					m_volume[AudioBusType::AudioBusType_Master] = volumeProperty[i].getFloat();
+					Config::m_audioVar.volume_master = m_volume[AudioBusType::AudioBusType_Master];
 					break;
 				case Properties::Music:
-					m_volumeMusic = volumeProperty[i].getFloat();
-					Config::m_audioVar.volume_music = m_volumeMusic;
+					m_volume[AudioBusType::AudioBusType_Music] = volumeProperty[i].getFloat();
+					Config::m_audioVar.volume_music = m_volume[AudioBusType::AudioBusType_Music];
 					break;
 				case Properties::SoundEffect:
-					m_volumeSoundEffects = volumeProperty[i].getFloat();
-					Config::m_audioVar.volume_sfx = m_volumeSoundEffects;
+					m_volume[AudioBusType::AudioBusType_SFX] = volumeProperty[i].getFloat();
+					Config::m_audioVar.volume_sfx = m_volume[AudioBusType::AudioBusType_SFX];
 					break;
 			}
 		}
 	}
 
+	// Find an impact sound event for each object material type
 	for(unsigned int materialTypeIndex = 0; materialTypeIndex < ObjectMaterialType::NumberOfMaterialTypes; materialTypeIndex++)
 	{
-		std::string materialTypeString = GetString(static_cast<ObjectMaterialType>(materialTypeIndex));
+		// Convert object material type to text string
+		const std::string materialTypeString = GetString(static_cast<ObjectMaterialType>(materialTypeIndex));
+
+		// Try to find a matching audio event
+		auto soundEvent = m_audioSystem->getEvent(materialTypeString);
 
-		for(auto &sound : m_audioSystem->m_impactSounds)
+		// If the impact event was found, add it to the impact events array entry for this object material type
+		if(soundEvent != nullptr)
 		{
-			if(sound.first == materialTypeString)
-			{
-				std::cout << "found sound: " << sound.first << std::endl;
-				m_impactEvents[materialTypeIndex] = sound.second;
-				break;
-			}
+			ErrHandlerLoc::get().log(ErrorType::Info, ErrorSource::Source_AudioScene, "Impact sound found: \"" + materialTypeString + "\"");
+			m_impactEvents[materialTypeIndex] = soundEvent;
 		}
 	}
-	
-	//FMOD::Studio::Bank *soundBank = nullptr;
-	//if(AudioSystem::fmodErrorLog(m_studioSystem->loadBankFile((Config::filepathVar().sound_path + "Music.bank").c_str(), FMOD_STUDIO_LOAD_BANK_NORMAL, &soundBank), "Music.bank"))
-	//{
-	//	if(AudioSystem::fmodErrorLog(soundBank->loadSampleData(), "Music.bank"))
-	//	{
-	//		std::cout << "Music.bank loaded" << std::endl;
-	//	}
-	//}
-
-	//loadParameterGUIDs();
 
 	return ErrorCode::Success;
 }
@@ -163,26 +153,26 @@ void AudioScene::exportSetup(PropertySet &p_propertySet)
 
 	// Add volume settings
 	auto &volumePropertySet = p_propertySet.addPropertySet(Properties::Volume);
-	volumePropertySet.addProperty(Properties::Ambient, m_volumeAmbient);
-	volumePropertySet.addProperty(Properties::Master, m_volumeMaster);
-	volumePropertySet.addProperty(Properties::Music, m_volumeMusic);
-	volumePropertySet.addProperty(Properties::SoundEffect, m_volumeSoundEffects);
+	volumePropertySet.addProperty(Properties::Ambient, m_volume[AudioBusType::AudioBusType_Ambient]);
+	volumePropertySet.addProperty(Properties::Master, m_volume[AudioBusType::AudioBusType_Master]);
+	volumePropertySet.addProperty(Properties::Music, m_volume[AudioBusType::AudioBusType_Music]);
+	volumePropertySet.addProperty(Properties::SoundEffect, m_volume[AudioBusType::AudioBusType_SFX]);
 
 }
 
 void AudioScene::activate()
 {
 	// Set volume of all buses
-	m_audioSystem->getBus(AudioBusType::AudioBusType_Ambient)->setVolume(m_volumeAmbient);
-	m_audioSystem->getBus(AudioBusType::AudioBusType_Master)->setVolume(m_volumeMaster);
-	m_audioSystem->getBus(AudioBusType::AudioBusType_Music)->setVolume(m_volumeMusic);
-	m_audioSystem->getBus(AudioBusType::AudioBusType_SFX)->setVolume(m_volumeSoundEffects);
+	m_audioSystem->getBus(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
+	m_audioSystem->getBus(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
+	m_audioSystem->getBus(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
+	m_audioSystem->getBus(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
 
 	// Set volume of all channel groups
-	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Ambient)->setVolume(m_volumeAmbient);
-	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Master)->setVolume(m_volumeMaster);
-	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Music)->setVolume(m_volumeMusic);
-	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_SFX)->setVolume(m_volumeSoundEffects);
+	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
+	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
+	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
+	m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
 }
 
 void AudioScene::deactivate()
@@ -411,32 +401,32 @@ void AudioScene::update(const float p_deltaTime)
 	//	|___________________________|
 	//
 	// Ambient volume
-	if(m_volumeAmbient != Config::audioVar().volume_ambient)
+	if(m_volume[AudioBusType::AudioBusType_Ambient] != Config::audioVar().volume_ambient)
 	{
-		m_volumeAmbient = Config::audioVar().volume_ambient;
-		m_audioSystem->getBus(AudioBusType::AudioBusType_Ambient)->setVolume(m_volumeAmbient);
-		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Ambient)->setVolume(m_volumeAmbient);
+		m_volume[AudioBusType::AudioBusType_Ambient] = Config::audioVar().volume_ambient;
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
 	}
 	// Master volume
-	if(m_volumeMaster != Config::audioVar().volume_master)
+	if(m_volume[AudioBusType::AudioBusType_Master] != Config::audioVar().volume_master)
 	{
-		m_volumeMaster = Config::audioVar().volume_master;
-		m_audioSystem->getBus(AudioBusType::AudioBusType_Master)->setVolume(m_volumeMaster);
-		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Master)->setVolume(m_volumeMaster);
+		m_volume[AudioBusType::AudioBusType_Master] = Config::audioVar().volume_master;
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
 	}
 	// Music volume
-	if(m_volumeMusic != Config::audioVar().volume_music)
+	if(m_volume[AudioBusType::AudioBusType_Music] != Config::audioVar().volume_music)
 	{
-		m_volumeMusic = Config::audioVar().volume_music;
-		m_audioSystem->getBus(AudioBusType::AudioBusType_Music)->setVolume(m_volumeMusic);
-		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Music)->setVolume(m_volumeMusic);
+		m_volume[AudioBusType::AudioBusType_Music] = Config::audioVar().volume_music;
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
 	}
 	// SFX volume
-	if(m_volumeSoundEffects != Config::audioVar().volume_sfx)
+	if(m_volume[AudioBusType::AudioBusType_SFX] != Config::audioVar().volume_sfx)
 	{
-		m_volumeSoundEffects = Config::audioVar().volume_sfx;
-		m_audioSystem->getBus(AudioBusType::AudioBusType_SFX)->setVolume(m_volumeSoundEffects);
-		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_SFX)->setVolume(m_volumeSoundEffects);
+		m_volume[AudioBusType::AudioBusType_SFX] = Config::audioVar().volume_sfx;
+		m_audioSystem->getBus(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
 	}
 
 	// Update the sound system
@@ -561,6 +551,45 @@ ErrorCode AudioScene::destroyObject(SystemObject *p_systemObject)
 	return ErrorCode::Destroy_obj_not_found;
 }
 
+void AudioScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+{
+	if(CheckBitmask(p_changeType, Systems::Changes::Audio::VolumeAmbient))
+	{
+		m_volume[AudioBusType::AudioBusType_Ambient] = p_subject->getFloat(this, Systems::Changes::Audio::VolumeAmbient);
+
+		Config::m_audioVar.volume_ambient = m_volume[AudioBusType::AudioBusType_Ambient];
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Ambient)->setVolume(m_volume[AudioBusType::AudioBusType_Ambient]);
+	}
+
+	if(CheckBitmask(p_changeType, Systems::Changes::Audio::VolumeMaster))
+	{
+		m_volume[AudioBusType::AudioBusType_Master] = p_subject->getFloat(this, Systems::Changes::Audio::VolumeMaster);
+
+		Config::m_audioVar.volume_master = m_volume[AudioBusType::AudioBusType_Master];
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Master)->setVolume(m_volume[AudioBusType::AudioBusType_Master]);
+	}
+
+	if(CheckBitmask(p_changeType, Systems::Changes::Audio::VolumeMusic))
+	{
+		m_volume[AudioBusType::AudioBusType_Music] = p_subject->getFloat(this, Systems::Changes::Audio::VolumeMusic);
+
+		Config::m_audioVar.volume_music = m_volume[AudioBusType::AudioBusType_Music];
+		m_audioSystem->getBus(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_Music)->setVolume(m_volume[AudioBusType::AudioBusType_Music]);
+	}
+
+	if(CheckBitmask(p_changeType, Systems::Changes::Audio::VolumeSFX))
+	{
+		m_volume[AudioBusType::AudioBusType_SFX] = p_subject->getFloat(this, Systems::Changes::Audio::VolumeSFX);
+
+		Config::m_audioVar.volume_sfx = m_volume[AudioBusType::AudioBusType_SFX];
+		m_audioSystem->getBus(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
+		m_audioSystem->getChannelGroup(AudioBusType::AudioBusType_SFX)->setVolume(m_volume[AudioBusType::AudioBusType_SFX]);
+	}
+}
+
 void AudioScene::createSound(SoundComponent &p_soundComponent)
 {
 	if(p_soundComponent.m_sound != nullptr)

+ 5 - 6
Praxis3D/Source/AudioScene.h

@@ -113,12 +113,14 @@ public:
 
 	ErrorCode destroyObject(SystemObject *p_systemObject);
 
-	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType);
 
 	SystemTask *getSystemTask() { return m_audioTask; };
 	Systems::TypeID getSystemType() { return Systems::TypeID::Audio; };
-	BitMask getDesiredSystemChanges() { return Systems::Changes::Generic::CreateObject || Systems::Changes::Generic::DeleteObject; }
+	BitMask getDesiredSystemChanges() { return Systems::Changes::Generic::CreateObject || Systems::Changes::Generic::DeleteObject || Systems::Changes::Audio::AllVolume; }
 	BitMask getPotentialSystemChanges() { return Systems::Changes::None; }
+	const std::vector<std::pair<std::string, FMOD::Studio::Bank *>> &getBankFilenames() const { return m_bankFilenames; }
+	const float getVolume(const AudioBusType p_audioBusType) const { return m_volume[p_audioBusType]; }
 
 private:
 	void createSound(SoundComponent &p_soundComponent);
@@ -139,10 +141,7 @@ private:
 	SingleSound m_collisionSounds[ObjectMaterialType::NumberOfMaterialTypes];
 
 	// Volume values of different buses
-	float m_volumeAmbient;
-	float m_volumeMaster;
-	float m_volumeMusic;
-	float m_volumeSoundEffects;
+	float m_volume[AudioBusType::AudioBusType_NumOfTypes];
 
 	FMOD::ChannelGroup *m_soundTypeChannelGroups[SoundComponent::SoundType_NumOfTypes];
 

+ 37 - 10
Praxis3D/Source/AudioSystem.cpp

@@ -32,16 +32,11 @@ ErrorCode AudioSystem::init()
 			// Initialize our Instance with 36 Channels
 			m_studioSystem->initialize(Config::audioVar().num_audio_channels, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL | FMOD_INIT_VOL0_BECOMES_VIRTUAL | FMOD_INIT_3D_RIGHTHANDED, NULL);
 
-			FMOD::Studio::Bank *defaultSoundBank = nullptr;
-
 			// Load default sound bank
-			if(fmodErrorLog(m_studioSystem->loadBankFile((Config::filepathVar().sound_path + Config::audioVar().default_sound_bank).c_str(), FMOD_STUDIO_LOAD_BANK_NORMAL, &defaultSoundBank), Config::audioVar().default_sound_bank))
-				if(fmodErrorLog(defaultSoundBank->loadSampleData(), Config::audioVar().default_sound_bank))
-					addImpactSoundBank(defaultSoundBank);
+			loadBankFile(Config::filepathVar().sound_path + Config::audioVar().default_sound_bank, FMOD_STUDIO_LOAD_BANK_NORMAL);
 
 			// Load default string bank
-			if(fmodErrorLog(m_studioSystem->loadBankFile((Config::filepathVar().sound_path + Config::audioVar().default_sound_bank_string).c_str(), FMOD_STUDIO_LOAD_BANK_NORMAL, &defaultSoundBank), Config::audioVar().default_sound_bank_string))
-				fmodErrorLog(defaultSoundBank->loadSampleData(), Config::audioVar().default_sound_bank_string);
+			loadBankFile(Config::filepathVar().sound_path + Config::audioVar().default_sound_bank_string, FMOD_STUDIO_LOAD_BANK_NORMAL);
 
 			// Create channel groups
 			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
@@ -64,9 +59,9 @@ ErrorCode AudioSystem::init()
 			}
 
 			// Add all channel groups (other than master) to the master channel group as a nested group
-			//for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
-			//	if(i != AudioBusType::AudioBusType_Master)
-			//		m_channelGroups[AudioBusType::AudioBusType_Master]->addGroup(m_channelGroups[i]);
+			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
+				if(i != AudioBusType::AudioBusType_Master)
+					m_channelGroups[AudioBusType::AudioBusType_Master]->addGroup(m_channelGroups[i]);
 		}
 	}
 
@@ -82,3 +77,35 @@ ErrorCode AudioSystem::setup(const PropertySet &p_properties)
 
 	return returnCode;
 }
+
+FMOD::Studio::Bank *AudioSystem::loadBankFile(const std::string p_filename, FMOD_STUDIO_LOAD_BANK_FLAGS p_flags)
+{
+	// Search through the already loaded banks, and if a match is found, return it
+	for(auto const &bankAndFilename : m_loadedBanks)
+	{
+		if(bankAndFilename.first == p_filename)
+			return bankAndFilename.second;
+	}
+
+	FMOD::Studio::Bank *newBank = nullptr;
+
+	// Bank hasn't been loaded yet, so try to load it
+	if(fmodErrorLog(m_studioSystem->loadBankFile(p_filename.c_str(), FMOD_STUDIO_LOAD_BANK_NORMAL, &newBank), p_filename))
+	{
+		if(fmodErrorLog(newBank->loadSampleData(), p_filename))
+		{
+			m_loadedBanks.push_back(std::make_pair(p_filename, newBank));
+			registerEvents(newBank);
+		}
+		else
+		{
+			newBank = nullptr;
+		}
+	}
+	else
+	{
+		newBank = nullptr;
+	}
+
+	return newBank;
+}

+ 23 - 5
Praxis3D/Source/AudioSystem.h

@@ -119,7 +119,7 @@ protected:
 			return true;
 	}
 
-	void addImpactSoundBank(FMOD::Studio::Bank *p_soundBank)
+	inline void registerEvents(FMOD::Studio::Bank *p_soundBank)
 	{
 		// Get the number of sound events
 		int numOfEvents = 0;
@@ -138,8 +138,8 @@ protected:
 				char path[512];
 				events[eventIndex]->getPath(path, 512, nullptr);
 
-				// Extract the sound event name from the path, and assign the event itself to the event name entry in the impact sound map
-				m_impactSounds[Utilities::splitStringAfterDelimiter(Config::audioVar().pathDelimiter, std::string(path))] = events[eventIndex];
+				// Extract the sound event name from the path, and assign the event itself to the event name entry in the sound event map
+				m_soundEvents[Utilities::splitStringAfterDelimiter(Config::audioVar().pathDelimiter, std::string(path))] = events[eventIndex];
 			}
 
 			// Delete the pointer to an array of pointers that was created
@@ -147,6 +147,19 @@ protected:
 		}
 	}
 
+	// Search through all loaded sound events and return a match; if no match is found, return a nullptr
+	inline FMOD::Studio::EventDescription *getEvent(const std::string &p_eventName)
+	{
+		auto event = m_soundEvents.find(p_eventName);
+
+		if(event == m_soundEvents.end())
+			return nullptr;
+		else
+			return event->second;
+	}
+
+	FMOD::Studio::Bank *loadBankFile(const std::string p_filename, FMOD_STUDIO_LOAD_BANK_FLAGS p_flags);
+
 	// Load all the audio buses from the studio system into a buses array
 	void assignBusses()
 	{
@@ -178,9 +191,14 @@ protected:
 
 	AudioScene *m_audioScenes[EngineStateType::EngineStateType_NumOfTypes];
 
-	// Sound effects for object impacts
+	// Sound banks for object impacts
 	FMOD::Studio::Bank *m_impactBank;
-	std::unordered_map<std::string, FMOD::Studio::EventDescription *> m_impactSounds;
+
+	// All loaded sound events
+	std::unordered_map<std::string, FMOD::Studio::EventDescription *> m_soundEvents;
+
+	// All banks that have been loaded
+	std::vector<std::pair<std::string, FMOD::Studio::Bank *>> m_loadedBanks;
 
 	// FMOD studio and core system handles
 	FMOD::Studio::System *m_studioSystem;

+ 1 - 0
Praxis3D/Source/CommonDefinitions.cpp

@@ -1,3 +1,4 @@
 #include "CommonDefinitions.h"
 
+DEFINE_ENUM(RenderPassType, RENDER_PASS_TYPE)
 DEFINE_ENUM(ObjectMaterialType, OBJ_MATERIAL_ID)

+ 15 - 16
Praxis3D/Source/CommonDefinitions.h

@@ -48,22 +48,21 @@ enum MemoryBarrierType : unsigned int
 	MemoryBarrierType_AccessAndFetchBarrier,
 	MemoryBarrierType_ShaderStorageBarrier
 };
-enum RenderPassType : unsigned int
-{
-	RenderPassType_Geometry,
-	RenderPassType_Lighting,
-	RenderPassType_AtmScattering,
-	RenderPassType_HdrMapping,
-	RenderPassType_Blur,
-	RenderPassType_Bloom,
-	RenderPassType_BloomComposite,
-	RenderPassType_LenseFlare,
-	RenderPassType_LenseFlareComposite,
-	RenderPassType_Luminance,
-	RenderPassType_Final,
-	RenderPassType_GUI,
-	RenderPassType_NumOfTypes
-};
+#define RENDER_PASS_TYPE(Code) \
+	Code(RenderPassType_Geometry, = 0) \
+	Code(RenderPassType_Lighting,) \
+	Code(RenderPassType_AtmScattering,) \
+	Code(RenderPassType_HdrMapping,) \
+	Code(RenderPassType_Blur,) \
+	Code(RenderPassType_Bloom,) \
+	Code(RenderPassType_BloomComposite,) \
+	Code(RenderPassType_LenseFlare,) \
+	Code(RenderPassType_LenseFlareComposite,) \
+	Code(RenderPassType_Luminance,) \
+	Code(RenderPassType_Final,) \
+	Code(RenderPassType_GUI,) \
+	Code(RenderPassType_NumOfTypes,)
+DECLARE_ENUM(RenderPassType, RENDER_PASS_TYPE)
 typedef std::vector<RenderPassType> RenderingPasses;
 
 enum BufferType : unsigned int

+ 8 - 2
Praxis3D/Source/Config.cpp

@@ -200,16 +200,22 @@ void Config::init()
 	AddVariablePredef(m_GUIVar, gui_dark_style);
 	AddVariablePredef(m_GUIVar, gui_sequence_array_reserve_size);
 	AddVariablePredef(m_GUIVar, editor_asset_selection_button_size_multiplier);
-	AddVariablePredef(m_GUIVar, editor_float_slider_speed); 
+	AddVariablePredef(m_GUIVar, editor_asset_texture_button_size_x);
+	AddVariablePredef(m_GUIVar, editor_asset_texture_button_size_y);
+	AddVariablePredef(m_GUIVar, editor_audio_banks_max_height);
+	AddVariablePredef(m_GUIVar, editor_float_slider_speed);
 	AddVariablePredef(m_GUIVar, editor_lua_variables_max_height);
 	AddVariablePredef(m_GUIVar, editor_play_button_size);
-	AddVariablePredef(m_GUIVar, gui_file_dialog_min_size_x);
+	AddVariablePredef(m_GUIVar, editor_render_pass_max_height);
+	AddVariablePredef(m_GUIVar, gui_file_dialog_min_size_x); 
 	AddVariablePredef(m_GUIVar, gui_file_dialog_min_size_y);
 	AddVariablePredef(m_GUIVar, gui_file_dialog_dir_color_R);
 	AddVariablePredef(m_GUIVar, gui_file_dialog_dir_color_G);
 	AddVariablePredef(m_GUIVar, gui_file_dialog_dir_color_B); 
 	AddVariablePredef(m_GUIVar, editor_button_add_texture);
 	AddVariablePredef(m_GUIVar, editor_button_add_list_texture);
+	AddVariablePredef(m_GUIVar, editor_button_arrow_down_texture);
+	AddVariablePredef(m_GUIVar, editor_button_arrow_up_texture);
 	AddVariablePredef(m_GUIVar, editor_button_delete_entry_texture); 
 	AddVariablePredef(m_GUIVar, editor_button_gui_sequence_texture);
 	AddVariablePredef(m_GUIVar, editor_button_open_file_texture);

+ 25 - 5
Praxis3D/Source/Config.h

@@ -220,8 +220,15 @@ namespace Systems
 			static constexpr BitMask Spatialized			= Changes::Type::Audio + Changes::Common::Shared6;
 			static constexpr BitMask StartPlaying			= Changes::Type::Audio + Changes::Common::Shared7;
 			static constexpr BitMask Volume					= Changes::Type::Audio + Changes::Common::Shared8;
-
-			static constexpr BitMask All					= Filename | ListenerID | Loop | Reload | SoundType | Spatialized | StartPlaying | Volume;
+			
+			static constexpr BitMask VolumeAmbient			= Changes::Type::Audio + Changes::Common::Shared9;
+			static constexpr BitMask VolumeMaster			= Changes::Type::Audio + Changes::Common::Shared10;
+			static constexpr BitMask VolumeMusic			= Changes::Type::Audio + Changes::Common::Shared11;
+			static constexpr BitMask VolumeSFX				= Changes::Type::Audio + Changes::Common::Shared12;
+			static constexpr BitMask AllVolume				= VolumeAmbient | VolumeMaster | VolumeMusic | VolumeSFX;
+
+			static constexpr BitMask All					= Filename | ListenerID | Loop | Reload | SoundType | Spatialized | StartPlaying | 
+																Volume | AllVolume;
 		}
 		namespace Graphics
 		{
@@ -248,7 +255,8 @@ namespace Systems
 			static constexpr BitMask IntermediateBuffer		= Changes::Type::Graphics + Changes::Graphics::Framebuffers + Changes::Common::Shared13;
 			static constexpr BitMask FinalBuffer			= Changes::Type::Graphics + Changes::Graphics::Framebuffers + Changes::Common::Shared14;
 			static constexpr BitMask RenderToTextureBuffer	= Changes::Type::Graphics + Changes::Graphics::Framebuffers + Changes::Common::Shared15;
-			static constexpr BitMask AllBuffers				= PositionBuffer | DiffuseBuffer | NormalBuffer | EmissiveBuffer | MatPropertiesBuffer | IntermediateBuffer | FinalBuffer | RenderToTextureBuffer;
+			static constexpr BitMask AllBuffers				= PositionBuffer | DiffuseBuffer | NormalBuffer | EmissiveBuffer | MatPropertiesBuffer | 
+																IntermediateBuffer | FinalBuffer | RenderToTextureBuffer;
 
 			static constexpr BitMask All					= AllCamera | AllLighting | AllBuffers;
 		}
@@ -266,9 +274,9 @@ namespace Systems
 			static constexpr BitMask Mass					= Changes::Type::Physics + Changes::Common::Shared4;
 			static constexpr BitMask Restitution			= Changes::Type::Physics + Changes::Common::Shared5;
 			static constexpr BitMask Kinematic				= Changes::Type::Physics + Changes::Common::Shared6;
-			//static constexpr BitMask LinearVelocity			= Changes::Type::Physics + Changes::Common::Shared7;
+			static constexpr BitMask Gravity				= Changes::Type::Physics + Changes::Common::Shared7;
 
-			static constexpr BitMask All					= CollisionShapeType | CollisionShapeSize | Friction | Mass | Restitution | Kinematic;
+			static constexpr BitMask All					= CollisionShapeType | CollisionShapeSize | Friction | Mass | Restitution | Kinematic | Gravity;
 		}
 		namespace Script
 		{
@@ -946,9 +954,13 @@ public:
 			gui_dark_style = true;
 			gui_sequence_array_reserve_size = 50;
 			editor_asset_selection_button_size_multiplier = 2.0f;
+			editor_asset_texture_button_size_x = 60.0f;
+			editor_asset_texture_button_size_y = 60.0f;
+			editor_audio_banks_max_height = 100.0f;
 			editor_float_slider_speed = 0.01f;
 			editor_lua_variables_max_height = 200.0f;
 			editor_play_button_size = 30.0f;
+			editor_render_pass_max_height = 250.0f;
 			gui_file_dialog_min_size_x = 400.0f;
 			gui_file_dialog_min_size_y = 200.0f;
 			gui_file_dialog_dir_color_R = 0.905f;
@@ -956,6 +968,8 @@ public:
 			gui_file_dialog_dir_color_B = 0.314f;
 			editor_button_add_texture = "buttons\\button_add_3.png";
 			editor_button_add_list_texture = "buttons\\button_add_from_list_1.png";
+			editor_button_arrow_down_texture = "buttons\\button_arrow_down_1.png";
+			editor_button_arrow_up_texture = "buttons\\button_arrow_up_1.png";
 			editor_button_delete_entry_texture = "buttons\\button_delete_5.png";
 			editor_button_gui_sequence_texture = "buttons\\button_gui_sequence_1.png";
 			editor_button_open_file_texture = "buttons\\button_open_file_1.png";
@@ -972,9 +986,13 @@ public:
 		bool gui_dark_style;
 		int gui_sequence_array_reserve_size;
 		float editor_asset_selection_button_size_multiplier;
+		float editor_asset_texture_button_size_x;
+		float editor_asset_texture_button_size_y;
+		float editor_audio_banks_max_height;
 		float editor_float_slider_speed;
 		float editor_lua_variables_max_height;
 		float editor_play_button_size;
+		float editor_render_pass_max_height;
 		float gui_file_dialog_min_size_x;
 		float gui_file_dialog_min_size_y;
 		float gui_file_dialog_dir_color_R;
@@ -982,6 +1000,8 @@ public:
 		float gui_file_dialog_dir_color_B;
 		std::string editor_button_add_texture;
 		std::string editor_button_add_list_texture;
+		std::string editor_button_arrow_down_texture;
+		std::string editor_button_arrow_up_texture;
 		std::string editor_button_delete_entry_texture;
 		std::string editor_button_gui_sequence_texture;
 		std::string editor_button_open_file_texture;

+ 5 - 2
Praxis3D/Source/Containers.h

@@ -4,6 +4,7 @@
 
 #include "CommonDefinitions.h"
 #include "Math.h"
+#include "PropertySet.h"
 
 // Stores all spatial data (position, rotation, scale)
 struct SpatialData
@@ -172,8 +173,9 @@ struct DoubleBufferedContainer
 // 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(const EngineChangeType p_changeType = EngineChangeType::EngineChangeType_None, const EngineStateType p_engineStateType = EngineStateType::EngineStateType_Default) : m_changeType(p_changeType), m_engineStateType(p_engineStateType) { }
+	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const std::string &p_filename) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_filename(p_filename) { }
+	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const PropertySet &p_sceneProperties) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_sceneProperties(p_sceneProperties) { }
 	~EngineChangeData() { }
 
 	void setChangeType(const EngineChangeType p_changeType) { m_changeType = p_changeType; }
@@ -183,4 +185,5 @@ struct EngineChangeData
 	EngineChangeType m_changeType;
 	EngineStateType m_engineStateType;
 	std::string m_filename;
+	PropertySet m_sceneProperties;
 };

+ 26 - 2
Praxis3D/Source/EditorState.cpp

@@ -18,9 +18,9 @@ EditorState::~EditorState()
 
 ErrorCode EditorState::load()
 {
-	ErrorCode returnError = ErrorCode::Initialize_failure;
+	ErrorCode returnError = ErrorCode::Success;
 
-	if(m_initialized)
+	if(m_initialized && !m_loaded)
 	{
 		// Load the scene map, and log an error if it wasn't successful
 		returnError = m_sceneLoader.loadFromFile(m_sceneFilename);
@@ -32,6 +32,30 @@ ErrorCode EditorState::load()
 		{
 			// 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);
+			m_loaded = true;
+		}
+	}
+
+	return returnError;
+}
+
+ErrorCode EditorState::load(const PropertySet &p_sceneProperty)
+{
+	ErrorCode returnError = ErrorCode::Success;
+
+	if(m_initialized && !m_loaded && p_sceneProperty)
+	{
+		// Load the scene map, and log an error if it wasn't successful
+		returnError = m_sceneLoader.loadFromProperties(p_sceneProperty);
+		if(returnError != ErrorCode::Success)
+		{
+			ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
+		}
+		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);
+			m_loaded = true;
 		}
 	}
 

+ 2 - 0
Praxis3D/Source/EditorState.h

@@ -13,6 +13,8 @@ public:
 
 	ErrorCode load();
 
+	ErrorCode load(const PropertySet &p_sceneProperty);
+
 	void update(Engine &p_engine);
 
 	void activate();

+ 751 - 20
Praxis3D/Source/EditorWindow.cpp

@@ -5,6 +5,7 @@
 #include "EditorWindow.h"
 #include "imgui_internal.h"
 #include "RendererScene.h"
+#include "ShaderUniformUpdater.h"
 #include "WorldScene.h"
 
 // Include every component
@@ -145,7 +146,6 @@ void EditorWindow::update(const float p_deltaTime)
 
     ImGui::PopStyleColor();
 
-
     m_imguiStyle.TabRounding = 0.0f;
 
     ImGuiWindowClass windowClassWithNoTabBar;
@@ -164,6 +164,9 @@ void EditorWindow::update(const float p_deltaTime)
         {
             if(ImGui::MenuItem("New"))
             {
+                // Set the new scene settings tab flag to be selected (bring to focus)
+                m_newSceneSettingsTabFlags = ImGuiTabItemFlags_SetSelected;
+                m_showNewMapWindow = true;
             }
             if(ImGui::MenuItem("Open...")) 
             {
@@ -186,7 +189,34 @@ void EditorWindow::update(const float p_deltaTime)
                 }
             }
             ImGui::Separator();
-            if(ImGui::MenuItem("Save")) {}
+            if(ImGui::MenuItem("Save")) 
+            {
+                // If the scene filename is empty (meaning the scene wasn't loaded from a file), launch the save-as file browser window
+                if(m_systemScene->getSceneLoader()->getSceneFilename().empty())
+                {
+                    // Only open the file browser if it's not opened already
+                    if(m_currentlyOpenedFileBrowser == FileBrowserActivated::FileBrowserActivated_None)
+                    {
+                        // Set the file browser activation to Save Scene
+                        m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_SaveScene;
+
+                        // Define file browser variables
+                        m_fileBrowserDialog.m_definedFilename = m_systemScene->getSceneLoader()->getSceneFilename();
+                        m_fileBrowserDialog.m_filter = ".pmap,.*";
+                        m_fileBrowserDialog.m_title = "Save scene";
+                        m_fileBrowserDialog.m_name = "SaveSceneFileDialog";
+                        m_fileBrowserDialog.m_rootPath = Config::filepathVar().map_path;
+                        m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_ConfirmOverwrite;
+
+                        // Tell the GUI scene to open the file browser
+                        m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
+                    }
+                }
+                else
+                {
+                    m_systemScene->getSceneLoader()->saveToFile(m_systemScene->getSceneLoader()->getSceneFilename());
+                }
+            }
             if(ImGui::MenuItem("Save as..."))
             {
                 // Only open the file browser if it's not opened already
@@ -210,7 +240,7 @@ void EditorWindow::update(const float p_deltaTime)
             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));
+                m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneReload, EngineStateType::EngineStateType_Editor));
             }
 
             ImGui::Separator();
@@ -972,9 +1002,6 @@ void EditorWindow::update(const float p_deltaTime)
                         }
                     }
 
-                    {
-                    }
-
                     auto *shaderComponent = entityRegistry.try_get<ShaderComponent>(m_selectedEntity.m_entityID);
                     if(shaderComponent != nullptr)
                     {
@@ -1548,6 +1575,18 @@ void EditorWindow::update(const float p_deltaTime)
                 ImGui::EndTabItem();
             }
             
+            if(ImGui::BeginTabItem("Scene settings"))
+            {
+                if(m_currentSceneData.m_modified)
+                {
+                    updateSceneData(m_currentSceneData);
+                    m_currentSceneData.m_modified = false;
+                }
+                drawSceneData(m_currentSceneData, true);
+
+                ImGui::EndTabItem();
+            }
+
             ImGui::EndTabBar();
         }
 
@@ -1566,17 +1605,18 @@ void EditorWindow::update(const float p_deltaTime)
         {
             if(ImGui::BeginTabBar("##BottomWindowTabBar", ImGuiTabBarFlags_None))
             {
-                if(ImGui::BeginTabItem("Assets"))
+                if(ImGui::BeginTabItem("Textures"))
                 {
+                    // Calculate the available window size
+                    float visibleWindowSize = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
+
                     // Draw each texture in the 2D texture loader pool
                     for(decltype(m_textureAssets.size()) i = 0, size = m_textureAssets.size(); i < size; i++)
                     {
-                        // Set each entry to be drawn on the same line (except the first entry)
-                        if(i > 0)
-                            ImGui::SameLine();
+                        ImGui::PushID((int)i);
 
                         // Draw the texture
-                        if(ImGui::ImageButton((ImTextureID)m_textureAssets[i].first->getHandle(), ImVec2(60.0f, 60.0f), ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0))
+                        if(ImGui::ImageButton((ImTextureID)m_textureAssets[i].first->getHandle(), m_textureAssetImageSize, ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0))
                         {
                             m_selectedTexture = m_textureAssets[i].first;
 
@@ -1592,8 +1632,7 @@ void EditorWindow::update(const float p_deltaTime)
                             ImGui::Text("");
                             ImGui::SeparatorText("Click to open in Texture Inspector");
                             ImGui::Text("");
-                            ImGui::PopStyleVar();
-                            ImGui::PopStyleVar();
+                            ImGui::PopStyleVar(2); // ImGuiStyleVar_ItemSpacing, ImGuiStyleVar_SeparatorTextAlign
                             ImGui::Separator();
 
                             ImGui::Text(("Filename: " + m_textureAssets[i].second).c_str());
@@ -1611,10 +1650,229 @@ void EditorWindow::update(const float p_deltaTime)
 
                             ImGui::EndTooltip();
                         }
+
+                        // Calculate expected position if the next texture was on the same line
+                        float nextTextureSize = ImGui::GetItemRectMax().x + m_imguiStyle.ItemSpacing.x + m_textureAssetImageSize.x;
+
+                        // If this is not the last texture and the next text will fit on the same line, set next draw to be on the same line
+                        if(i + 1 < size && nextTextureSize < visibleWindowSize)
+                            ImGui::SameLine();
+
+                        ImGui::PopID();
                     }
+                    ImGui::EndTabItem();
+                }
 
+                if(ImGui::BeginTabItem("Models"))
+                {
+                    for(decltype(m_modelAssets.size()) i = 0, size = m_modelAssets.size(); i < size; i++)
+                    {
+                        ImGui::Text(m_modelAssets[i].second.c_str());
+                    }
                     ImGui::EndTabItem();
                 }
+
+                if(ImGui::BeginTabItem("Shaders"))
+                {
+                    auto contentRegionWidth = ImGui::GetContentRegionAvail().x;
+                    auto singleWindowWidth = contentRegionWidth / 3.0f;
+
+                    ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
+                    ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
+
+                    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
+
+                    if(ImGui::BeginChild("##ShaderAssetsProgramSelection", ImVec2(contentRegionWidth * 0.25f, 0), true))
+                    {
+                        ImGui::SeparatorText("Shader programs:");
+                        for(decltype(m_shaderAssets.size()) shaderIndex = 0, size = m_shaderAssets.size(); shaderIndex < size; shaderIndex++)
+                        {
+                            if(ImGui::Selectable(m_shaderAssets[shaderIndex].second.c_str(), m_selectedProgramShader == shaderIndex))
+                            {
+                                m_selectedProgramShader = (int)shaderIndex;
+                                m_selectedShader = -1;
+                            }
+                        }
+                    }
+                    ImGui::EndChild();
+
+                    ImGui::SameLine();
+
+                    if(m_selectedProgramShader >= 0 && m_selectedProgramShader < m_shaderAssets.size())
+                    {
+                        if(ImGui::BeginChild("##ShaderAssetsShaderSelection", ImVec2(contentRegionWidth * 0.25f, 0), true))
+                        {
+                            ImGui::SeparatorText("Shaders:");
+                            for(unsigned int shaderType = 0; shaderType < ShaderType::ShaderType_NumOfTypes; shaderType++)
+                            {
+                                if(!m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(shaderType).empty())
+                                {
+                                    if(ImGui::Selectable(m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(shaderType).c_str(), m_selectedShader == shaderType))
+                                    {
+                                        m_selectedShader = (int)shaderType;
+                                    }
+                                }
+                            }
+                        }
+                        ImGui::EndChild();
+                    }
+
+                    ImGui::SameLine();
+
+                    ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
+
+                    if(m_selectedShader >= 0 && m_selectedShader < ShaderType::ShaderType_NumOfTypes)
+                    {
+                        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
+                        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
+
+                        if(ImGui::BeginChild("##ShaderAssetsSettings", ImVec2(contentRegionWidth * 0.5f, 0), true))
+                        {
+                            ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
+                            ImGui::SeparatorText("Shader settings:");
+
+                            // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
+                            float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;                
+
+                            // Calculate button width so they span across the whole window width
+                            auto buttonWidth = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x * 2.0f) / 3.0f;
+
+                            if(ImGui::Button("Open in text editor", ImVec2(buttonWidth, 0)))
+                            {
+
+                            }
+
+                            ImGui::SameLine();
+
+                            if(ImGui::Button("Open in file explorer", ImVec2(buttonWidth, 0)))
+                            {
+                                ShellExecuteA(NULL, "explore", (Filesystem::getCurrentDirectory() + "\\" + Config::filepathVar().shader_path + Utilities::stripFilePath(m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(m_selectedShader))).c_str(), NULL, NULL, SW_SHOWDEFAULT);
+                            }
+
+                            ImGui::SameLine();
+
+                            if(ImGui::Button("Reload shader program", ImVec2(buttonWidth, 0)))
+                            {
+
+                            }
+
+                            ImGui::Separator();
+
+                            // Draw SHADER FILENAME
+                            auto shaderFilename = m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(m_selectedShader);
+                            drawLeftAlignedLabelText("Filename:", inputWidgetOffset, calcTextSizedButtonOffset(1) - inputWidgetOffset - m_imguiStyle.FramePadding.x);
+                            if(ImGui::InputText("##ShaderFilenameInput", &shaderFilename, ImGuiInputTextFlags_EnterReturnsTrue))
+                            {
+                            }
+
+                            // Draw OPEN button
+                            ImGui::SameLine(calcTextSizedButtonOffset(1));
+                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_OpenFile], "##ShaderFileOpenFileButton", "Open a shader file"))
+                            {
+                            }
+
+                            // Draw RELOAD button
+                            ImGui::SameLine(calcTextSizedButtonOffset(0));
+                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Reload], "##ShaderFileReloadButton", "Reload the shader"))
+                            {
+                            }
+
+                            // Draw SHADER TYPE
+                            auto shaderType = m_selectedShader;
+                            drawLeftAlignedLabelText("Shader type:", inputWidgetOffset);
+                            if(ImGui::Combo("##ShaderTypePicker", &shaderType, &m_shaderTypeStrings[0], (int)m_shaderTypeStrings.size()))
+                            {
+                            }
+
+                            // Draw DEFAULT SHADER
+                            auto defaultShader = m_shaderAssets[m_selectedProgramShader].first->isDefaultProgram();
+                            drawLeftAlignedLabelText("Default shader:", inputWidgetOffset);
+                            if(ImGui::Checkbox("##DefaultShaderCheck", &defaultShader))
+                            {
+                            }                           
+                            
+                            // Draw LOADED-TO-VIDEO-MEMORY
+                            auto loadedToVideoMemory = m_shaderAssets[m_selectedProgramShader].first->isLoadedToVideoMemory();
+                            drawLeftAlignedLabelText("Loaded to video memory:", inputWidgetOffset);
+                            if(ImGui::Checkbox("##LoadedToVideoMemoryCheck", &loadedToVideoMemory))
+                            {
+                            }
+
+                            // Draw UNIFORM UPDATER SETTINGS
+                            if(ImGui::TreeNodeEx("Uniform updater settings:", ImGuiTreeNodeFlags_Framed)) // ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Leaf
+                            {
+                                // Get uniform updater
+                                auto const &uniformUpdater = m_shaderAssets[m_selectedProgramShader].first->getUniformUpdater();
+
+                                // Draw UPDATES PER FRAME
+                                auto numUpdatesPerFrame = uniformUpdater.getNumUpdatesPerFrame();
+                                drawLeftAlignedLabelText("Updates per frame:", inputWidgetOffset);
+                                if(numUpdatesPerFrame > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numUpdatesPerFrame).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numUpdatesPerFrame).c_str());
+
+                                // Draw UPDATES PER MODEL
+                                auto numUpdatesPerModel = uniformUpdater.getNumUpdatesPerModel();
+                                drawLeftAlignedLabelText("Updates per model:", inputWidgetOffset);
+                                if(numUpdatesPerModel > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numUpdatesPerModel).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numUpdatesPerModel).c_str());
+
+                                // Draw UPDATES PER MESH
+                                auto numUpdatesPerMesh = uniformUpdater.getNumUpdatesPerMesh();
+                                drawLeftAlignedLabelText("Updates per mesh:", inputWidgetOffset);
+                                if(numUpdatesPerMesh > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numUpdatesPerMesh).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numUpdatesPerMesh).c_str());
+
+                                // Draw TEXTURE UPDATES
+                                auto numTextureUpdates = uniformUpdater.getNumTextureUpdates();
+                                drawLeftAlignedLabelText("Texture updates:", inputWidgetOffset);
+                                if(numTextureUpdates > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numTextureUpdates).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numTextureUpdates).c_str());
+
+                                // Draw UNIFORM BLOCKS
+                                auto numUniformBlocks = uniformUpdater.getNumUniformBlocks();
+                                drawLeftAlignedLabelText("Uniform blocks:", inputWidgetOffset);
+                                if(numUniformBlocks > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numUniformBlocks).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numUniformBlocks).c_str());
+
+                                // Draw SSB BLOCKS
+                                auto numSSBBlocks = uniformUpdater.getNumSSBBufferBlocks();
+                                drawLeftAlignedLabelText("SSB blocks:", inputWidgetOffset);
+                                if(numSSBBlocks > 0)
+                                    ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), Utilities::toString(numSSBBlocks).c_str());
+                                else
+                                    ImGui::TextDisabled(Utilities::toString(numSSBBlocks).c_str());
+
+                                ImGui::TreePop();
+                            }
+                        }
+                        else
+                            ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
+                        ImGui::EndChild();
+                    }
+
+                    ImGui::PopStyleVar(2); // ImGuiStyleVar_ChildBorderSize, ImGuiStyleVar_SeparatorTextAlign
+                    ImGui::PopStyleColor(); // ImGuiCol_Border
+
+                    ImGui::EndTabItem();
+                }
+
+                if(ImGui::BeginTabItem("Scripts"))
+                {
+                    ImGui::EndTabItem();
+                }
+
                 ImGui::EndTabBar();
             }
             ImGui::End();
@@ -1698,10 +1956,6 @@ void EditorWindow::update(const float p_deltaTime)
                                 {
                                     if(ImGui::BeginTabBar("##CenterWindowTabBar", ImGuiTabBarFlags_None))
                                     {
-                                        //if(ImGui::BeginTabItem("Scene viewport"))
-                                        //{
-                                        //    ImGui::EndTabItem();
-                                        //}
                                         if(ImGui::BeginTabItem("Texture inspector"))
                                         {
                                             ImVec2 textureSize = ImVec2((float)m_selectedTexture->getTextureWidth(), (float)m_selectedTexture->getTextureHeight());
@@ -1726,6 +1980,85 @@ void EditorWindow::update(const float p_deltaTime)
                     }
                 }
 
+                if(m_showNewMapWindow)
+                {
+                    if(ImGui::BeginTabItem("New scene settings", 0, m_newSceneSettingsTabFlags))
+                    {
+                        // Reset the tab flags (that previously been set to focus the window, when the new scene button was pressed), so it doesn't get continuously focused
+                        m_newSceneSettingsTabFlags = 0;
+
+                        auto contentRegionSize = ImGui::GetContentRegionAvail();
+
+                        ImGui::NewLine();
+
+                        ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
+                        ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
+                        if(ImGui::BeginChild("##NewSceneSettingsWindow", ImVec2(contentRegionSize.x * 0.6f, contentRegionSize.y * 0.9f), true))
+                        {
+                            ImGui::PopStyleVar(); // ImGuiStyleVar_ChildBorderSize
+                            ImGui::PopStyleColor(); // ImGuiCol_Border
+
+                            ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
+                            ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 5.0f);
+                            ImGui::PopStyleVar(2); //ImGuiStyleVar_SeparatorTextAlign, ImGuiStyleVar_SeparatorTextBorderSize
+
+                            ImGui::NewLine();
+
+                            //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 10.0f));
+                            //if(ImGui::BeginChild("##NewSceneSettingsWindow2"))
+                            //{
+                                drawSceneData(m_newSceneData);
+                            //    ImGui::EndChild();
+                            //}
+                            //ImGui::PopStyleVar(); // ImGuiStyleVar_FramePadding
+                            ImGui::EndChild();
+                        }
+                        else
+                        {
+                            ImGui::PopStyleVar(); // ImGuiStyleVar_ChildBorderSize
+                            ImGui::PopStyleColor(); // ImGuiCol_Border
+                        }
+
+                        ImGui::SameLine();
+
+                        if(ImGui::BeginChild("##NewSceneButtonsWindow"))
+                        {
+                            const ImVec2 buttonSize(contentRegionSize.x * 0.2f, ImGui::GetFrameHeight() * 1.5f);
+                            const float centerButtonOffset = (ImGui::GetContentRegionAvail().x / 2.0f) - (buttonSize.x / 2.0f);
+
+                            //ImGui::NewLine();
+                            ImGui::SetCursorPosX(centerButtonOffset);
+                            if(ImGui::Button("Create scene", buttonSize))
+                            {
+                                PropertySet sceneProperties(Properties::Default);
+
+                                generateNewMap(sceneProperties, m_newSceneData);
+
+                                // Send a notification to the engine to reload the current engine state
+                                m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneReload, EngineStateType::EngineStateType_Editor, sceneProperties));
+                            }
+
+                            ImGui::SetCursorPosX(centerButtonOffset);
+                            if(ImGui::Button("Cancel", buttonSize))
+                            {
+                                m_newSceneData = SceneData();
+                                m_showNewMapWindow = false;
+                            }
+
+                            ImGui::NewLine();
+                            ImGui::SetCursorPosX(centerButtonOffset);
+                            if(ImGui::Button("Reload to default", buttonSize))
+                            {
+                                m_newSceneData = SceneData();
+                            }
+
+                            ImGui::EndChild();
+                        }
+
+                        ImGui::EndTabItem();
+                    }
+                }
+
                 ImGui::EndTabBar();
             }
             ImGui::End();
@@ -1894,6 +2227,35 @@ void EditorWindow::update(const float p_deltaTime)
                 }
             }
             break;
+        case EditorWindow::FileBrowserActivated_AudioBankFile:
+            {
+                // If the file browser was activated and it is now closed, process the result
+                if(m_fileBrowserDialog.m_closed)
+                {
+                    if(m_fileBrowserDialog.m_success)
+                    {
+                        // Get the current directory path
+                        const std::string currentDirectory = Filesystem::getCurrentDirectory() + "\\" + Config::filepathVar().sound_path;
+
+                        // Check if the selected file is within the current directory
+                        if(m_fileBrowserDialog.m_filePathName.rfind(currentDirectory, 0) == 0)
+                        {
+                            if(m_fileBrowserDialog.m_userStringPointer != nullptr)
+                            {
+                                *m_fileBrowserDialog.m_userStringPointer = m_fileBrowserDialog.m_filePathName.substr(currentDirectory.size());
+                            }
+                        }
+                        else
+                            ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
+                    }
+
+                    // Reset the file browser and mark the file browser as not opened
+                    m_fileBrowserDialog.resetAll();
+                    m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
+                }
+            }
+            break;
+            
     }
 }
 
@@ -1926,6 +2288,7 @@ void EditorWindow::setup(EditorWindowSettings &p_editorWindowSettings)
     m_buttonTextures.emplace_back(Loaders::texture2D().load(Config::GUIVar().editor_button_open_file_texture));
     m_buttonTextures.emplace_back(Loaders::texture2D().load(Config::GUIVar().editor_button_reload_texture));
     m_buttonTextures.emplace_back(Loaders::texture2D().load(Config::GUIVar().editor_button_open_asset_list_texture));
+    m_buttonTextures.emplace_back(Loaders::texture2D().load(Config::GUIVar().editor_button_arrow_up_texture));
 
     assert(m_buttonTextures.size() == ButtonTextureType::ButtonTextureType_NumOfTypes && "m_buttonTextures array is different size than the number of button textures, in EditorWindow.cpp");
 
@@ -1953,6 +2316,246 @@ void EditorWindow::setup(EditorWindowSettings &p_editorWindowSettings)
         m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Physics), DataType::DataType_SimulationActive, (void *)false);
 }
 
+void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChanges)
+{
+    // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
+    float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
+
+    ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
+
+    // Center the separator text
+    ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
+    ImGui::SeparatorText("Scene settings:");
+
+    // Draw LOAD IN BACKGROUND
+    drawLeftAlignedLabelText("Load in background:", inputWidgetOffset);
+    if(ImGui::Checkbox("##LoadInBackgroundCheckbox", &p_sceneData.m_loadInBackground) && p_sendChanges)
+    {
+        p_sceneData.m_modified = true;
+        m_systemScene->getSceneLoader()->setLoadInBackground(p_sceneData.m_loadInBackground);
+    }
+
+    // Draw GRAVITY
+    drawLeftAlignedLabelText("Gravity:", inputWidgetOffset);
+    if(ImGui::DragFloat3("##GravityDrag", glm::value_ptr(p_sceneData.m_gravity), Config::GUIVar().editor_float_slider_speed) && p_sendChanges)
+    {
+        p_sceneData.m_modified = true;
+        m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Physics), Systems::Changes::Physics::Gravity);
+    }
+
+    ImGui::SeparatorText("Audio scene settings:");
+
+    // Create a duplicate variable for each volume type and multiply it by 100, so it can be shown as percentage
+    float volumeMult[AudioBusType::AudioBusType_NumOfTypes];
+    for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
+        volumeMult[i] = p_sceneData.m_volume[i] * 100.0f;
+
+    ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+    ImGui::SeparatorText("Volume control:");
+    ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+    // Draw MASTER VOLUME
+    drawLeftAlignedLabelText("Master volume:", inputWidgetOffset);
+    if(ImGui::SliderFloat("##MasterVolumeSlider", &volumeMult[AudioBusType::AudioBusType_Master], 0.0f, 100.0f, "%.0f%%", ImGuiSliderFlags_None))
+    {
+        p_sceneData.m_volume[AudioBusType::AudioBusType_Master] = volumeMult[AudioBusType::AudioBusType_Master] / 100.0f;
+        if(p_sendChanges)
+        {
+            p_sceneData.m_modified = true;
+            m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Audio), Systems::Changes::Audio::VolumeMaster);
+        }
+    }
+
+    // Draw AMBIENT VOLUME
+    drawLeftAlignedLabelText("Ambient volume:", inputWidgetOffset);
+    if(ImGui::SliderFloat("##AmbientVolumeSlider", &volumeMult[AudioBusType::AudioBusType_Ambient], 0.0f, 100.0f, "%.0f%%", ImGuiSliderFlags_None))
+    {
+        p_sceneData.m_volume[AudioBusType::AudioBusType_Ambient] = volumeMult[AudioBusType::AudioBusType_Ambient] / 100.0f;
+        if(p_sendChanges)
+        {
+            p_sceneData.m_modified = true;
+            m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Audio), Systems::Changes::Audio::VolumeAmbient);
+        }
+    }
+
+    // Draw MUSIC VOLUME
+    drawLeftAlignedLabelText("Music volume:", inputWidgetOffset);
+    if(ImGui::SliderFloat("##MusicVolumeSlider", &volumeMult[AudioBusType::AudioBusType_Music], 0.0f, 100.0f, "%.0f%%", ImGuiSliderFlags_None))
+    {
+        p_sceneData.m_volume[AudioBusType::AudioBusType_Music] = volumeMult[AudioBusType::AudioBusType_Music] / 100.0f;
+        if(p_sendChanges)
+        {
+            p_sceneData.m_modified = true;
+            m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Audio), Systems::Changes::Audio::VolumeMusic);
+        }
+    }
+
+    // Draw SFX VOLUME
+    drawLeftAlignedLabelText("Sound effect volume:", inputWidgetOffset);
+    if(ImGui::SliderFloat("##SFXVolumeSlider", &volumeMult[AudioBusType::AudioBusType_SFX], 0.0f, 100.0f, "%.0f%%", ImGuiSliderFlags_None))
+    {
+        p_sceneData.m_volume[AudioBusType::AudioBusType_SFX] = volumeMult[AudioBusType::AudioBusType_SFX] / 100.0f;
+        if(p_sendChanges)
+        {
+            p_sceneData.m_modified = true;
+            m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Audio), Systems::Changes::Audio::VolumeSFX);
+        }
+    }
+
+    // Calculate rendering passes window height and cap it to a max height value
+    float audioBanksWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y) * (p_sceneData.m_audioBanks.size() + 2);
+    audioBanksWindowHeight = audioBanksWindowHeight > Config::GUIVar().editor_audio_banks_max_height ? Config::GUIVar().editor_audio_banks_max_height : audioBanksWindowHeight;
+
+    if(ImGui::BeginChild("##AudioBanks", ImVec2(0, audioBanksWindowHeight), true))
+    {
+        ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+        ImGui::SeparatorText("Audio banks:");
+        ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+        for(decltype(p_sceneData.m_audioBanks.size()) size = p_sceneData.m_audioBanks.size(), i = 0; i < size; i++)
+        {
+            // Draw BANK FILENAME
+            drawLeftAlignedLabelText("Filename:", inputWidgetOffset, calcTextSizedButtonOffset(2) - inputWidgetOffset - m_imguiStyle.FramePadding.x);
+            if(ImGui::InputText(("##AudioBankFilenameInput" + Utilities::toString(i)).c_str(), &p_sceneData.m_audioBanks[i], ImGuiInputTextFlags_EnterReturnsTrue))
+            {
+            }
+
+            // Draw OPEN button
+            ImGui::SameLine(calcTextSizedButtonOffset(2));
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_OpenFile], "##" + Utilities::toString(i) + "AudioBankOpenButton", "Browse for an Audio Bank file"))
+            {
+                // Only open the file browser if it's not opened already
+                if(m_currentlyOpenedFileBrowser == FileBrowserActivated::FileBrowserActivated_None)
+                {
+                    // Set the file browser activation to Lua Script
+                    m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_AudioBankFile;
+
+                    // Define file browser variables
+                    m_fileBrowserDialog.m_filter = "Audio Bank files (.bank){.bank},All files{.*}";
+                    m_fileBrowserDialog.m_title = "Select an Audio Bank file";
+                    m_fileBrowserDialog.m_name = "OpenAudioBankFileDialog";
+                    m_fileBrowserDialog.m_rootPath = Config::filepathVar().sound_path;
+                    m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_None;
+                    m_fileBrowserDialog.m_userStringPointer = &p_sceneData.m_audioBanks[i];
+
+                    // Tell the GUI scene to open the file browser
+                    m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
+                }
+            }
+
+            // Draw DELETE button
+            ImGui::SameLine(calcTextSizedButtonOffset(1));
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_DeleteEntry], "##" + Utilities::toString(i) + "AudioBanksDeleteButton", "Remove this Audio Bank entry"))
+            {
+                p_sceneData.m_audioBanks.erase(p_sceneData.m_audioBanks.begin() + i);
+                size = p_sceneData.m_audioBanks.size();
+                i--;
+            }
+
+            // Draw ADD button
+            ImGui::SameLine(calcTextSizedButtonOffset(0));
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "##" + Utilities::toString(i) + "AudioBanksAddButton", "Add a new Audio Bank entry"))
+            {
+                p_sceneData.m_audioBanks.insert(p_sceneData.m_audioBanks.begin() + i + 1, "");
+                size = p_sceneData.m_audioBanks.size();
+            }
+        }
+
+        // Draw ADD button
+        ImGui::SetCursorPosX(calcTextSizedButtonOffset(0));
+        if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "##AudioBanksAddAtEndButton", "Add a new Audio Bank entry"))
+        {
+            p_sceneData.m_audioBanks.push_back("");
+        }
+
+    }
+    ImGui::EndChild();
+
+    ImGui::SeparatorText("Graphics scene settings:");
+
+    // Calculate rendering passes window height and cap it to a max height value
+    float renderPassWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y) * (p_sceneData.m_renderingPasses.size() + 2);
+    renderPassWindowHeight = renderPassWindowHeight > Config::GUIVar().editor_render_pass_max_height ? Config::GUIVar().editor_render_pass_max_height : renderPassWindowHeight;
+
+    if(ImGui::BeginChild("##RenderingPasses", ImVec2(0, renderPassWindowHeight), true))//, ImVec2(0, childWindowHeight), true, ImGuiWindowFlags_None)
+    {
+        ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+        ImGui::SeparatorText("Rendering passes:");
+        ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+        for(decltype(p_sceneData.m_renderingPasses.size()) size = p_sceneData.m_renderingPasses.size(), i = 0; i < size; i++)
+        {
+            // Draw RENDERING PASS COMBO
+            int renderType = (int)p_sceneData.m_renderingPasses[i];		
+            ImGui::AlignTextToFramePadding();
+            ImGui::Text((Utilities::toString(i + 1) + ". Render Pass type:").c_str());
+
+            // Draw UP button
+            ImGui::SameLine();
+            ImGui::SetCursorPosX(inputWidgetOffset);
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_ArrowUp], "##" + Utilities::toString(i) + "RenderingPassUpButton", "Move up"))
+            {
+                if(i > 0)
+                {
+                    auto tempRenderType = p_sceneData.m_renderingPasses[i];
+                    p_sceneData.m_renderingPasses[i] = p_sceneData.m_renderingPasses[i - 1];
+                    p_sceneData.m_renderingPasses[i - 1] = tempRenderType;
+                }
+            }
+
+            // Draw DOWN button
+            ImGui::SameLine();
+            ImGui::SetCursorPosX(inputWidgetOffset + m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x * 3);
+            if(drawTextSizedButtonInverted(m_buttonTextures[ButtonTextureType::ButtonTextureType_ArrowUp], "##" + Utilities::toString(i) + "RenderingPassDownButton", "Move down"))
+            {
+                if(i < size - 1)
+                {
+                    auto tempRenderType = p_sceneData.m_renderingPasses[i];
+                    p_sceneData.m_renderingPasses[i] = p_sceneData.m_renderingPasses[i + 1];
+                    p_sceneData.m_renderingPasses[i + 1] = tempRenderType;
+                }
+            }
+
+            // Draw RENDER PASSES
+            ImGui::SameLine();
+            ImGui::SetCursorPosX(inputWidgetOffset + (m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x * 3) * 2);
+            ImGui::SetNextItemWidth(calcTextSizedButtonOffset(1) - m_imguiStyle.FramePadding.x * 2);
+            if(ImGui::Combo(("##RenderingPassCombo" + Utilities::toString(i)).c_str(), &renderType, &m_renderingPassesTypeText[0], (int)m_renderingPassesTypeText.size()))
+            {
+                p_sceneData.m_renderingPasses[i] = static_cast<RenderPassType>(renderType);
+            }
+
+            // Draw DELETE button
+            ImGui::SameLine(calcTextSizedButtonOffset(1));
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_DeleteEntry], "##" + Utilities::toString(i) + "RenderingPassDeleteButton", "Remove this Render Pass entry"))
+            {
+                p_sceneData.m_renderingPasses.erase(p_sceneData.m_renderingPasses.begin() + i);
+                size = p_sceneData.m_renderingPasses.size();
+                i--;
+            }
+
+            // Draw ADD button
+            ImGui::SameLine(calcTextSizedButtonOffset(0));
+            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "##" + Utilities::toString(i) + "RenderingPassAddButton", "Add a new Render Pass entry"))
+            {
+                p_sceneData.m_renderingPasses.insert(p_sceneData.m_renderingPasses.begin() + i + 1, RenderPassType::RenderPassType_AtmScattering);
+                size = p_sceneData.m_renderingPasses.size();
+            }
+        }
+
+        // Draw ADD button
+        ImGui::SetCursorPosX(calcTextSizedButtonOffset(0));
+        if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "##RenderingPassAddAtEndButton", "Add a new Render Pass entry"))
+        {
+            p_sceneData.m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
+        }
+    }
+    ImGui::EndChild();
+
+    ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextAlign
+    ImGui::PopStyleColor(); // ImGuiCol_Border
+}
+
 void EditorWindow::drawEntityHierarchyEntry(EntityHierarchyEntry &p_entityEntry)
 {
     static ImGuiTreeNodeFlags baseNodeFlags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
@@ -1974,6 +2577,34 @@ void EditorWindow::drawEntityHierarchyEntry(EntityHierarchyEntry &p_entityEntry)
     }
 }
 
+void EditorWindow::updateSceneData(SceneData &p_sceneData)
+{
+    // Get the required system scenes
+    const auto *audioScene = static_cast<AudioScene *>(m_systemScene->getSceneLoader()->getSystemScene(Systems::Audio));
+    const auto *graphicsScene = static_cast<RendererScene *>(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics));
+    const auto *physicsScene = static_cast<PhysicsScene *>(m_systemScene->getSceneLoader()->getSystemScene(Systems::Physics));
+
+    // Set load-in-background flag
+    p_sceneData.m_loadInBackground = m_systemScene->getSceneLoader()->getLoadInBackground();
+
+    // Add audio bank filenames
+    p_sceneData.m_audioBanks.clear();
+    const auto &bankFilenames = audioScene->getBankFilenames();
+    for(const auto &bank : bankFilenames)
+        p_sceneData.m_audioBanks.push_back(bank.first);
+
+    // Set audio volume
+    for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
+        p_sceneData.m_volume[i] = audioScene->getVolume(static_cast<AudioBusType>(i));
+
+    // Add rendering passes
+    p_sceneData.m_renderingPasses.clear();
+    p_sceneData.m_renderingPasses = graphicsScene->getRenderingPasses();
+
+    // Set gravity
+    p_sceneData.m_gravity = physicsScene->getGravity();
+}
+
 void EditorWindow::updateEntityList()
 {
     // Make sure to clear old entity list entries
@@ -2202,9 +2833,10 @@ void EditorWindow::updateAssetLists()
     m_textureAssets.clear();
 
     // Go over each texture in the loaders texture pool
-    auto texturePool = Loaders::texture2D().getObjectPool();
+    const auto &texturePool = Loaders::texture2D().getObjectPool();
     for(decltype(texturePool.size()) i = 0, size = texturePool.size(); i < size; i++)
     {
+        // Add the texture and name entry
         m_textureAssets.push_back(std::make_pair(texturePool[i], texturePool[i]->getFilename()));
 
         // Set the longest texture name from all loaded texture assets, required for setting popup sizes when showing the list of textures
@@ -2222,13 +2854,112 @@ void EditorWindow::updateAssetLists()
     m_modelAssets.clear();
 
     // Go over each model in the loaders model pool
-    auto modelPool = Loaders::model().getObjectPool();
+    const auto &modelPool = Loaders::model().getObjectPool();
     for(decltype(modelPool.size()) i = 0, size = modelPool.size(); i < size; i++)
     {
+        // Add the model and name entry
         m_modelAssets.push_back(std::make_pair(modelPool[i], modelPool[i]->getFilename()));
 
-        // Set the longest model name from all loaded model assets, required for setting popup sizes when showing the list of model
+        // Set the longest model name from all loaded model assets, required for setting popup sizes when showing the list of models
         if(m_modelAssetLongestName.size() < modelPool[i]->getFilename().size())
             m_modelAssetLongestName = modelPool[i]->getFilename();
     }
+
+
+    //	 ____________________________
+    //	|							 |
+    //	|	    SHADER ASSETS        |
+    //	|____________________________|
+    //
+    // Clear shader asset array
+    m_shaderAssets.clear();
+
+    // Go over each shader in the loaders shader pool
+    const auto &shaderPool = Loaders::shader().getObjectPool();
+    for(decltype(shaderPool.size()) i = 0, size = shaderPool.size(); i < size; i++)
+    {
+        // Set the shader name based on the types of shader present
+        std::string shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_Fragment);
+        if(shaderName.empty())
+        {
+            shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_Vertex);
+            if(shaderName.empty())
+            {
+                shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_Compute);
+                if(shaderName.empty())
+                {
+                    shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_Geometry);
+                    if(shaderName.empty())
+                    {
+                        shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_TessControl);
+                        if(shaderName.empty())
+                        {
+                            shaderName = shaderPool[i]->getShaderFilename(ShaderType::ShaderType_TessEvaluation);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Remove the extension from the shader filename
+        shaderName = Utilities::removeExtension(shaderName);
+
+        // Add the shader and name entry
+        m_shaderAssets.push_back(std::make_pair(shaderPool[i], shaderName));
+
+        // Set the longest shader name from all loaded shader assets, required for setting popup sizes when showing the list of shaders
+        if(m_shaderAssetLongestName.size() < shaderName.size())
+            m_shaderAssetLongestName = shaderName;
+    }
+}
+
+void EditorWindow::generateNewMap(PropertySet &p_newSceneProperties, SceneData &p_sceneData)
+{
+    // Add load in background flag
+    p_newSceneProperties.addProperty(Properties::LoadInBackground, p_sceneData.m_loadInBackground);
+
+    // Add root property set game objects
+    auto &gameObjects = p_newSceneProperties.addPropertySet(Properties::GameObject);
+
+    // Create an array entry for root entity
+    auto &rootObjectEntry = gameObjects.addPropertySet(Properties::ArrayEntry);
+    rootObjectEntry.addProperty(Properties::PropertyID::Name, std::string("root"));
+    rootObjectEntry.addProperty(Properties::PropertyID::ID, 0);
+    rootObjectEntry.addProperty(Properties::PropertyID::Parent, 0);
+
+    // Create sun directional light
+    auto &dirLightObjectEntry = gameObjects.addPropertySet(Properties::ArrayEntry);
+    dirLightObjectEntry.addProperty(Properties::PropertyID::Name, std::string("Sun"));
+    dirLightObjectEntry.addProperty(Properties::PropertyID::ID, 1);
+    dirLightObjectEntry.addProperty(Properties::PropertyID::Parent, 0);
+    dirLightObjectEntry.addPropertySet(Properties::PropertyID::World).addPropertySet(Properties::PropertyID::SpatialComponent);
+    auto &lightComponentEntry = dirLightObjectEntry.addPropertySet(Properties::PropertyID::Graphics).addPropertySet(Properties::PropertyID::LightComponent);
+    lightComponentEntry.addProperty(Properties::PropertyID::Type, Properties::PropertyID::DirectionalLight);
+    lightComponentEntry.addProperty(Properties::PropertyID::Intensity, 10.0f);
+
+    auto &editorCameraObjectEntry = gameObjects.addPropertySet(Properties::ArrayEntry);
+    editorCameraObjectEntry.addProperty(Properties::PropertyID::Name, std::string("EditorCamera"));
+    editorCameraObjectEntry.addProperty(Properties::PropertyID::ID, 2000000000);
+    editorCameraObjectEntry.addProperty(Properties::PropertyID::Parent, 0);
+    editorCameraObjectEntry.addPropertySet(Properties::PropertyID::Graphics).addPropertySet(Properties::PropertyID::CameraComponent);
+    editorCameraObjectEntry.addPropertySet(Properties::PropertyID::World).addPropertySet(Properties::PropertyID::SpatialComponent);
+    editorCameraObjectEntry.addPropertySet(Properties::PropertyID::Script).addPropertySet(Properties::PropertyID::LuaComponent).addProperty(Properties::PropertyID::Filename, std::string("Camera_free_object_spawn.lua"));
+
+    // Add root property set for systems
+    auto &rootSystemsPropertySet = p_newSceneProperties.addPropertySet(Properties::Systems);
+
+    // Add audio banks
+    if(!p_sceneData.m_audioBanks.empty())
+    {
+        auto &audioBanksPropertySet = rootSystemsPropertySet.addPropertySet(Properties::Audio).addPropertySet(Properties::Scene).addPropertySet(Properties::Banks);
+        for(auto &bankFilename : p_sceneData.m_audioBanks)
+        {
+            if(!bankFilename.empty())
+                audioBanksPropertySet.addPropertySet(Properties::ArrayEntry).addProperty(Properties::PropertyID::Filename, bankFilename);
+        }
+    }
+
+    // Add rendering passes
+    auto &graphicsScenePropertySet = rootSystemsPropertySet.addPropertySet(Properties::Graphics).addPropertySet(Properties::Scene);
+    RendererScene::exportRenderingPasses(graphicsScenePropertySet, p_sceneData.m_renderingPasses);
 }

+ 114 - 4
Praxis3D/Source/EditorWindow.h

@@ -25,6 +25,7 @@ public:
 		m_renderSceneToTexture = true;
 		m_GUISequenceEnabled = false;
 		m_LUAScriptingEnabled = true;
+		m_showNewMapWindow = false;
 		m_sceneState = EditorSceneState::EditorSceneState_Pause;
 		m_centerWindowSize = glm::ivec2(0);
 
@@ -38,12 +39,22 @@ public:
 
 		m_luaVariableTypeStrings = { "null", "bool", "int", "float", "double", "vec2i", "vec2f", "vec3f", "vec4f", "string", "propertyID" };
 
+		m_shaderTypeStrings = { "Compute", "Fragment", "Geometry", "Vertex", "Tessellation control", "Tessellation evaluation" };
+		m_selectedProgramShader = -1;
+		m_selectedShader = -1;
+
 		for(unsigned int i = 0; i < ObjectMaterialType::NumberOfMaterialTypes; i++)
 			m_physicalMaterialProperties.push_back(GetString(static_cast<ObjectMaterialType>(i)));
 
+		for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
+			m_renderingPassesTypeText.push_back(GetString(static_cast<RenderPassType>(i)));
+
+		m_newSceneSettingsTabFlags = 0;
+
 		m_fontSize = ImGui::GetFontSize();
 		m_buttonSizedByFont = ImVec2(m_fontSize, m_fontSize);
 		m_assetSelectionPopupImageSize = ImVec2(m_fontSize, m_fontSize) * Config::GUIVar().editor_asset_selection_button_size_multiplier;
+		m_textureAssetImageSize = ImVec2(Config::GUIVar().editor_asset_texture_button_size_x, Config::GUIVar().editor_asset_texture_button_size_y);
 	}
 	~EditorWindow();
 
@@ -108,6 +119,11 @@ public:
 					return m_selectedEntity.m_componentData.m_physicsComponents.m_rigidBodyConstructionInfo->m_collisionShapeSize;
 				}
 				break;
+				case Systems::Changes::Physics::Gravity:
+					{
+						return m_currentSceneData.m_gravity;
+					}
+					break;
 			}
 		}
 
@@ -242,6 +258,30 @@ public:
 				}
 				break;
 
+			case Systems::Changes::Audio::VolumeAmbient:
+				{
+					return m_currentSceneData.m_volume[AudioBusType::AudioBusType_Ambient];
+				}
+				break;
+
+			case Systems::Changes::Audio::VolumeMaster:
+				{
+					return m_currentSceneData.m_volume[AudioBusType::AudioBusType_Master];
+				}
+				break;
+
+			case Systems::Changes::Audio::VolumeMusic:
+				{
+					return m_currentSceneData.m_volume[AudioBusType::AudioBusType_Music];
+				}
+				break;
+
+			case Systems::Changes::Audio::VolumeSFX:
+				{
+					return m_currentSceneData.m_volume[AudioBusType::AudioBusType_SFX];
+				}
+				break;
+
 			case Systems::Changes::Graphics::CutoffAngle:
 				{
 					if(m_selectedEntity.m_lightType == LightComponent::LightComponentType::LightComponentType_spot)
@@ -479,6 +519,35 @@ private:
 		bool m_modelDataModified;
 		bool m_soundFilenameModified;
 	};
+	struct SceneData
+	{
+		SceneData()
+		{
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Geometry);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Lighting);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Bloom);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Luminance);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_GUI);
+
+			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
+				m_volume[i] = 1.0f;
+
+			m_gravity = glm::vec3(0.0f, -9.8f, 0.0f);
+			m_loadInBackground = false;
+			m_modified = true;
+		}
+
+		RenderingPasses m_renderingPasses;
+		std::vector<std::string> m_audioBanks;
+
+		float m_volume[AudioBusType::AudioBusType_NumOfTypes];
+		glm::vec3 m_gravity;
+		bool m_loadInBackground;
+		bool m_modified;
+	};
 
 	enum ButtonTextureType : unsigned int
 	{
@@ -492,6 +561,7 @@ private:
 		ButtonTextureType_OpenFile,
 		ButtonTextureType_Reload,
 		ButtonTextureType_OpenAssetList,
+		ButtonTextureType_ArrowUp,
 		ButtonTextureType_NumOfTypes
 	};
 	enum EditorSceneState : unsigned int
@@ -507,9 +577,11 @@ private:
 		FileBrowserActivated_SaveScene,
 		FileBrowserActivated_SoundFile,
 		FileBrowserActivated_ModelFile,
-		FileBrowserActivated_TextureFile
+		FileBrowserActivated_TextureFile,
+		FileBrowserActivated_AudioBankFile
 	};
 
+	void drawSceneData(SceneData &p_sceneData, const bool p_sendChanges = false);
 	void drawEntityHierarchyEntry(EntityHierarchyEntry &p_entityEntry);
 	inline void drawLeftAlignedLabelText(const char *p_labelText, float p_nextWidgetOffset)
 	{
@@ -548,6 +620,27 @@ private:
 
 		return returnBool;
 	}
+	inline bool drawTextSizedButtonInverted(const TextureLoader2D::Texture2DHandle &p_texture, const std::string &p_buttonLabel, const std::string p_tooltipText = "")
+	{
+		bool returnBool = false;
+
+		// Draw the open button
+		if(ImGui::ImageButton(p_buttonLabel.c_str(),
+			(ImTextureID)p_texture.getHandle(),
+			m_buttonSizedByFont,
+			ImVec2(1, 0),
+			ImVec2(0, 1),
+			ImVec4(0.0f, 0.0f, 0.0f, 0.0f)))
+		{
+			returnBool = true;
+		}
+
+		// Draw the tooltip if the tooltip text is not empty and button is hovered over
+		if(!p_tooltipText.empty() && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
+			ImGui::SetTooltip(p_tooltipText.c_str(), ImGui::GetStyle().HoverDelayShort);
+
+		return returnBool;
+	}
 
 	// Calculates the offset for a square text-sized button from the right side of the edge 
 	// (p_buttonIndex is the button count from the right side)
@@ -560,11 +653,14 @@ private:
 		return m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x + (m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x * 3) * p_buttonIndex;
 	}
 
+	void updateSceneData(SceneData &p_sceneData);
 	void updateEntityList();
 	void updateHierarchyList();
 	void updateComponentList();
 	void updateAssetLists();
 
+	void generateNewMap(PropertySet &p_newSceneProperties, SceneData &p_sceneData);
+
 	inline std::string getTextureFormatString(const TextureFormat p_textureFormat) const
 	{
 		switch(p_textureFormat)
@@ -681,6 +777,7 @@ private:
 	bool m_renderSceneToTexture;
 	bool m_GUISequenceEnabled;
 	bool m_LUAScriptingEnabled;
+	bool m_showNewMapWindow;
 	EditorSceneState m_sceneState;
 	glm::ivec2 m_centerWindowSize;
 	std::vector<const char *> m_physicalMaterialProperties;
@@ -692,18 +789,24 @@ private:
 	FileBrowserDialog m_fileBrowserDialog;
 	const ImVec2 m_playPauseButtonSize;
 	ImVec2 m_assetSelectionPopupImageSize;
+	ImVec2 m_textureAssetImageSize;
 
 	// LUA variables editor data
 	std::vector<const char *> m_luaVariableTypeStrings;
 
 	// Assets variables
-	std::vector<std::pair<Texture2D *, std::string>> m_textureAssets;
-	std::vector<std::pair<Model *, std::string>> m_modelAssets;
+	std::vector<const char *> m_shaderTypeStrings;
+	std::vector<std::pair<const Texture2D *, std::string>> m_textureAssets;
+	std::vector<std::pair<const Model *, std::string>> m_modelAssets;
+	std::vector<std::pair<const ShaderLoader::ShaderProgram *, std::string>> m_shaderAssets;
 	std::string m_textureAssetLongestName;
 	std::string m_modelAssetLongestName;
+	std::string m_shaderAssetLongestName;
+	int m_selectedProgramShader;
+	int m_selectedShader;
 
 	// Texture inspector variables
-	Texture2D *m_selectedTexture;
+	Texture2D const * m_selectedTexture;
 	ImGuiTabItemFlags m_textureInspectorTabFlags;
 	DoubleBufferedContainer<FunctorSequence> m_textureInspectorSequence;
 
@@ -712,10 +815,17 @@ private:
 	std::vector<EntityListEntry> m_entityList;
 	std::vector<EntityHierarchyEntry> m_entityHierarchy;
 	SelectedEntity m_selectedEntity;
+	SceneData m_currentSceneData;
+
+	// New scene settings
+	SceneData m_newSceneData;
+	ImGuiTabItemFlags m_newSceneSettingsTabFlags;
+	std::vector<const char *> m_renderingPassesTypeText;
 
 	// Button textures
 	std::vector<TextureLoader2D::Texture2DHandle> m_buttonTextures;
 
+	// ImGui properties
 	ImGuiStyle &m_imguiStyle;
 	float m_fontSize;
 

+ 38 - 14
Praxis3D/Source/Engine.cpp

@@ -141,47 +141,71 @@ void Engine::processEngineChanges()
 	if(changeControllerScene->getEngineChangePending())
 	{
 		// Go over each engine change
-		auto &engineChanges = changeControllerScene->getEngineChangeQueue();
+		auto engineChanges = changeControllerScene->getEngineChangeQueue();
 		for(auto const &change : engineChanges)
 		{
 			switch(change.m_changeType)
 			{
 				case EngineChangeType_SceneFilename:
 					{
+						// Create the state if it hasn't been created already
+						if(m_engineStates[change.m_engineStateType] == nullptr)
+							createState(&m_engineStates[change.m_engineStateType], change.m_engineStateType);
+
 						m_engineStates[change.m_engineStateType]->setSceneFilename(change.m_filename);
 					}
 					break;
 				case EngineChangeType_SceneLoad:
 					{
-						if(m_engineStates[change.m_engineStateType] != nullptr)
+						if(initializeState(change.m_engineStateType))
 						{
-							// Load the scene
-							ErrorCode loadError = m_engineStates[change.m_engineStateType]->load();
+							bool stateLoaded = false;
 
-							// If it failed to load, log an error
-							if(loadError != ErrorCode::Success)
-								ErrHandlerLoc::get().log(loadError, getEngineStateTypeString(change.m_engineStateType), ErrorSource::Source_Engine);
+							if(change.m_sceneProperties)
+								loadState(change.m_engineStateType, change.m_sceneProperties);
+							else
+								loadState(change.m_engineStateType, change.m_filename);
 						}
-						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)
+						if(m_engineStates[change.m_engineStateType] != nullptr)
 						{
-							delete m_engineStates[m_currentStateType];
-							m_engineStates[m_currentStateType] = nullptr;
+							delete m_engineStates[change.m_engineStateType];
+							m_engineStates[change.m_engineStateType] = nullptr;
 						}
 
-						setCurrentStateType(m_currentStateType);
+						if(initializeState(change.m_engineStateType))
+						{
+							bool stateLoaded = false;
+
+							if(change.m_sceneProperties)
+								stateLoaded = loadState(change.m_engineStateType, change.m_sceneProperties);
+							else
+								stateLoaded = loadState(change.m_engineStateType, change.m_filename);
+
+							if(stateLoaded)
+								setCurrentState(change.m_engineStateType);
+						}
 						return;
 					}
 					break;
 				case EngineChangeType_StateChange:
 					{
-						setCurrentStateType(change.m_engineStateType);
+						if(initializeState(change.m_engineStateType))
+						{
+							bool stateLoaded = false;
+
+							if(change.m_sceneProperties)
+								stateLoaded = loadState(change.m_engineStateType, change.m_sceneProperties);
+							else
+								stateLoaded = loadState(change.m_engineStateType, change.m_filename);
+
+							if(stateLoaded)
+								setCurrentState(change.m_engineStateType);
+						}
 					}
 					break;
 			}

+ 80 - 0
Praxis3D/Source/Engine.h

@@ -81,6 +81,86 @@ private:
 		}
 	}
 
+	bool initializeState(const EngineStateType p_newStateType)
+	{
+		bool stateInitialized = true;
+
+		// 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_engineStates[p_newStateType]->isInitialized())
+		{
+			// Initialize the current state
+			ErrorCode stateInitError = m_engineStates[p_newStateType]->init(m_taskManager);
+
+			// If it failed to initialize, log an error
+			if(stateInitError != ErrorCode::Success)
+			{
+				ErrHandlerLoc::get().log(stateInitError, ErrorSource::Source_Engine);
+				stateInitialized = false;
+			}
+		}
+
+		return stateInitialized;
+	}
+
+	bool loadState(const EngineStateType p_stateType, const std::string &p_sceneFilename = "")
+	{
+		bool stateLoaded = true;
+
+		// Check if the scene has been created and initialized
+		if(m_engineStates[p_stateType] != nullptr && m_engineStates[p_stateType]->isInitialized())
+		{
+			if(!p_sceneFilename.empty())
+				m_engineStates[p_stateType]->setSceneFilename(p_sceneFilename);
+
+			// Load the scene
+			ErrorCode loadError = m_engineStates[p_stateType]->load();
+
+			// If it failed to load, log an error
+			if(loadError != ErrorCode::Success)
+			{
+				ErrHandlerLoc::get().log(loadError, getEngineStateTypeString(p_stateType), ErrorSource::Source_Engine);
+				stateLoaded = false;
+			}
+		}
+
+		return stateLoaded;
+	}
+	bool loadState(const EngineStateType p_stateType, const PropertySet &p_propertySet)
+	{
+		bool stateLoaded = true;
+
+		// Check if the scene has been created and initialized
+		if(m_engineStates[p_stateType] != nullptr && m_engineStates[p_stateType]->isInitialized())
+		{
+			// Load the scene
+			ErrorCode loadError = m_engineStates[p_stateType]->load(p_propertySet);
+
+			// If it failed to load, log an error
+			if(loadError != ErrorCode::Success)
+			{
+				ErrHandlerLoc::get().log(loadError, getEngineStateTypeString(p_stateType), ErrorSource::Source_Engine);
+				stateLoaded = false;
+			}
+		}
+
+		return stateLoaded;
+	}
+
+	void setCurrentState(const EngineStateType p_newStateType)
+	{
+		if(m_engineStates[m_currentStateType] != nullptr)
+			m_engineStates[m_currentStateType]->deactivate();
+
+		if(m_engineStates[p_newStateType] != nullptr)
+			m_engineStates[p_newStateType]->activate();
+
+		m_currentStateType = p_newStateType;
+		Config::m_engineVar.engineState = m_currentStateType;
+	}
+
 	// Creates and initializes all the services and their locators
 	ErrorCode initServices();
 

+ 1 - 1
Praxis3D/Source/EngineState.cpp

@@ -8,7 +8,7 @@
 #include "ScriptSystem.h"
 #include "WorldSystem.h"
 
-EngineState::EngineState(Engine &p_engine, EngineStateType p_engineState) : m_engine(p_engine), m_engineStateType(p_engineState), m_initialized(false)
+EngineState::EngineState(Engine &p_engine, EngineStateType p_engineState) : m_engine(p_engine), m_engineStateType(p_engineState), m_initialized(false), m_loaded(false)
 {
 	m_sceneChangeController = nullptr;
 	m_objectChangeController = nullptr;

+ 3 - 0
Praxis3D/Source/EngineState.h

@@ -20,6 +20,8 @@ public:
 
 	virtual ErrorCode load() = 0;
 
+	virtual ErrorCode load(const PropertySet &p_sceneProperty) = 0;
+
 	virtual void update(Engine &p_engine) = 0;
 
 	virtual void activate();
@@ -38,6 +40,7 @@ public:
 
 protected:
 	bool m_initialized;
+	bool m_loaded;
 	EngineStateType m_engineStateType;
 
 	// Reference to engine, used for getting systems

+ 1 - 0
Praxis3D/Source/Filesystem.h

@@ -63,6 +63,7 @@ public:
 		return std::filesystem::exists(p_file);
 	}
 
+	// Returns the current working directory
 	static std::string getCurrentDirectory()
 	{
 		std::filesystem::path path = std::filesystem::current_path();

+ 26 - 0
Praxis3D/Source/GUIScene.h

@@ -43,6 +43,7 @@ struct FileBrowserDialog
 
 	FileBrowserDialog()
 	{
+		m_userStringPointer = nullptr;
 		m_opened = false;
 		m_closed = false;
 		m_success = false;
@@ -55,6 +56,7 @@ struct FileBrowserDialog
 	// This prepares the dialog to be opened again, without affecting the starting data
 	void reset()
 	{
+		m_userStringPointer = nullptr;
 		m_opened = false;
 		m_closed = false;
 		m_success = false;
@@ -63,6 +65,28 @@ struct FileBrowserDialog
 		m_filePathName.clear();
 	}
 
+	// Reset all values
+	void resetAll()
+	{
+		m_name.clear();
+		m_title.clear();
+		m_filter.clear();
+		m_rootPath.clear();
+		m_definedFilename.clear();
+		m_filePath.clear();
+		m_filename.clear();
+		m_filePathName.clear();
+
+		m_userStringPointer = nullptr;
+
+		m_numOfSelectableFiles = 1;
+		m_flags = FileBrowserDialogFlags_None;
+
+		m_opened = false;
+		m_closed = false;
+		m_success = false;
+	}
+
 	std::string m_name,
 				m_title,
 				m_filter,
@@ -72,6 +96,8 @@ struct FileBrowserDialog
 				m_filename,
 				m_filePathName;
 
+	std::string *m_userStringPointer;
+
 	int m_numOfSelectableFiles;
 	unsigned int m_flags;
 

+ 11 - 0
Praxis3D/Source/LuaScript.cpp

@@ -284,6 +284,10 @@ void LuaScript::setFunctions()
 	m_luaState.set_function("setEngineRunning", [](const bool p_v1) -> const void {Config::m_engineVar.running = 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)); });
 
+	m_luaState.set_function("sendEngineChange", sol::overload([this](const EngineChangeType p_v1) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(p_v1)); },
+		[this](const EngineChangeType p_v1, const EngineStateType p_v2) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(p_v1, p_v2)); },
+		[this](const EngineChangeType p_v1, const EngineStateType p_v2, const std::string p_v3) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(p_v1, p_v2, p_v3)); }));
+
 	// GUI functions
 	auto GUITable = m_luaState.create_table("GUI");
 	GUITable.set_function("Begin", sol::overload([this](const std::string &p_v1) -> const void { m_GUIData.addFunctor([=] { ImGui::Begin(p_v1.c_str()); }); },
@@ -347,6 +351,13 @@ void LuaScript::setUsertypes()
 		"Play", EngineStateType::EngineStateType_Play,
 		"Editor", EngineStateType::EngineStateType_Editor);
 
+	m_luaState.new_enum("EngineChangeType",
+		"None", EngineChangeType::EngineChangeType_None,
+		"SceneFilename", EngineChangeType::EngineChangeType_SceneFilename,
+		"SceneLoad", EngineChangeType::EngineChangeType_SceneLoad,
+		"SceneReload", EngineChangeType::EngineChangeType_SceneReload,
+		"StateChange", EngineChangeType::EngineChangeType_StateChange);
+
 	// Config variables
 	m_luaState.new_usertype<Config::EngineVariables>("EngineVariables",
 		"change_ctrl_cml_notify_list_reserv", &Config::EngineVariables::change_ctrl_cml_notify_list_reserv,

+ 21 - 2
Praxis3D/Source/MainMenuState.h

@@ -13,14 +13,33 @@ public:
 
 	ErrorCode load()
 	{
-		ErrorCode returnError = ErrorCode::Initialize_failure;
+		ErrorCode returnError = ErrorCode::Success;
 
-		if(m_initialized)
+		if(m_initialized && !m_loaded)
 		{
 			// 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);
+			else
+				m_loaded = true;
+		}
+
+		return returnError;
+	}
+
+	ErrorCode load(const PropertySet &p_sceneProperty)
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		if(m_initialized && !m_loaded)
+		{
+			// Load the scene map, and log an error if it wasn't successful
+			returnError = m_sceneLoader.loadFromProperties(p_sceneProperty);
+			if(returnError != ErrorCode::Success)
+				ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
+			else
+				m_loaded = true;
 		}
 
 		return returnError;

+ 3 - 4
Praxis3D/Source/Math.h

@@ -97,10 +97,12 @@ namespace Math
 
 	const inline glm::vec3 toGlmVec3(const btVector3 &p_vec) noexcept { return glm::vec3(p_vec.getX(), p_vec.getY(), p_vec.getZ()); }
 
-	const inline glm::vec4 toGlmVec4(const glm::quat &p_quat) noexcept { return glm::vec4(p_quat.x, p_quat.y, p_quat.z, p_quat.w); }
+	const inline glm::vec4 toGlmVec4(const glm::quat &p_quat) noexcept { return glm::vec4(p_quat.w, p_quat.x, p_quat.y, p_quat.z); }
 
 	const inline glm::quat toGlmQuat(const btQuaternion &p_quat) noexcept { return glm::quat(p_quat.getW(), p_quat.getX(), p_quat.getY(), p_quat.getZ()); }
 
+	const inline glm::quat toGlmQuat(const glm::vec4 &p_vec) noexcept { return glm::quat(p_vec.x, p_vec.y, p_vec.z, p_vec.w); }
+
 	const inline btQuaternion toBtQuaternion(const glm::quat &p_quat) noexcept { return btQuaternion(p_quat.x, p_quat.y, p_quat.z, p_quat.w); }
 
 	const inline btMatrix3x3 toBtMatrix3x3(const glm::mat3 &p_mat) noexcept { return btMatrix3x3(p_mat[0][0], p_mat[1][0], p_mat[2][0], p_mat[0][1], p_mat[1][1], p_mat[2][1], p_mat[0][2], p_mat[1][2], p_mat[2][2]); }
@@ -146,7 +148,4 @@ namespace Math
 			p_mat[8], p_mat[9], p_mat[10], p_mat[11],
 			p_mat[12], p_mat[13], p_mat[14], p_mat[15]);
 	}
-
-	//float toRadian(const float p_degrees) { return glm::radians(p_degrees); }
-	//glm::quat test;
 }

+ 8 - 0
Praxis3D/Source/PhysicsScene.cpp

@@ -516,3 +516,11 @@ ErrorCode PhysicsScene::destroyObject(SystemObject *p_systemObject)
 	// If this point is reached, no object was found, return an appropriate error
 	return ErrorCode::Destroy_obj_not_found;
 }
+
+void PhysicsScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+{
+	if(CheckBitmask(p_changeType, Systems::Changes::Physics::Gravity))
+	{
+		m_dynamicsWorld->setGravity(Math::toBtVector3(p_subject->getVec3(this, Systems::Changes::Physics::Gravity)));
+	}
+}

+ 3 - 1
Praxis3D/Source/PhysicsScene.h

@@ -133,7 +133,7 @@ public:
 
 	ErrorCode destroyObject(SystemObject *p_systemObject);
 
-	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType);
 
 	void receiveData(const DataType p_dataType, void *p_data, const bool p_deleteAfterReceiving)
 	{
@@ -150,6 +150,8 @@ public:
 	BitMask getDesiredSystemChanges() { return Systems::Changes::Generic::CreateObject || Systems::Changes::Generic::DeleteObject; }
 	BitMask getPotentialSystemChanges() { return Systems::Changes::None; }
 
+	const glm::vec3 getGravity() const { return Math::toGlmVec3(m_dynamicsWorld->getGravity()); }
+
 	// Fluch the collision contacts of a rigid body (used after changing the collision shape dimensions)
 	void cleanProxyFromPairs(btRigidBody &p_rigidBody)
 	{

+ 21 - 2
Praxis3D/Source/PlayState.h

@@ -13,14 +13,33 @@ public:
 
 	ErrorCode load()
 	{
-		ErrorCode returnError = ErrorCode::Initialize_failure;
+		ErrorCode returnError = ErrorCode::Success;
 
-		if(m_initialized)
+		if(m_initialized && !m_loaded)
 		{
 			// 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);
+			else
+				m_loaded = true;
+		}
+
+		return returnError;
+	}
+
+	ErrorCode load(const PropertySet &p_sceneProperty)
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		if(m_initialized && !m_loaded)
+		{
+			// Load the scene map, and log an error if it wasn't successful
+			returnError = m_sceneLoader.loadFromProperties(p_sceneProperty);
+			if(returnError != ErrorCode::Success)
+				ErrHandlerLoc::get().log(returnError, ErrorSource::Source_SceneLoader);
+			else
+				m_loaded = true;
 		}
 
 		return returnError;

+ 25 - 2
Praxis3D/Source/PropertySet.h

@@ -508,7 +508,7 @@ public:
 	const inline bool operator<(const Property &p_property) const noexcept { return (m_propertyID < p_property.m_propertyID); }
 
 	// Assignment operator
-	Property &operator=(const Property &p_property) noexcept
+	inline Property &operator=(const Property &p_property) noexcept
 	{
 		m_propertyID = p_property.m_propertyID;
 		m_variableType = p_property.m_variableType;
@@ -753,6 +753,26 @@ public:
 		return combinedSet;
 	}
 
+	// Assignment operator, simply copies everything from the passed PropertySet
+	inline PropertySet &operator=(const PropertySet &p_propertySet) noexcept
+	{
+		// Copy variables
+		m_optimizedForSearch = p_propertySet.m_optimizedForSearch;
+		m_propertyID = p_propertySet.m_propertyID;
+		m_numProperties = p_propertySet.m_numProperties;
+		m_numPropertySets = p_propertySet.m_numPropertySets;
+
+		// Clear the old property vectors
+		m_properties.clear();
+		m_propertySets.clear();
+
+		// Copy the new property vectors
+		m_properties = p_propertySet.m_properties;
+		m_propertySets = p_propertySet.m_propertySets;
+
+		return *this;
+	}
+
 	// Setters
 	const inline void setPropertyID(const Properties::PropertyID p_propertyID) { m_propertyID = p_propertyID; }
 
@@ -853,7 +873,10 @@ public:
 private:
 	struct PropertyContainer
 	{
-		PropertyContainer() : m_type(PropertyContainerType::Type_Null) { }
+		PropertyContainer() : m_type(PropertyContainerType::Type_Null)
+		{
+			m_propertySet.m_property = nullptr;
+		}
 		PropertyContainer(Property *p_property) : m_type(PropertyContainerType::Type_SingleProperty) 
 		{ 
 			m_propertySet.m_property = p_property; 

+ 0 - 94
Praxis3D/Source/RendererFrontend.cpp

@@ -173,76 +173,10 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 {
 	m_renderingPassesSet = true;
 
-	// Make sure the entries of the rendering passes are set to nullptr
-	//for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
-	//{
-	//	if(m_initializedRenderingPasses[i] != nullptr)
-	//		delete m_initializedRenderingPasses[i];
-
-	//	m_initializedRenderingPasses[i] = nullptr;
-	//}
-
 	m_activeRenderPasses.clear();
 
 	bool guiRenderPassSet = false;
 
-	// Create rendering passes
-	//for(decltype(p_renderingPasses.size()) i = 0, size = p_renderingPasses.size(); i < size; i++)
-	//{
-	//	switch(p_renderingPasses[i])
-	//	{
-	//	case RenderPassType_Geometry:
-	//		if(m_initializedRenderingPasses[RenderPassType_Geometry] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Geometry] = new GeometryPass(*this);
-	//		break;
-	//	case RenderPassType_Lighting:
-	//		if(m_initializedRenderingPasses[RenderPassType_Lighting] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Lighting] = new LightingPass(*this);
-	//		break;
-	//	case RenderPassType_AtmScattering:
-	//		if(m_initializedRenderingPasses[RenderPassType_AtmScattering] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_AtmScattering] = new AtmScatteringPass(*this);
-	//		break;
-	//	case RenderPassType_HdrMapping:
-	//		if(m_initializedRenderingPasses[RenderPassType_HdrMapping] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_HdrMapping] = new HdrMappingPass(*this);
-	//		break;
-	//	case RenderPassType_Blur:
-	//		if(m_initializedRenderingPasses[RenderPassType_Blur] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Blur] = new BlurPass(*this);
-	//		break;
-	//	case RenderPassType_Bloom:
-	//		if(m_initializedRenderingPasses[RenderPassType_Bloom] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Bloom] = new BloomPass(*this);
-	//		break;
-	//	case RenderPassType_BloomComposite:
-	//		if(m_initializedRenderingPasses[RenderPassType_BloomComposite] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_BloomComposite] = new BloomCompositePass(*this);
-	//		break;
-	//	case RenderPassType_LenseFlare:
-	//		if(m_initializedRenderingPasses[RenderPassType_LenseFlare] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_LenseFlare] = new LenseFlarePass(*this);
-	//		break;
-	//	case RenderPassType_LenseFlareComposite:
-	//		if(m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] = new LenseFlareCompositePass(*this);
-	//		break;
-	//	case RenderPassType_Luminance:
-	//		if(m_initializedRenderingPasses[RenderPassType_Luminance] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Luminance] = new LuminancePass(*this);
-	//		break;
-	//	case RenderPassType_Final:
-	//		if(m_initializedRenderingPasses[RenderPassType_Final] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_Final] = new FinalPass(*this);
-	//		break;
-	//	case RenderPassType_GUI:
-	//		if(m_initializedRenderingPasses[RenderPassType_GUI] == nullptr)
-	//			m_initializedRenderingPasses[RenderPassType_GUI] = new GUIPass(*this);
-	//		guiRenderPassSet = true;
-	//		break;
-	//	}
-	//}
-
 	for(decltype(p_renderingPasses.size()) i = 0, size = p_renderingPasses.size(); i < size; i++)
 	{
 		switch(p_renderingPasses[i])
@@ -333,34 +267,6 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 		}
 	}
 
-	// Initialize rendering passes
-	//for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
-	//{
-	//	// Check if has been created
-	//	if(m_initializedRenderingPasses[i] != nullptr)
-	//	{
-	//		// Initialize the rendering pass and check if it was successful
-	//		if(m_initializedRenderingPasses[i]->init() != ErrorCode::Success)
-	//		{
-	//			// Log an error and delete the rendering pass
-	//			ErrHandlerLoc::get().log(ErrorType::Error, ErrorSource::Source_Renderer, m_initializedRenderingPasses[i]->getName() + " failed to load.");
-	//			delete m_initializedRenderingPasses[i];
-	//			m_initializedRenderingPasses[i] = nullptr;
-	//		}
-	//	}
-	//}
-
-	//// Reserve the required space for rendering passes array
-	//m_renderingPasses.clear();
-	//m_renderingPasses.reserve(p_renderingPasses.size());
-
-	//// Add required rendering passes to the main array
-	//for(decltype(p_renderingPasses.size()) i = 0, size = p_renderingPasses.size(); i < size; i++)
-	//{
-	//	if(m_initializedRenderingPasses[p_renderingPasses[i]] != nullptr)
-	//		m_renderingPasses.push_back(m_initializedRenderingPasses[p_renderingPasses[i]]);
-	//}
-
 	//passLoadCommandsToBackend();
 	glViewport(0, 0, m_frameData.m_screenSize.x, m_frameData.m_screenSize.y);
 }

+ 3 - 1
Praxis3D/Source/RendererScene.cpp

@@ -107,6 +107,8 @@ void RendererScene::exportSetup(PropertySet &p_propertySet)
 	objectPoolSizePropertySet.addProperty(Properties::LightComponent, (int)worldScene->getPoolSize<LightComponent>());
 	objectPoolSizePropertySet.addProperty(Properties::ModelComponent, (int)worldScene->getPoolSize<ModelComponent>());
 	objectPoolSizePropertySet.addProperty(Properties::ShaderComponent, (int)worldScene->getPoolSize<ShaderComponent>());
+
+	exportRenderingPasses(p_propertySet, m_renderingPasses);
 }
 
 void RendererScene::activate()
@@ -706,7 +708,7 @@ ErrorCode RendererScene::destroyObject(SystemObject *p_systemObject)
 
 void RendererScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
 {
-	//std::cout << "change occurred" << std::endl;
+
 }
 
 void RendererScene::receiveData(const DataType p_dataType, void *p_data, const bool p_deleteAfterReceiving)

+ 48 - 1
Praxis3D/Source/RendererScene.h

@@ -213,7 +213,54 @@ public:
 	SystemTask *getSystemTask() { return m_renderTask; }
 	Systems::TypeID getSystemType() { return Systems::Graphics; }
 	inline SceneObjects &getSceneObjects() { return m_sceneObjects; }
-	
+	const inline RenderingPasses &getRenderingPasses() const { return m_renderingPasses; }
+
+	static void exportRenderingPasses(PropertySet &p_propertySet, const RenderingPasses &p_renderingPasses)
+	{
+		// Create the render passes Property Set entry
+		auto &RenderPassesPropertySet = p_propertySet.addPropertySet(Properties::RenderPasses);
+
+		// Go over each rendering pass
+		for(auto renderPassType : p_renderingPasses)
+		{
+			// Convert RenderPassType to PropertyID
+			Properties::PropertyID renderPassTypeProperty = Properties::Null;
+			switch(renderPassType)
+			{
+				case RenderPassType::RenderPassType_AtmScattering:
+					renderPassTypeProperty = Properties::AtmScatteringRenderPass;
+					break;
+				case RenderPassType::RenderPassType_Bloom:
+					renderPassTypeProperty = Properties::BloomRenderPass;
+					break;
+				case RenderPassType::RenderPassType_Geometry:
+					renderPassTypeProperty = Properties::GeometryRenderPass;
+					break;
+				case RenderPassType::RenderPassType_GUI:
+					renderPassTypeProperty = Properties::GUIRenderPass;
+					break;
+				case RenderPassType::RenderPassType_Lighting:
+					renderPassTypeProperty = Properties::LightingRenderPass;
+					break;
+				case RenderPassType::RenderPassType_Luminance:
+					renderPassTypeProperty = Properties::LuminanceRenderPass;
+					break;
+				case RenderPassType::RenderPassType_Final:
+					renderPassTypeProperty = Properties::FinalRenderPass;
+					break;
+			}
+
+			// Add the rendering pass array entry and rendering pass type
+			RenderPassesPropertySet.addPropertySet(Properties::ArrayEntry).addProperty(Properties::Type, renderPassTypeProperty);
+		}
+	}
+	static std::string getRenderingPassString(const RenderPassType p_renderPassType)
+	{
+		std::string returnString;
+
+
+	}
+
 private:
 	MaterialData loadMaterialData(PropertySet &p_materialProperty, Model::MaterialArrays &p_materialArraysFromModel, MaterialType p_materialType, std::size_t p_meshIndex);
 

+ 0 - 38
Praxis3D/Source/RendererSystem.cpp

@@ -83,45 +83,7 @@ ErrorCode RendererSystem::setup(const PropertySet &p_properties)
 
 void RendererSystem::exportSetup(PropertySet &p_propertySet)
 {
-	// Create the render passes Property Set entry
-	auto &RenderPassesPropertySet = p_propertySet.addPropertySet(Properties::RenderPasses);
 
-	// Get the rendering passes from the renderer
-	auto renderingPasses = m_renderer.getRenderingPasses();
-
-	// Go over each rendering pass
-	for(auto renderPassType : renderingPasses)
-	{
-		// Convert RenderPassType to PropertyID
-		Properties::PropertyID renderPassTypeProperty = Properties::Null;
-		switch(renderPassType)
-		{
-			case RenderPassType::RenderPassType_AtmScattering:
-				renderPassTypeProperty = Properties::AtmScatteringRenderPass;
-				break;
-			case RenderPassType::RenderPassType_Bloom:
-				renderPassTypeProperty = Properties::BloomRenderPass;
-				break;
-			case RenderPassType::RenderPassType_Geometry:
-				renderPassTypeProperty = Properties::GeometryRenderPass;
-				break;
-			case RenderPassType::RenderPassType_GUI:
-				renderPassTypeProperty = Properties::GUIRenderPass;
-				break;
-			case RenderPassType::RenderPassType_Lighting:
-				renderPassTypeProperty = Properties::LightingRenderPass;
-				break;
-			case RenderPassType::RenderPassType_Luminance:
-				renderPassTypeProperty = Properties::LuminanceRenderPass;
-				break;
-			case RenderPassType::RenderPassType_Final:
-				renderPassTypeProperty = Properties::FinalRenderPass;
-				break;
-		}
-
-		// Add the rendering pass array entry and rendering pass type
-		RenderPassesPropertySet.addPropertySet(Properties::ArrayEntry).addProperty(Properties::Type, renderPassTypeProperty);
-	}
 }
 
 ErrorCode RendererSystem::preload()

+ 79 - 110
Praxis3D/Source/SceneLoader.cpp

@@ -19,136 +19,81 @@ SceneLoader::~SceneLoader()
 {
 }
 
-ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
+ErrorCode SceneLoader::loadFromProperties(const PropertySet &p_sceneProperties)
 {
 	ErrorCode returnError = ErrorCode::Success;
-	m_filename = p_filename;
 
 	EntitiesConstructionInfo constructionInfo;
 
-	// Load properties from file
-	PropertyLoader loadedProperties(Config::filepathVar().map_path + p_filename);
-	returnError = loadedProperties.loadFromFile();
+	// Get systems property set
+	auto &systemProperties = p_sceneProperties.getPropertySetByID(Properties::Systems);
 
-	// Check if loading was successful, return an error, if not
-	if(returnError != ErrorCode::Success)
+	// Iterate over all systems scenes
+	for(int sysIndex = 0; sysIndex < Systems::NumberOfSystems; sysIndex++)
 	{
-		ErrHandlerLoc().get().log(returnError, ErrorSource::Source_SceneLoader);
-	}
-	else
-	{
-		// Get systems property set
-		auto &systemProperties = loadedProperties.getPropertySet().getPropertySetByID(Properties::Systems);
+		// Create an empty property set, in case there is none in the loaded file, because a system scene setup must be called either way
+		PropertySet scenePropertySet;
+		PropertySet systemropertySet;
 
-		// Iterate over all systems scenes
-		for(int sysIndex = 0; sysIndex < Systems::NumberOfSystems; sysIndex++)
+		// Iterate over each system property set
+		for(decltype(systemProperties.getNumPropertySets()) propIndex = 0, propSize = systemProperties.getNumPropertySets(); propIndex < propSize; propIndex++)
 		{
-			// Create an empty property set, in case there is none in the loaded file, because a system scene setup must be called either way
-			PropertySet scenePropertySet;
-			PropertySet systemropertySet;
-
-			// Iterate over each system property set
-			for(decltype(systemProperties.getNumPropertySets()) propIndex = 0, propSize = systemProperties.getNumPropertySets(); propIndex < propSize; propIndex++)
+			// If the system scene property matches in the loaded file, retrieve it so it can be passed to the corresponding scene
+			if(Systems::SystemNames[m_systemScenes[sysIndex]->getSystemType()] == GetString(systemProperties.getPropertySetUnsafe(propIndex).getPropertyID()))
 			{
-				// If the system scene property matches in the loaded file, retrieve it so it can be passed to the corresponding scene
-				if(Systems::SystemNames[m_systemScenes[sysIndex]->getSystemType()] == GetString(systemProperties.getPropertySetUnsafe(propIndex).getPropertyID()))
-				{
-					scenePropertySet = systemProperties.getPropertySetUnsafe(propIndex).getPropertySetByID(Properties::Scene);
-					systemropertySet = systemProperties.getPropertySetUnsafe(propIndex).getPropertySetByID(Properties::System);
-				}
+				scenePropertySet = systemProperties.getPropertySetUnsafe(propIndex).getPropertySetByID(Properties::Scene);
+				systemropertySet = systemProperties.getPropertySetUnsafe(propIndex).getPropertySetByID(Properties::System);
 			}
-
-			// Pass the scene and system propertySet parameters
-			m_systemScenes[sysIndex]->getSystem()->setup(systemropertySet);
-			m_systemScenes[sysIndex]->setup(scenePropertySet);
 		}
 
-		// Get Game Objects
-		auto &gameObjects = loadedProperties.getPropertySet().getPropertySetByID(Properties::GameObject);
-		if(gameObjects)
-		{
-			// Reserve enough room for all the game objects
-			constructionInfo.resize(gameObjects.getNumPropertySets());
-
-			// Iterate over all game objects
-			for(decltype(gameObjects.getNumPropertySets()) objIndex = 0, objSize = gameObjects.getNumPropertySets(); objIndex < objSize; objIndex++)
-			{
-				// Import the game object data from PropertySets to EntitiesConstructionInfo
-				importFromProperties(constructionInfo[objIndex], gameObjects.getPropertySetUnsafe(objIndex));
-			}
+		// Pass the scene and system propertySet parameters
+		m_systemScenes[sysIndex]->getSystem()->setup(systemropertySet);
+		m_systemScenes[sysIndex]->setup(scenePropertySet);
+	}
 
-			// Get the world scene required for creating entities
-			WorldScene *worldScene = static_cast<WorldScene*>(m_systemScenes[Systems::World]);
+	// Get Game Objects
+	auto &gameObjects = p_sceneProperties.getPropertySetByID(Properties::GameObject);
+	if(gameObjects)
+	{
+		// Reserve enough room for all the game objects
+		constructionInfo.resize(gameObjects.getNumPropertySets());
 
-			// Go over each entity and create it
-			for(decltype(constructionInfo.size()) i = 0, size = constructionInfo.size(); i < size; i++)
-			{
-				worldScene->createEntity(constructionInfo[i], false);
-			}
-		}
-		else
+		// Iterate over all game objects
+		for(decltype(gameObjects.getNumPropertySets()) objIndex = 0, objSize = gameObjects.getNumPropertySets(); objIndex < objSize; objIndex++)
 		{
-			// GameObject property set is missing
-			returnError = ErrorCode::GameObjects_missing;
-			ErrHandlerLoc().get().log(ErrorCode::GameObjects_missing, ErrorSource::Source_SceneLoader);
+			// Import the game object data from PropertySets to EntitiesConstructionInfo
+			importFromProperties(constructionInfo[objIndex], gameObjects.getPropertySetUnsafe(objIndex));
 		}
 
-		/*/ Get the object link property sets
-		auto &objLinkProperties = loadedProperties.getPropertySet().getPropertySetByID(Properties::ObjectLinks);
-
-		// Iterate over all object link property sets
-		for(decltype(objLinkProperties.getNumPropertySets()) linkIndex = 0, linkSize = objLinkProperties.getNumPropertySets(); linkIndex < linkSize; linkIndex++)
-		{
-			// Get subject name
-			const auto &subjectName = objLinkProperties.getPropertySetUnsafe(linkIndex).getPropertyByID(Properties::Subject).getString();
-			if(subjectName.empty()) // Make sure subject's name is not empty
-				continue;
-
-			// Get observer name
-			const auto &observerName = objLinkProperties.getPropertySetUnsafe(linkIndex).getPropertyByID(Properties::Observer).getString();
-			if(observerName.empty()) // Make sure observer's name is not empty
-				continue;
-
-			// Iterate over created objects and match subject's name
-			for(decltype(createdObjects.size()) subjIndex = 0, subjSize = createdObjects.size(); subjIndex < subjSize; subjIndex++)
-			{
-				// Compare subject name
-				if(createdObjects[subjIndex].first == subjectName)
-				{
-					// Iterate over created objects and match observer's name
-					for(decltype(createdObjects.size()) observIndex = 0, observSize = createdObjects.size(); observIndex < observSize; observIndex++)
-					{
-						// Compare observer name
-						if(createdObjects[observIndex].first == observerName)
-						{
-							// Create object link between subject and observer in the change controller
-							m_changeController->createObjectLink(createdObjects[subjIndex].second, createdObjects[observIndex].second);
-
-							// Exit the inner loop
-							break;
-						}
-					}
-
-					// Exit the outer loop
-					break;
-				}
-			}
-		}*/
+		// Get the world scene required for creating entities
+		WorldScene *worldScene = static_cast<WorldScene *>(m_systemScenes[Systems::World]);
 
-		// Check if the scene should be loaded in background
-		if(loadedProperties.getPropertySet().getPropertyByID(Properties::LoadInBackground).getBool())
-		{
-			// Start loading in background threads in all scenes
-			for(int i = 0; i < Systems::NumberOfSystems; i++)
-				m_systemScenes[i]->loadInBackground();
-		}
-		else
+		// Go over each entity and create it
+		for(decltype(constructionInfo.size()) i = 0, size = constructionInfo.size(); i < size; i++)
 		{
-			// Preload all scenes sequentially
-			for(int i = 0; i < Systems::NumberOfSystems; i++)
-				m_systemScenes[i]->preload();
+			worldScene->createEntity(constructionInfo[i], false);
 		}
 	}
+	else
+	{
+		// GameObject property set is missing
+		returnError = ErrorCode::GameObjects_missing;
+		ErrHandlerLoc().get().log(ErrorCode::GameObjects_missing, ErrorSource::Source_SceneLoader);
+	}
+
+	// Check if the scene should be loaded in background
+	if(p_sceneProperties.getPropertyByID(Properties::LoadInBackground).getBool())
+	{
+		// Start loading in background threads in all scenes
+		for(int i = 0; i < Systems::NumberOfSystems; i++)
+			m_systemScenes[i]->loadInBackground();
+	}
+	else
+	{
+		// Preload all scenes sequentially
+		for(int i = 0; i < Systems::NumberOfSystems; i++)
+			m_systemScenes[i]->preload();
+	}
 
 	// Make sure to clear the memory of contructionInfo
 	for(decltype(constructionInfo.size()) i = 0, size = constructionInfo.size(); i < size; i++)
@@ -157,6 +102,30 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 	return returnError;
 }
 
+ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
+{
+	ErrorCode returnError = ErrorCode::Success;
+	m_filename = p_filename;
+
+	EntitiesConstructionInfo constructionInfo;
+
+	// Load properties from file
+	PropertyLoader loadedProperties(Config::filepathVar().map_path + p_filename);
+	returnError = loadedProperties.loadFromFile();
+
+	// Check if loading was successful, return an error, if not
+	if(returnError != ErrorCode::Success)
+	{
+		ErrHandlerLoc().get().log(returnError, ErrorSource::Source_SceneLoader);
+	}
+	else
+	{
+		returnError = loadFromProperties(loadedProperties.getPropertySet());
+	}
+
+	return returnError;
+}
+
 ErrorCode SceneLoader::saveToFile(const std::string p_filename)
 {
 	std::string filename;
@@ -399,7 +368,7 @@ void SceneLoader::importFromProperties(ComponentsConstructionInfo &p_constructio
 
 	// Load graphics components
 	{
-		auto &sceneProperty = p_properties.getPropertySetByID(Properties::Rendering);
+		auto &sceneProperty = p_properties.getPropertySetByID(Properties::Graphics);
 		if(sceneProperty)
 		{
 			for(decltype(sceneProperty.getNumPropertySets()) i = 0, size = sceneProperty.getNumPropertySets(); i < size; i++)
@@ -1027,7 +996,7 @@ void SceneLoader::importFromProperties(WorldComponentsConstructionInfo &p_constr
 						p_constructionInfo.m_spatialConstructionInfo->m_localRotationEuler = p_properties[i].getVec3f();
 						break;
 					case Properties::LocalRotationQuaternion:
-						p_constructionInfo.m_spatialConstructionInfo->m_localRotationQuaternion = glm::quat(p_properties[i].getVec4f());
+						p_constructionInfo.m_spatialConstructionInfo->m_localRotationQuaternion = Math::toGlmQuat(p_properties[i].getVec4f());
 						break;
 					case Properties::LocalScale:
 						p_constructionInfo.m_spatialConstructionInfo->m_localScale = p_properties[i].getVec3f();

+ 9 - 0
Praxis3D/Source/SceneLoader.h

@@ -36,6 +36,9 @@ public:
 	inline SystemScene *getSystemScene(Systems::TypeID p_systemType) const { return m_systemScenes[p_systemType]; }
 	inline UniversalScene *getChangeController() const { return m_changeController; }
 
+	// Load a scene from properties, pass it along to every system scene and create every entity
+	ErrorCode loadFromProperties(const PropertySet &p_sceneProperties);
+
 	// Load a scene from file, pass it along to every system scene and create every entity
 	ErrorCode loadFromFile(const std::string &p_filename);
 
@@ -54,6 +57,12 @@ public:
 	// Set the scene filename that is used when loading from file
 	inline void setSceneFilename(const std::string &p_filename) { m_filename = p_filename; }
 
+	// Returns the load-in-background flag
+	inline bool getLoadInBackground() const { return m_loadInBackground; }
+
+	// Set the load-in-background flag
+	inline void setLoadInBackground(const bool p_loadInBackground) { m_loadInBackground = p_loadInBackground; }
+
 private:
 	ErrorCode importFromFile(ComponentsConstructionInfo &p_constructionInfo, const std::string &p_filename);
 	void importFromProperties(ComponentsConstructionInfo &p_constructionInfo, const PropertySet &p_properties);

+ 3 - 1
Praxis3D/Source/ShaderLoader.h

@@ -224,7 +224,7 @@ public:
 		}
 
 		// Returns a filename of a shader of specific type
-		const inline std::string getShaderFilename(const unsigned int p_shaderType)
+		const inline std::string &getShaderFilename(const unsigned int p_shaderType) const
 		{
 			return m_shaderFilename[p_shaderType];
 
@@ -285,6 +285,8 @@ public:
 	inline ShaderProgram *load() { return &m_defaultProgram; }
 	ShaderProgram *load(const PropertySet &p_properties);
 
+	const inline std::vector<ShaderProgram *> &getObjectPool() const { return m_shaderPrograms; }
+
 private:
 	SpinWait	m_vertFragMutex,
 				m_geomMutex;

+ 0 - 10
Praxis3D/Source/Universal.cpp

@@ -227,16 +227,6 @@ 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)

+ 23 - 3
Praxis3D/Source/Universal.h

@@ -33,10 +33,30 @@ public:
 	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);
+	//void 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;
+	//}
+
+	// Queues an engine change that is processed before the next frame by the Engine
+	template<class... T_Args>
+	void sendEngineChange(T_Args&&... p_args)
+	{
+		// 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.emplace_back(std::forward<T_Args>(p_args)...);
+		m_engineChangePending = true;
+	}
 
 	// Sends a one-off notification about a change, without requiring the object linking
-	void sendChange(SystemObject *p_subject, SystemObject *p_observer, BitMask p_changedBits)
+	void sendChange(ObservedSubject *p_subject, Observer *p_observer, BitMask p_changedBits)
 	{
 		// Check if any changes have been done
 		//if(p_changedBits)
@@ -124,4 +144,4 @@ protected:
 	UniversalScene		*m_scene;
 	ChangeController	*m_objectChangeController;
 	SystemObjectMap		m_objectExtensions;
-};
+};

+ 1 - 1
Praxis3D/Source/Utilities.h

@@ -129,7 +129,7 @@ namespace Utilities
 	// Strip and return the directory path from a full file path in Windows environment
 	static std::string stripFilePath(const std::string &p_fullPath)
 	{
-		std::string returnFilename = p_fullPath;
+		std::string returnFilename = "";
 
 		// Make sure full path string is not empty
 		if(!p_fullPath.empty())

+ 112 - 15
Praxis3D/imgui.ini

@@ -4,9 +4,9 @@ Size=400,400
 Collapsed=0
 
 [Window][Dear ImGui Demo]
-Pos=102,374
-Size=507,545
-Collapsed=0
+Pos=37,390
+Size=461,545
+Collapsed=1
 
 [Window][Dear ImGui Stack Tool]
 Pos=60,60
@@ -85,25 +85,25 @@ Collapsed=0
 
 [Window][##LeftWindow]
 Pos=0,69
-Size=301,797
+Size=301,779
 Collapsed=0
 DockId=0x00000001,0
 
 [Window][##RightWindow]
-Pos=1458,69
-Size=462,1011
+Pos=1411,69
+Size=509,1011
 Collapsed=0
 DockId=0x00000008,0
 
 [Window][##BottomWindow]
-Pos=0,868
-Size=1456,212
+Pos=0,850
+Size=1409,230
 Collapsed=0
 DockId=0x00000006,0
 
 [Window][##CenterWindow]
 Pos=303,69
-Size=1153,797
+Size=1106,779
 Collapsed=0
 DockId=0x00000004,0
 
@@ -207,6 +207,11 @@ Pos=599,114
 Size=876,586
 Collapsed=0
 
+[Window][Select an Audio Bank file##OpenAudioBankFileDialog]
+Pos=145,151
+Size=855,618
+Collapsed=0
+
 [Table][0x9A4BDFDE,4]
 RefScale=13
 Column 0  Sort=0v
@@ -731,14 +736,106 @@ Column 0  Sort=0v
 RefScale=13
 Column 0  Sort=0v
 
+[Table][0x9270E4B4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x14AA0B15,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0xD40470D6,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0x0AC15F9D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3A0C0C56,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0x20E76501,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0xB9985B94,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0B11422A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x926E7CBF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3D513403,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD6F80844,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x04E7CA0C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x80222D7E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x195D13EB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x29CB95E7,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5BDC9158,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1262BA67,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5074BEAD,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBEFB7081,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x532E098B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8CE3C88A,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0xAC38CE54,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0x73F50F55,4]
+RefScale=13
+Column 0  Sort=0v
+
 [Docking][Data]
-DockSpace         ID=0x8B93E3BD Pos=0,69 Size=1920,1011 Split=X
-  DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1456,1011 Split=Y
-    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,725 Split=X
+DockSpace         ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,69 Size=1920,1011 Split=X
+  DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1409,1011 Split=Y
+    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,779 Split=X
       DockNode    ID=0x00000001 Parent=0x00000005 SizeRef=301,1011 Selected=0xB1B21E90
-      DockNode    ID=0x00000003 Parent=0x00000005 SizeRef=1153,1011 Split=X
+      DockNode    ID=0x00000003 Parent=0x00000005 SizeRef=1106,1011 Split=X
         DockNode  ID=0x00000002 Parent=0x00000003 SizeRef=332,1042 Selected=0xD00C9CD4
         DockNode  ID=0x00000004 Parent=0x00000003 SizeRef=1586,1042 CentralNode=1 Selected=0xFB1B6F35
-    DockNode      ID=0x00000006 Parent=0x00000007 SizeRef=1920,212 Selected=0x51C0BD29
-  DockNode        ID=0x00000008 Parent=0x8B93E3BD SizeRef=462,1011 Selected=0x96D2BCA2
+    DockNode      ID=0x00000006 Parent=0x00000007 SizeRef=1920,230 Selected=0x51C0BD29
+  DockNode        ID=0x00000008 Parent=0x8B93E3BD SizeRef=509,1011 Selected=0x96D2BCA2