Browse Source

Implemented Fmod; added templated execution in TaskScheduler

Implemented Fmod audio system
Added templated functionality to TaskScheduler and TaskManager, to be able to call any  method in SystemTask concurrently
Added SoundComponent to the audio scene, which can load (or stream) and play audio defined in a map file
Added AudioVariables in Config, for the audio system
Added activation/deactivation of EngineStates and SystemScenes, by submitting an appropriate call to SystemTask when EngineState is changed
Added a way to get a value from PropertySet, without modifying the said value if the PropertySet is not present (to keep default initialized variables in ConstructionInfo)
Added more ErrorCodes and ErrorSources for the audio system
Paul A 3 years ago
parent
commit
22ab5b123c

+ 4 - 3
.gitignore

@@ -1,8 +1,9 @@
 Praxis3D/x64/Debug 64bit/Praxis3D.vcxproj.FileListAbsolute.txt
-
 \.vs/Praxis3D/v16/ipch/
 VC/
-
+Dependencies/include/fmod/
+Dependencies/x64/Debug/lib/fmod/
+Dependencies/x64/Release/lib/fmod/
 *.dae
 *.jpeg
 *.jpg
@@ -34,4 +35,4 @@ Builds/x64/Debug/Praxis3D.lib
 Praxis3D/x64/Debug/CodeAnalysisResultManifest.txt
 .vs/
 Builds/x64/Debug/Praxis3D.exe
-Builds/x64/Debug/Praxis3D.exe
+Builds/x64/Debug/Praxis3D.exe

BIN
Builds/x64/Debug/fmod.dll


+ 12 - 0
Praxis3D/Data/Maps/mainMenu.pmap

@@ -25,6 +25,18 @@
 			"Name": "GUI",
 			"ID": "1",
 			"Parent": "0",
+			"Audio":
+			{
+				"SoundComponent":
+				{
+					"Filename": "chat.mp3",
+					"Type": "Music",
+					"Loop": "true",
+					"Spatialized": "false",
+					"StartPlaying": "true",
+					"Volume": "0.5f"
+				}
+			},
 			"GUI":
 			{
 				"Sequence":

+ 32 - 6
Praxis3D/Data/Scripts/MainMenu.lua

@@ -13,11 +13,18 @@ function init ()
 	create(Types.Conditional, 'exitButtonPressed')
 	create(Types.Conditional, 'textureButton')
 	
+	-- Engine logo texture
 	praxisLogoTexture = loadTexture2D('PraxisLogoFinal.png')
 	praxisLogoTexture:loadToMemory()
 	praxisLogoTexture:loadToVideoMemory()
 	
+	-- Fmod logo texture
+	fmodLogoTexture = loadTexture2D('FMOD Logo White - Black Background.png')
+	fmodLogoTexture:loadToMemory()
+	fmodLogoTexture:loadToVideoMemory()
+	
 	praxisLogoScale = 1
+	fmodLogoScale = 0.05
 	
 	buttonSizeX = 100.0
 	buttonSizeY = 25.0
@@ -45,10 +52,19 @@ function update (p_deltaTime)
 	buttonPositionX = halfScreenSizeX * buttonOffsetMultX
 	buttonPositionY = halfScreenSizeY * buttonOffsetMultY
 	
-	-- Calculate logo size based in texture and window size (try to fit it on screen vertically)
-	logoAdjustedSizeX = praxisLogoTexture:getTextureWidth() / (praxisLogoTexture:getTextureHeight() / graphicsVariables.current_resolution_y) * praxisLogoScale
-	logoAdjustedSizeY = praxisLogoTexture:getTextureHeight() / (praxisLogoTexture:getTextureHeight() / graphicsVariables.current_resolution_y) * praxisLogoScale
+	-- Calculate engine logo size based based on texture and window size (try to fit it on screen vertically)
+	engineLogoAdjustedSizeX = praxisLogoTexture:getTextureWidth() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
+	engineLogoAdjustedSizeY = praxisLogoTexture:getTextureHeight() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
 
+	-- Calculate fmod logo size based on texture and window size (so it scaled with the screen size)
+	fmodLogoAdjustedSizeMultAverage = ((fmodLogoTexture:getTextureHeight() / graphicsVariables.current_resolution_y) + (fmodLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x)) / 2
+	fmodLogoAdjustedSizeX = fmodLogoTexture:getTextureWidth() / fmodLogoAdjustedSizeMultAverage * fmodLogoScale
+	fmodLogoAdjustedSizeY = fmodLogoTexture:getTextureHeight() / fmodLogoAdjustedSizeMultAverage * fmodLogoScale
+	
+	-- Remove window padding and border size, so the content inside the window fills the whole window
+	GUI.PushStyleVar(ImGuiStyleVar.WindowPadding, 0.0, 0.0)
+	GUI.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0.0)
+	
 	-- Draw the background color
 	GUI.PushStyleColor(ImGuiCol.WindowBg, 0.102, 0.102, 0.102, 255.0)
 	GUI.SetNextWindowPos(0, 0)
@@ -58,10 +74,17 @@ function update (p_deltaTime)
 	GUI.PopStyleColor()
 	
 	-- Draw the engine logo
-	GUI.SetNextWindowPos((graphicsVariables.current_resolution_x - logoAdjustedSizeX) / 2, (graphicsVariables.current_resolution_y - logoAdjustedSizeY) / 2)
-	GUI.SetNextWindowSize(logoAdjustedSizeX, logoAdjustedSizeY)
+	GUI.SetNextWindowPos((graphicsVariables.current_resolution_x - engineLogoAdjustedSizeX) / 2, (graphicsVariables.current_resolution_y - engineLogoAdjustedSizeY) / 2)
+	GUI.SetNextWindowSize(engineLogoAdjustedSizeX, engineLogoAdjustedSizeY)
 	GUI.Begin('PRAXIS LOGO', bitwiseOr(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings, ImGuiWindowFlags.NoBackground, ImGuiWindowFlags.NoMouseInputs))
-	GUI.Image(praxisLogoTexture, logoAdjustedSizeX, logoAdjustedSizeY)
+	GUI.Image(praxisLogoTexture, engineLogoAdjustedSizeX, engineLogoAdjustedSizeY)
+	GUI.End()
+	
+	-- Draw the fmod logo
+	GUI.SetNextWindowPos((halfScreenSizeX - (fmodLogoAdjustedSizeX / 2)) * fmodLogoOffsetMultX, (halfScreenSizeY - (fmodLogoAdjustedSizeY / 2)) * fmodLogoOffsetMultY)
+	GUI.SetNextWindowSize(fmodLogoAdjustedSizeX, fmodLogoAdjustedSizeY)
+	GUI.Begin('FMOD LOGO', bitwiseOr(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings, ImGuiWindowFlags.NoBackground, ImGuiWindowFlags.NoMouseInputs))
+	GUI.Image(fmodLogoTexture, fmodLogoAdjustedSizeX, fmodLogoAdjustedSizeY)
 	GUI.End()
 	
 	-- Draw the EXIT button
@@ -94,6 +117,9 @@ function update (p_deltaTime)
 	GUI.Button('LOAD MAP', buttonSizeX, buttonSizeY, loadButtonPressed)
 	GUI.End()
 	
+	-- Pop the removal of frame padding and border size
+	GUI.PopStyleVar(2)
+	
 	-- Check if the EXIT button is pressed; close the engine if it is
 	if exitButtonPressed:isChecked() then
 		print('Exit called from MainMenu.lua')

+ 35 - 9
Praxis3D/Data/Scripts/MainMenu_image_buttons.lua

@@ -67,12 +67,21 @@ function init ()
 	buttonExitPressedTexture:loadToMemory()
 	buttonExitPressedTexture:loadToVideoMemory()
 	
-	-- Logo texture
+	-- Engine logo texture
 	praxisLogoTexture = loadTexture2D('logo1.png')
 	praxisLogoTexture:loadToMemory()
 	praxisLogoTexture:loadToVideoMemory()
 	
+	-- Fmod logo texture
+	fmodLogoTexture = loadTexture2D('FMOD Logo White - Black Background.png')
+	fmodLogoTexture:loadToMemory()
+	fmodLogoTexture:loadToVideoMemory()
+	
 	praxisLogoScale = 1
+	fmodLogoScale = 0.05
+	
+	fmodLogoOffsetMultX = 0.05
+	fmodLogoOffsetMultY = 1.95
 	
 	--buttonSizeX = 100.0
 	--buttonSizeY = 25.0
@@ -110,9 +119,18 @@ function update (p_deltaTime)
 	buttonPositionX = halfScreenSizeX * buttonOffsetMultX
 	buttonPositionY = halfScreenSizeY * buttonOffsetMultY
 	
-	-- Calculate logo size based in texture and window size (try to fit it on screen vertically)
-	logoAdjustedSizeX = praxisLogoTexture:getTextureWidth() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
-	logoAdjustedSizeY = praxisLogoTexture:getTextureHeight() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
+	-- Calculate engine logo size based based on texture and window size (try to fit it on screen vertically)
+	engineLogoAdjustedSizeX = praxisLogoTexture:getTextureWidth() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
+	engineLogoAdjustedSizeY = praxisLogoTexture:getTextureHeight() / (praxisLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x) * praxisLogoScale
+	
+	-- Calculate fmod logo size based on texture and window size (so it scaled with the screen size)
+	fmodLogoAdjustedSizeMultAverage = ((fmodLogoTexture:getTextureHeight() / graphicsVariables.current_resolution_y) + (fmodLogoTexture:getTextureWidth() / graphicsVariables.current_resolution_x)) / 2
+	fmodLogoAdjustedSizeX = fmodLogoTexture:getTextureWidth() / fmodLogoAdjustedSizeMultAverage * fmodLogoScale
+	fmodLogoAdjustedSizeY = fmodLogoTexture:getTextureHeight() / fmodLogoAdjustedSizeMultAverage * fmodLogoScale
+
+	-- Remove window padding and border size, so the content inside the window fills the whole window
+	GUI.PushStyleVar(ImGuiStyleVar.WindowPadding, 0.0, 0.0)
+	GUI.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0.0)
 
 	-- Draw the background color
 	GUI.PushStyleColor(ImGuiCol.WindowBg, 0.102, 0.102, 0.102, 255.0)
@@ -123,10 +141,17 @@ function update (p_deltaTime)
 	GUI.PopStyleColor()
 	
 	-- Draw the engine logo
-	GUI.SetNextWindowPos((graphicsVariables.current_resolution_x - logoAdjustedSizeX) / 2, (graphicsVariables.current_resolution_y - logoAdjustedSizeY) / 2)
-	GUI.SetNextWindowSize(logoAdjustedSizeX, logoAdjustedSizeY)
+	GUI.SetNextWindowPos((graphicsVariables.current_resolution_x - engineLogoAdjustedSizeX) / 2, (graphicsVariables.current_resolution_y - engineLogoAdjustedSizeY) / 2)
+	GUI.SetNextWindowSize(engineLogoAdjustedSizeX, engineLogoAdjustedSizeY)
 	GUI.Begin('PRAXIS LOGO', bitwiseOr(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings, ImGuiWindowFlags.NoBackground, ImGuiWindowFlags.NoMouseInputs))
-	GUI.Image(praxisLogoTexture, logoAdjustedSizeX, logoAdjustedSizeY)
+	GUI.Image(praxisLogoTexture, engineLogoAdjustedSizeX, engineLogoAdjustedSizeY)
+	GUI.End()
+	
+	-- Draw the fmod logo
+	GUI.SetNextWindowPos((halfScreenSizeX - (fmodLogoAdjustedSizeX / 2)) * fmodLogoOffsetMultX, (halfScreenSizeY - (fmodLogoAdjustedSizeY / 2)) * fmodLogoOffsetMultY)
+	GUI.SetNextWindowSize(fmodLogoAdjustedSizeX, fmodLogoAdjustedSizeY)
+	GUI.Begin('FMOD LOGO', bitwiseOr(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings, ImGuiWindowFlags.NoBackground, ImGuiWindowFlags.NoMouseInputs))
+	GUI.Image(fmodLogoTexture, fmodLogoAdjustedSizeX, fmodLogoAdjustedSizeY)
 	GUI.End()
 	
 	-- Push transparent background colors for all buttons
@@ -192,6 +217,9 @@ function update (p_deltaTime)
 	-- Pop the transparent background colors for all buttons
 	GUI.PopStyleColor(3)
 	
+	-- Pop the removal of frame padding and border size
+	GUI.PopStyleVar(2)
+	
 	-- Check if the EXIT button is pressed; close the engine if it is
 	if exitButtonPressed:isChecked() then
 		print('Exit called from MainMenu.lua')
@@ -229,6 +257,4 @@ function update (p_deltaTime)
 		fileBrowserLoadMapOpened = false
 		fileBrowserLoadMap:reset()
 	end
-	
-	--print('test')
 end

+ 3 - 0
Praxis3D/Data/error-strings-eng.data

@@ -8,6 +8,8 @@
 		"Initialize_failure"				: "Failed to initialized",
 		"File_not_found"						: "File missing",
 		"Filename_empty"						: "Filename is empty",
+		"Audio_no_drivers"					: "System has no sound devices",
+		"Audio_system_init_failed"	: "Audio system has failed to initialize",
 		"Destroy_obj_not_found"			: "Destruction: object not found",
 		"Glew_failed"								: "OpenGL Extension Wrangler Library has failed to initialize",
 		"Ifstream_failed"						: "Unable to read input file stream",
@@ -94,6 +96,7 @@
 		"Source_ShaderLoader"							: "Shader Loader",
 		"Source_SkyObject"								: "Sky Object",
 		"Source_SkyPass"									: "Sky Rendering Pass",
+		"Source_SoundComponent"						: "Sound Component",
 		"Source_TextureLoader"						: "Texture Loader",
 		"Source_Window"										: "Window System",
 		"Source_World"										: "World",

+ 1 - 0
Praxis3D/Praxis3D.vcxproj

@@ -292,6 +292,7 @@
     <ClInclude Include="Source\ModelComponent.h" />
     <ClInclude Include="Source\ModelLoader.h" />
     <ClInclude Include="Source\ModelGraphicsObjects.h" />
+    <ClInclude Include="Source\SoundComponent.h" />
     <ClInclude Include="Source\NullObjects.h" />
     <ClInclude Include="Source\NullSystemObjects.h" />
     <ClInclude Include="Source\ObjectDirectory.h" />

+ 3 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -857,6 +857,9 @@
     <ClInclude Include="Source\GraphicsLoadComponents.h">
       <Filter>Renderer\Objects\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\SoundComponent.h">
+      <Filter>Audio\Components</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 127 - 91
Praxis3D/Source/AudioScene.cpp

@@ -8,17 +8,58 @@
 AudioScene::AudioScene(SystemBase *p_system, SceneLoader *p_sceneLoader) : SystemScene(p_system, p_sceneLoader)
 {
 	m_audioTask = nullptr;
+	m_fmodSystem = nullptr;
+	m_masterChannelGroup = nullptr;
+	m_musicChannelGroup = nullptr;
+	m_ambientChannelGroup = nullptr;
+	m_numSoundDrivers = 0;
 }
 
 AudioScene::~AudioScene()
 {
+	if(m_fmodSystem != nullptr)
+	{
+		m_fmodSystem->close();
+		m_fmodSystem->release();
+	}
 }
 
 ErrorCode AudioScene::init()
 {
+	ErrorCode returnError = ErrorCode::Success;
+
 	m_audioTask = new AudioTask(this);
 
-	return ErrorCode::Success;
+	// Create the fmod sound system
+	auto fmodError = FMOD::System_Create(&m_fmodSystem);
+
+	// Check if the sound system was created successfully
+	if(fmodError != FMOD_OK)
+	{
+		ErrHandlerLoc::get().log(ErrorCode::Audio_system_init_failed, ErrorSource::Source_AudioScene);
+		returnError = ErrorCode::Audio_system_init_failed;
+	}
+	else
+	{
+		// Get the number of sound drivers
+		m_fmodSystem->getNumDrivers(&m_numSoundDrivers);
+
+		// Do not continue if there are no sound drivers
+		if(m_numSoundDrivers == 0)
+		{
+			ErrHandlerLoc::get().log(ErrorCode::Audio_no_drivers, ErrorSource::Source_AudioScene);
+			returnError = ErrorCode::Audio_no_drivers;
+		}
+		else
+		{
+			// Initialize our Instance with 36 Channels
+			m_fmodSystem->init(Config::audioVar().num_audio_channels, FMOD_INIT_NORMAL, NULL);
+
+			m_fmodSystem->createChannelGroup("music", &m_musicChannelGroup);
+		}
+	}
+
+	return returnError;
 }
 
 ErrorCode AudioScene::setup(const PropertySet &p_properties)
@@ -29,32 +70,73 @@ ErrorCode AudioScene::setup(const PropertySet &p_properties)
 		{
 		case Properties::ObjectPoolSize:
 
-			break;
-		case Properties::Gravity:
-			//m_dynamicsWorld->setGravity(Math::toBtVector3(p_properties[i].getVec3f()));
 			break;
 		}
 	}
 
+	// Get the world scene required for reserving the component pools
+	WorldScene *worldScene = static_cast<WorldScene*>(m_sceneLoader->getSystemScene(Systems::World));
+
+	// Reserve every component type that belongs to this scene
+	worldScene->reserve<SoundComponent>(Config::objectPoolVar().sound_component_default_pool_size);
+
 	return ErrorCode::Success;
 }
 
+void AudioScene::activate()
+{
+}
+
+void AudioScene::deactivate()
+{
+	// Get the entity registry 
+	auto &entityRegistry = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World))->getEntityRegistry();
+
+	auto soundComponentView = entityRegistry.view<SoundComponent>();
+	for(auto entity : soundComponentView)
+	{
+		auto &component = soundComponentView.get<SoundComponent>(entity);
+
+		if(component.isObjectActive())
+		{
+			if(component.m_playing)
+			{
+				component.m_channel->stop();
+				component.m_playing = false;
+			}
+		}
+	}
+}
+
 void AudioScene::update(const float p_deltaTime)
 {
+	m_fmodSystem->update();
+
 	// Get the world scene required for getting the entity registry
-	WorldScene *worldScene = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World));
+	WorldScene *worldScene = static_cast<WorldScene*>(m_sceneLoader->getSystemScene(Systems::World));
 
 	// Get the entity registry 
 	auto &entityRegistry = worldScene->getEntityRegistry();
 
-	// Get the rigid body component view and iterate every entity that contains is
-	/*auto rigidBodyView = worldScene->getEntityRegistry().view<RigidBodyComponent>();
-	for(auto entity : rigidBodyView)
+	auto soundComponentView = entityRegistry.view<SoundComponent>();
+	for(auto entity : soundComponentView)
 	{
-		auto &component = rigidBodyView.get<RigidBodyComponent>(entity);
+		auto &component = soundComponentView.get<SoundComponent>(entity);
 
-		component.update(p_deltaTime);
-	}*/
+		if(component.isObjectActive())
+		{
+			if(!component.m_playing)
+			{
+				if(component.m_startPlaying)
+				{
+					component.m_playing = true;
+					m_fmodSystem->playSound(component.m_sound, 0, true, &component.m_channel);
+					component.m_channel->setVolume(component.m_volume);
+					component.m_channel->setPaused(false);
+				}
+			}
+		}
+	}
 }
 
 ErrorCode AudioScene::preload()
@@ -71,7 +153,7 @@ std::vector<SystemObject*> AudioScene::createComponents(const EntityID p_entityI
 	return createComponents(p_entityID, p_constructionInfo.m_audioComponents, p_startLoading);
 }
 
-/*SystemObject *AudioScene::createComponent(const EntityID &p_entityID, const RigidBodyComponent::RigidBodyComponentConstructionInfo &p_constructionInfo, const bool p_startLoading)
+SystemObject *AudioScene::createComponent(const EntityID &p_entityID, const SoundComponent::SoundComponentConstructionInfo &p_constructionInfo, const bool p_startLoading)
 {
 	// If valid type was not specified, or object creation failed, return a null object instead
 	SystemObject *returnObject = g_nullSystemBase.getScene()->getNullObject();
@@ -79,111 +161,65 @@ std::vector<SystemObject*> AudioScene::createComponents(const EntityID p_entityI
 	// Get the world scene required for attaching components to the entity
 	WorldScene *worldScene = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World));
 
-	auto &component = worldScene->addComponent<RigidBodyComponent>(p_entityID, this, p_constructionInfo.m_name, p_entityID);
+	auto &component = worldScene->addComponent<SoundComponent>(p_entityID, this, p_constructionInfo.m_name, p_entityID);
 
 	// Try to initialize the camera component
 	auto componentInitError = component.init();
 	if(componentInitError == ErrorCode::Success)
 	{
-		component.m_collisionShapeType = p_constructionInfo.m_collisionShapeType;
-		component.m_kinematic = p_constructionInfo.m_kinematic;
-		component.m_objectType = Properties::PropertyID::RigidBodyComponent;
+		component.m_soundType = p_constructionInfo.m_soundType;
+		component.m_soundFilename = p_constructionInfo.m_soundFilename;
+		component.m_loop = p_constructionInfo.m_loop;
+		component.m_spatialized = p_constructionInfo.m_spatialized;
+		component.m_startPlaying = p_constructionInfo.m_startPlaying;
+		component.m_volume = p_constructionInfo.m_volume;
+		component.m_objectType = Properties::PropertyID::SoundComponent;
 		component.setActive(p_constructionInfo.m_active);
-		component.setLoadedToMemory(true);
-		component.setLoadedToVideoMemory(true);
 
-		if(component.m_collisionShapeType != RigidBodyComponent::CollisionShapeType::CollisionShapeType_Null)
-		{
-			switch(component.m_collisionShapeType)
-			{
-			case RigidBodyComponent::CollisionShapeType::CollisionShapeType_Box:
-			{
-				btVector3 boxHalfExtents = Math::toBtVector3(p_constructionInfo.m_collisionShapeSize);
-				component.m_collisionShape.m_boxShape = new btBoxShape(boxHalfExtents);
-			}
-			break;
-
-			case RigidBodyComponent::CollisionShapeType::CollisionShapeType_Sphere:
-			{
-				float radius = p_constructionInfo.m_collisionShapeSize.x;
-				component.m_collisionShape.m_sphereShape = new btSphereShape(radius);
-			}
-			break;
-			}
+		FMOD_MODE mode = 0;
 
-			// Success on the loaded collision shape
-			ErrHandlerLoc().get().log(ErrorType::Info, ErrorSource::Source_RigidBodyComponent, component.getName() + " - Collision shape loaded");
+		if(component.m_loop)
+			mode |= FMOD_LOOP_NORMAL;
+		else
+			mode |= FMOD_LOOP_OFF;
 
-			// Create the struct that holds all the required information for constructing a rigid body
-			component.m_constructionInfo = new btRigidBody::btRigidBodyConstructionInfo(p_constructionInfo.m_mass, &component.m_motionState, component.getCollisionShape());
+		if(component.m_spatialized)
+			mode |= FMOD_3D | FMOD_3D_WORLDRELATIVE | FMOD_3D_INVERSEROLLOFF;
+		else
+			mode |= FMOD_2D;
 
-			component.m_constructionInfo->m_friction = p_constructionInfo.m_friction;
-			component.m_constructionInfo->m_restitution = p_constructionInfo.m_restitution;
 
-			// If mass is not zero, rigid body is dynamic; in that case, calculate the local inertia 
-			if(component.m_constructionInfo->m_mass != 0.0f)
+		switch(component.m_soundType)
+		{
+			case SoundComponent::SoundType::SoundType_Music:
 			{
-				// Kinematic objects must have a mass of zero
-				if(component.m_kinematic)
-				{
-					component.m_constructionInfo->m_mass = 0.0f;
-					ErrHandlerLoc().get().log(ErrorCode::Kinematic_has_mass, component.getName(), ErrorSource::Source_RigidBodyComponent);
-				}
-				else
-					component.getCollisionShape()->calculateLocalInertia(component.m_constructionInfo->m_mass, component.m_constructionInfo->m_localInertia);
+				m_fmodSystem->createStream((Config::filepathVar().sound_path + component.m_soundFilename).c_str(), mode, component.m_soundExInfo, &component.m_sound);
 			}
+			break;
 
-			// Set the body origin in space to the position in Spatial Component, if the Spatial Component is present
-			auto *spatialComponent = worldScene->getEntityRegistry().try_get<SpatialComponent>(p_entityID);
-			if(spatialComponent != nullptr)
+			case SoundComponent::SoundType::SoundType_Ambient:
 			{
-				btTransform groundTransform;
-				groundTransform.setIdentity();
-				groundTransform.setOrigin(Math::toBtVector3(spatialComponent->getSpatialDataChangeManager().getLocalSpaceData().m_spatialData.m_position));
-				groundTransform.setRotation(Math::toBtQuaternion(spatialComponent->getSpatialDataChangeManager().getLocalSpaceData().m_spatialData.m_rotationQuat));
-				//groundTransform.setFromOpenGLMatrix(&spatialComponent->getSpatialDataChangeManager().getLocalSpaceData().m_transformMat[0][0]);
-				component.m_motionState.setWorldTransform(groundTransform);
-
-				component.m_motionState.updateMotionStateTrans();
+				m_fmodSystem->createStream((Config::filepathVar().sound_path + component.m_soundFilename).c_str(), mode, component.m_soundExInfo, &component.m_sound);
 			}
+			break;
 
-			// Add the collision shape
-			m_collisionShapes.push_back(component.getCollisionShape());
-
-			// Create the rigid body by passing the rigid body construction info
-			component.m_rigidBody = new btRigidBody(*component.m_constructionInfo);
-
-			if(component.m_kinematic)
+			case SoundComponent::SoundType::SoundType_SoundEffect:
 			{
-				component.m_rigidBody->setCollisionFlags(component.m_rigidBody->getCollisionFlags() | btCollisionObject::CollisionFlags::CF_KINEMATIC_OBJECT);
-				component.m_rigidBody->setActivationState(DISABLE_DEACTIVATION);
+				m_fmodSystem->createSound((Config::filepathVar().sound_path + component.m_soundFilename).c_str(), mode, component.m_soundExInfo, &component.m_sound);
 			}
-
-			// Set linear velocity if it is not zero
-			if(p_constructionInfo.m_linearVelocity != glm::vec3())
-				component.m_rigidBody->setLinearVelocity(Math::toBtVector3(p_constructionInfo.m_linearVelocity));
-
-			// Add the rigid body to the dynamics world, essentially loading it into the physics system
-			m_dynamicsWorld->addRigidBody(component.m_rigidBody);
-
-			returnObject = &component;
-
-		}
-		else // Remove the component if it didn't have a collision shape
-		{
-			// Missing the collision shape
-			worldScene->removeComponent<RigidBodyComponent>(p_entityID);
-			ErrHandlerLoc().get().log(ErrorCode::Collision_missing, component.getName(), ErrorSource::Source_RigidBodyComponent);
+			break;
 		}
+
+		returnObject = &component;
 	}
 	else // Remove the component if it failed to initialize
 	{
-		worldScene->removeComponent<RigidBodyComponent>(p_entityID);
-		ErrHandlerLoc().get().log(componentInitError, ErrorSource::Source_RigidBodyComponent, component.getName());
+		worldScene->removeComponent<SoundComponent>(p_entityID);
+		ErrHandlerLoc().get().log(componentInitError, ErrorSource::Source_SoundComponent, component.getName());
 	}
 
 	return returnObject;
-}*/
+}
 
 ErrorCode AudioScene::destroyObject(SystemObject *p_systemObject)
 {

+ 30 - 9
Praxis3D/Source/AudioScene.h

@@ -1,6 +1,16 @@
 #pragma once
 
-#include "AUdioTask.h"
+#pragma comment(lib, "fmod/fmod_vc.lib")
+//#pragma comment(lib, "fmod/fmodL_vc.lib")
+//#pragma comment(lib, "fmod/fmodstudio_vc.lib")
+//#pragma comment(lib, "fmod/fmodstudioL_vc.lib")
+//#pragma comment(lib, "fmod/fsbank_vc.lib")
+
+#include <fmod/core/fmod.hpp>
+#include <fmod/core/fmod_errors.h>
+
+#include "AudioTask.h"
+#include "SoundComponent.h"
 #include "System.h"
 
 struct ComponentsConstructionInfo;
@@ -9,22 +19,22 @@ struct AudioComponentsConstructionInfo
 {
 	AudioComponentsConstructionInfo()
 	{
-		//m_rigidBodyConstructionInfo = nullptr;
+		m_soundConstructionInfo = nullptr;
 	}
 
 	// Perform a complete copy, instantiating (with new) every member variable pointer, instead of just assigning the pointer to the same memory
 	void completeCopy(const AudioComponentsConstructionInfo &p_other)
 	{
-		//Utilities::performCopy<RigidBodyComponent::RigidBodyComponentConstructionInfo>(&m_rigidBodyConstructionInfo, &p_other.m_rigidBodyConstructionInfo);
+		Utilities::performCopy<SoundComponent::SoundComponentConstructionInfo>(&m_soundConstructionInfo, &p_other.m_soundConstructionInfo);
 	}
 
 	void deleteConstructionInfo()
 	{
-		//if(m_rigidBodyConstructionInfo != nullptr)
-		//	delete m_rigidBodyConstructionInfo;
+		if(m_soundConstructionInfo != nullptr)
+			delete m_soundConstructionInfo;
 	}
 
-	//RigidBodyComponent::RigidBodyComponentConstructionInfo *m_rigidBodyConstructionInfo;
+	SoundComponent::SoundComponentConstructionInfo *m_soundConstructionInfo;
 };
 
 class AudioScene : public SystemScene
@@ -37,6 +47,10 @@ public:
 
 	ErrorCode setup(const PropertySet &p_properties);
 
+	void activate();
+
+	void deactivate();
+
 	void update(const float p_deltaTime);
 
 	ErrorCode preload();
@@ -48,13 +62,13 @@ public:
 	{
 		std::vector<SystemObject*> components;
 
-		//if(p_constructionInfo.m_rigidBodyConstructionInfo != nullptr)
-		//	components.push_back(createComponent(p_entityID, *p_constructionInfo.m_rigidBodyConstructionInfo, p_startLoading));
+		if(p_constructionInfo.m_soundConstructionInfo != nullptr)
+			components.push_back(createComponent(p_entityID, *p_constructionInfo.m_soundConstructionInfo, p_startLoading));
 
 		return components;
 	}
 
-	//SystemObject *createComponent(const EntityID &p_entityID, const RigidBodyComponent::RigidBodyComponentConstructionInfo &p_constructionInfo, const bool p_startLoading = true);
+	SystemObject *createComponent(const EntityID &p_entityID, const SoundComponent::SoundComponentConstructionInfo &p_constructionInfo, const bool p_startLoading = true);
 	ErrorCode destroyObject(SystemObject *p_systemObject);
 
 	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
@@ -66,4 +80,11 @@ public:
 
 private:
 	AudioTask *m_audioTask;
+	FMOD::System *m_fmodSystem;
+
+	FMOD::ChannelGroup *m_masterChannelGroup;
+	FMOD::ChannelGroup *m_musicChannelGroup;
+	FMOD::ChannelGroup *m_ambientChannelGroup;
+
+	int m_numSoundDrivers;
 };

+ 6 - 0
Praxis3D/Source/Config.cpp

@@ -26,6 +26,7 @@ namespace Properties
 // A version of the "AddVariable" macro, with 3 of the 5 parameters predefined
 #define AddVariablePredef(STRUCT, VAR) AddVariable(VEC_PREDEF, HASH_PREDEF, VEC_PREDEF.size() + OFFSET_PREDEF, STRUCT, VAR)
 
+Config::AudioVariables		Config::m_audioVar;
 Config::ComponentVariables	Config::m_componentVar;
 Config::ConfigFileVariables	Config::m_configFileVar;
 Config::EngineVariables		Config::m_engineVar;
@@ -51,6 +52,9 @@ void Config::init()
 	// Add variables and their names to internal containers, so they could be checked / names matched later.
 	// Note: not all the variables are assigned to containers, as some are not meant to be loaded from config file.
 
+	// Audio Variables
+	AddVariablePredef(m_audioVar, num_audio_channels);
+
 	// Component Variables
 	AddVariablePredef(m_componentVar, camera_component_name);
 	AddVariablePredef(m_componentVar, component_name_separator);
@@ -72,6 +76,7 @@ void Config::init()
 	AddVariablePredef(m_engineVar, gl_context_minor_version);
 	AddVariablePredef(m_engineVar, object_directory_init_pool_size);
 	AddVariablePredef(m_engineVar, smoothing_tick_samples);
+	AddVariablePredef(m_engineVar, task_scheduler_clock_frequency);
 
 	// Frame-buffer variables
 	AddVariablePredef(m_framebfrVar, gl_position_buffer_internal_format);
@@ -238,6 +243,7 @@ void Config::init()
 	AddVariablePredef(m_objPoolVar, shader_component_default_pool_size);
 	AddVariablePredef(m_objPoolVar, spatial_component_default_pool_size);
 	AddVariablePredef(m_objPoolVar, spot_light_pool_size);
+	AddVariablePredef(m_objPoolVar, sound_component_default_pool_size);
 
 	// File-path variables
 	AddVariablePredef(m_filepathVar, config_path);

+ 23 - 0
Praxis3D/Source/Config.h

@@ -228,7 +228,15 @@ namespace Properties
 	Code(Value,) \
 	Code(Variables,) \
 	/* Audio */ \
+	Code(Ambient,) \
 	Code(Audio,) \
+	Code(Loop,) \
+	Code(Music,) \
+	Code(SoundComponent,) \
+	Code(SoundEffect,) \
+	Code(Spatialized,) \
+	Code(StartPlaying,) \
+	Code(Volume,) \
 	/* Geometry */ \
 	Code(OffsetPosition,) \
 	Code(OffsetRotation,) \
@@ -466,6 +474,14 @@ class Config
 	friend class RendererFrontend;
 	friend class Window;
 public:
+	struct AudioVariables
+	{
+		AudioVariables()
+		{
+			num_audio_channels = 32;
+		}
+		int num_audio_channels;
+	};
 	struct ComponentVariables
 	{
 		ComponentVariables()
@@ -512,6 +528,7 @@ public:
 			gl_context_minor_version = 3;
 			object_directory_init_pool_size = 1000;
 			smoothing_tick_samples = 100;
+			task_scheduler_clock_frequency = 120;
 			running = true;
 			loadingState = true;
 			engineState = EngineStateType::EngineStateType_MainMenu;
@@ -529,6 +546,7 @@ public:
 		int gl_context_minor_version;
 		int object_directory_init_pool_size;
 		int smoothing_tick_samples;
+		int task_scheduler_clock_frequency;
 		bool running;
 		bool loadingState;
 		EngineStateType engineState;
@@ -909,6 +927,7 @@ public:
 			shader_component_default_pool_size = 5;
 			spatial_component_default_pool_size = 100;
 			spot_light_pool_size = 25;
+			sound_component_default_pool_size = 50;
 		}
 
 		int camera_component_default_pool_size;
@@ -922,6 +941,7 @@ public:
 		int shader_component_default_pool_size;
 		int spatial_component_default_pool_size;
 		int spot_light_pool_size;
+		int sound_component_default_pool_size;
 	};
 	struct PathsVariables
 	{
@@ -1400,6 +1420,7 @@ public:
 		bool window_in_focus;
 	};
 
+	const inline static AudioVariables		&audioVar()			{ return m_audioVar;		}
 	const inline static ComponentVariables	&componentVar()		{ return m_componentVar;	}
 	const inline static ConfigFileVariables	&configFileVar()	{ return m_configFileVar;	}
 	const inline static EngineVariables		&engineVar()		{ return m_engineVar;		}
@@ -1516,6 +1537,7 @@ private:
 		size_t m_mapKey;
 	};
 
+	static AudioVariables		m_audioVar;
 	static ComponentVariables	m_componentVar;
 	static ConfigFileVariables	m_configFileVar;
 	static EngineVariables		m_engineVar;
@@ -1540,6 +1562,7 @@ private:
 
 	static void setVariable(std::string p_name, std::string p_variable);
 
+	inline static AudioVariables		&setAudioVar()		{ return m_audioVar;		}
 	inline static ComponentVariables	&setComponentVar()	{ return m_componentVar;	}
 	inline static ConfigFileVariables	&setConfigFileVar()	{ return m_configFileVar;	}
 	inline static EngineVariables		&setEngineVar()		{ return m_engineVar;		}

+ 10 - 0
Praxis3D/Source/Engine.h

@@ -58,6 +58,16 @@ private:
 				ErrHandlerLoc::get().log(stateInitError, ErrorSource::Source_Engine);
 			}
 		}
+
+		// If the state changed, deactivate the previous one, and activate the new one
+		if(m_currentState != previousState)
+		{
+			if(previousState != nullptr)
+				previousState->deactivate();
+
+			if(m_currentState != nullptr)
+				m_currentState->activate();
+		}
 	}
 
 	// Creates and initializes all the services and their locators

+ 16 - 0
Praxis3D/Source/EngineState.cpp

@@ -67,6 +67,22 @@ ErrorCode EngineState::init(TaskManager *p_taskManager)
 	return returnError;
 }
 
+void EngineState::activate()
+{
+	m_scheduler->execute<>(std::function<void(SystemTask *)>(&SystemTask::activate));
+
+	m_objectChangeController->distributeChanges();
+	m_sceneChangeController->distributeChanges();
+}
+
+void EngineState::deactivate()
+{
+	m_scheduler->execute<>(std::function<void(SystemTask *)>(&SystemTask::deactivate));
+
+	m_objectChangeController->distributeChanges();
+	m_sceneChangeController->distributeChanges();
+}
+
 void EngineState::shutdown()
 {
 	if(m_initialized)

+ 5 - 0
Praxis3D/Source/EngineState.h

@@ -17,8 +17,13 @@ public:
 	virtual ~EngineState();
 
 	virtual ErrorCode init(TaskManager *p_taskManager);
+
 	virtual void update(Engine &p_engine) = 0;
 
+	virtual void activate();
+
+	virtual void deactivate();
+
 	virtual void shutdown();
 
 	virtual const EngineStateType getEngineStateType() = 0;

+ 4 - 0
Praxis3D/Source/ErrorCodes.h

@@ -40,6 +40,9 @@ DECLARE_ENUM(ErrorType, ERROR_TYPES)
     Code(Initialize_failure,) \
     Code(File_not_found,) \
     Code(Filename_empty,) \
+	/* Audio system errors */\
+	Code(Audio_no_drivers, ) \
+	Code(Audio_system_init_failed, ) \
 	/* General engine errors */\
     Code(Destroy_obj_not_found,) \
     Code(Glew_failed,) \
@@ -143,6 +146,7 @@ DECLARE_ENUM(ErrorCode, ERROR_CODES)
     Code(Source_ShaderLoader,) \
     Code(Source_SkyObject,) \
     Code(Source_SkyPass,) \
+    Code(Source_SoundComponent,) \
     Code(Source_TextureLoader,) \
     Code(Source_Window,) \
     Code(Source_World,) \

+ 2 - 0
Praxis3D/Source/ErrorHandler.cpp

@@ -28,6 +28,8 @@ ErrorHandler::ErrorHandler()
 	AssignErrorType(Initialize_failure, Info);
 	AssignErrorType(File_not_found, Warning);
 	AssignErrorType(Filename_empty, Warning);
+	AssignErrorType(Audio_no_drivers, Error);
+	AssignErrorType(Audio_system_init_failed, Error);
 	AssignErrorType(Destroy_obj_not_found, Warning);
 	AssignErrorType(Glew_failed, FatalError);
 	AssignErrorType(Ifstream_failed, Warning);

+ 3 - 1
Praxis3D/Source/LuaScript.cpp

@@ -279,8 +279,10 @@ void LuaScript::setFunctions()
 	GUITable.set_function("PopStyleVar", sol::overload([this](const int p_v1) -> const void { m_GUIData.addFunctor([=] { ImGui::PopStyleVar(p_v1); }); },
 		[this]() -> const void { m_GUIData.addFunctor([=] { ImGui::PopStyleVar(1); }); }));
 	GUITable.set_function("PushStyleColor", [this](const int p_v1, const float p_v2, const float p_v3, const float p_v4, const float p_v5) -> const void { m_GUIData.addFunctor([=] { ImGui::PushStyleColor(p_v1, ImVec4(p_v2, p_v3, p_v4, p_v5)); }); });
-	GUITable.set_function("PushStyleVar", [this](const int p_v1, const float p_v2, const float p_v3) -> const void { m_GUIData.addFunctor([=] { ImGui::PushStyleVar(p_v1, ImVec2(p_v2, p_v3)); }); });
+	GUITable.set_function("PushStyleVar", sol::overload([this](const int p_v1, const float p_v2) -> const void { m_GUIData.addFunctor([=] { ImGui::PushStyleVar(p_v1, p_v2); }); },
+		[this](const int p_v1, const float p_v2, const float p_v3) -> const void { m_GUIData.addFunctor([=] { ImGui::PushStyleVar(p_v1, ImVec2(p_v2, p_v3)); }); }));
 	GUITable.set_function("SetNextWindowPos", [this](const float p_v1, const float p_v2) -> const void { m_GUIData.addFunctor([=] { ImGui::SetNextWindowPos(ImVec2(p_v1, p_v2)); }); });
+	GUITable.set_function("SetNextWindowContentSize", [this](const float p_v1, const float p_v2) -> const void { m_GUIData.addFunctor([=] { ImGui::SetNextWindowContentSize(ImVec2(p_v1, p_v2)); }); });
 	GUITable.set_function("SetNextWindowSize", [this](const float p_v1, const float p_v2) -> const void { m_GUIData.addFunctor([=] { ImGui::SetNextWindowSize(ImVec2(p_v1, p_v2)); }); });
 	GUITable.set_function("ShowMetricsWindow", [this](bool p_v1) -> void { m_GUIData.addFunctor([=] { bool open = p_v1; ImGui::ShowMetricsWindow(&open); }); });
 	GUITable.set_function("SliderFloat", [this](const std::string &p_v1, float *p_v2, const float p_v3, const float p_v4) -> const void { m_GUIData.addFunctor([=] { ImGui::SliderFloat(p_v1.c_str(), p_v2, p_v3, p_v4); }); });

+ 1 - 1
Praxis3D/Source/MainMenuState.cpp

@@ -35,7 +35,7 @@ ErrorCode MainMenuState::init(TaskManager *p_taskManager)
 
 void MainMenuState::update(Engine &p_engine)
 {
-	m_scheduler->execute(ClockLocator::get().getDeltaSecondsF());
+	m_scheduler->execute<>(std::function<void(SystemTask*, float)>(&SystemTask::update), ClockLocator::get().getDeltaSecondsF());
 
 	m_objectChangeController->distributeChanges();
 	m_sceneChangeController->distributeChanges();

+ 1 - 1
Praxis3D/Source/PlayState.cpp

@@ -35,7 +35,7 @@ ErrorCode PlayState::init(TaskManager *p_taskManager)
 
 void PlayState::update(Engine &p_engine)
 {
-	m_scheduler->execute(ClockLocator::get().getDeltaSecondsF());
+	m_scheduler->execute<>(std::function<void(SystemTask *, float)>(&SystemTask::update), ClockLocator::get().getDeltaSecondsF());
 	
 	m_objectChangeController->distributeChanges();
 	m_sceneChangeController->distributeChanges();

+ 133 - 55
Praxis3D/Source/PropertySet.h

@@ -11,7 +11,7 @@
 class PropertySet;
 
 // Stores a single value of a variable type. Has a getter function for each type. If a getter of
-// incorrect type is called, it deals with it by converting the internal value to a type of the
+// incorrect type is called, it deals with it by converting the internal value to the type of the
 // getter. Has a propertyID tag, to distinguish between other instances.
 class Property
 {
@@ -33,50 +33,49 @@ public:
 	};
 
 	// Type can be determined by overloaded constructor call
-	Property() : m_propertyID(Properties::Null), m_variableType(Type_null) { }
-	Property(const Properties::PropertyID p_propertyID) : m_propertyID(p_propertyID), m_variableType(Type_null) { }
-	Property(const Properties::PropertyID p_propertyID, const bool p_variable) : m_propertyID(p_propertyID), m_variableType(Type_bool)
+	Property() noexcept : m_propertyID(Properties::Null), m_variableType(Type_null) { }
+	Property(const Properties::PropertyID p_propertyID) noexcept : m_propertyID(p_propertyID), m_variableType(Type_null) { }
+	Property(const Properties::PropertyID p_propertyID, const bool p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_bool)
 	{ 
 		m_variable.m_bool = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const int p_variable) : m_propertyID(p_propertyID), m_variableType(Type_int)
+	Property(const Properties::PropertyID p_propertyID, const int p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_int)
 	{
 		m_variable.m_int = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const float p_variable) : m_propertyID(p_propertyID), m_variableType(Type_float)
+	Property(const Properties::PropertyID p_propertyID, const float p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_float)
 	{
 		m_variable.m_float = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const double p_variable) : m_propertyID(p_propertyID), m_variableType(Type_double)
+	Property(const Properties::PropertyID p_propertyID, const double p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_double)
 	{
 		m_variable.m_double = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const glm::ivec2 &p_variable) : m_propertyID(p_propertyID), m_variableType(Type_vec2i)
+	Property(const Properties::PropertyID p_propertyID, const glm::ivec2 &p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_vec2i)
 	{
 		m_variable.m_vec2i = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const glm::vec2 &p_variable) : m_propertyID(p_propertyID), m_variableType(Type_vec2f)
+	Property(const Properties::PropertyID p_propertyID, const glm::vec2 &p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_vec2f)
 	{
 		m_variable.m_vec2f = p_variable;
-		//m_vec2f = new glm::vec2(p_variable);
 	}
-	Property(const Properties::PropertyID p_propertyID, const glm::vec3 &p_variable) : m_propertyID(p_propertyID), m_variableType(Type_vec3f)
+	Property(const Properties::PropertyID p_propertyID, const glm::vec3 &p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_vec3f)
 	{
 		m_variable.m_vec3f = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const glm::vec4 &p_variable) : m_propertyID(p_propertyID), m_variableType(Type_vec4f)
+	Property(const Properties::PropertyID p_propertyID, const glm::vec4 &p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_vec4f)
 	{
 		m_variable.m_vec4f = p_variable;
 	}
-	Property(const Properties::PropertyID p_propertyID, const std::string &p_variable) : m_propertyID(p_propertyID), m_variableType(Type_string)
+	Property(const Properties::PropertyID p_propertyID, const std::string &p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_string)
 	{
 		new (&m_variable.m_string) std::string(p_variable);
 	}
-	Property(const Properties::PropertyID p_propertyID, const Properties::PropertyID p_variable) : m_propertyID(p_propertyID), m_variableType(Type_propertyID)
+	Property(const Properties::PropertyID p_propertyID, const Properties::PropertyID p_variable) noexcept : m_propertyID(p_propertyID), m_variableType(Type_propertyID)
 	{
 		m_variable.m_ID = p_variable;
 	}
-	Property(const Property &p_property) : m_propertyID(p_property.m_propertyID), m_variableType(p_property.m_variableType)
+	Property(const Property &p_property) noexcept : m_propertyID(p_property.m_propertyID), m_variableType(p_property.m_variableType)
 	{
 		switch(m_variableType)
 		{
@@ -112,10 +111,9 @@ public:
 			break;
 		}
 	}
-	//~Property() { }
 
 	// Getters for each type
-	const inline bool getBool() const
+	const inline bool getBool() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -157,7 +155,7 @@ public:
 			break;
 		}
 	}
-	const inline int getInt() const
+	const inline int getInt() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -197,7 +195,7 @@ public:
 			break;
 		}
 	}
-	const inline float getFloat() const
+	const inline float getFloat() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -237,7 +235,7 @@ public:
 			break;
 		}
 	}
-	const inline double getDouble() const
+	const inline double getDouble() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -277,7 +275,7 @@ public:
 			break;
 		}
 	}
-	const inline glm::ivec2 getVec2i() const
+	const inline glm::ivec2 getVec2i() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -317,7 +315,7 @@ public:
 			break;
 		}
 	}
-	const inline glm::vec2 getVec2f() const
+	const inline glm::vec2 getVec2f() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -357,7 +355,7 @@ public:
 			break;
 		}
 	}
-	const inline glm::vec3 getVec3f() const
+	const inline glm::vec3 getVec3f() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -397,7 +395,7 @@ public:
 			break;
 		}
 	}
-	const inline glm::vec4 getVec4f() const
+	const inline glm::vec4 getVec4f() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -437,7 +435,7 @@ public:
 			break;
 		}
 	}
-	const inline std::string getString() const
+	const inline std::string getString() const noexcept
 	{
 		switch(m_variableType)
 		{
@@ -482,7 +480,7 @@ public:
 			break;
 		}
 	}
-	const inline Properties::PropertyID getID() const
+	const inline Properties::PropertyID getID() const noexcept
 	{
 		if(m_variableType == Type_propertyID)
 			return m_variable.m_ID;
@@ -491,22 +489,22 @@ public:
 	}
 
 	// Get the type of the native value stored inside the Property
-	const inline PropertyVariableType getVariableType() const { return m_variableType; }
+	const inline PropertyVariableType getVariableType() const noexcept { return m_variableType; }
 
 	// Returns the property ID of Property class, NOT the property ID stored in enum (as property value)
-	const inline Properties::PropertyID getPropertyID() const				{ return m_propertyID;			}
-	const inline void setPropertyID(Properties::PropertyID &p_propertyID)	{ m_propertyID = p_propertyID;	}
+	const inline Properties::PropertyID getPropertyID() const noexcept { return m_propertyID; }
+	const inline void setPropertyID(const Properties::PropertyID &p_propertyID) noexcept { m_propertyID = p_propertyID;	}
 
 	// Equal and less than operators accepting property ID enum
-	const inline bool operator==(const Properties::PropertyID &p_propertyID) const { return (m_propertyID == p_propertyID); }
-	const inline bool operator<(const Properties::PropertyID &p_propertyID) const { return (m_propertyID < p_propertyID); }
+	const inline bool operator==(const Properties::PropertyID &p_propertyID) const noexcept { return (m_propertyID == p_propertyID); }
+	const inline bool operator<(const Properties::PropertyID &p_propertyID) const noexcept { return (m_propertyID < p_propertyID); }
 
 	// Equal and less than operators accepting property class
-	const inline bool operator==(const Property &p_property) const { return (m_propertyID == p_property.m_propertyID); }
-	const inline bool operator<(const Property &p_property) const { return (m_propertyID < p_property.m_propertyID); }
+	const inline bool operator==(const Property &p_property) const noexcept { return (m_propertyID == p_property.m_propertyID); }
+	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)
+	Property &operator=(const Property &p_property) noexcept
 	{
 		m_propertyID = p_property.m_propertyID;
 		m_variableType = p_property.m_variableType;
@@ -549,13 +547,13 @@ public:
 	}
 
 	// Bool operator; returns true if the property is valid and false if the property is null
-	inline explicit operator bool() const { return m_propertyID != Properties::Null; }
+	inline explicit operator bool() const noexcept { return m_propertyID != Properties::Null; }
 
 	// Returns true if the native variable type of the property is string (meaning the string that was retrieved from the property is
-	const inline bool isVariableTypeString() const { return (m_variableType == PropertyVariableType::Type_string); }
+	const inline bool isVariableTypeString() const noexcept { return (m_variableType == PropertyVariableType::Type_string); }
 
 	// Converts the property to text (used for saving properties to text files)
-	const inline std::string toString() { return "\"" + std::string(GetString(m_propertyID)) + "\": \"" + getString() + "\""; }
+	const inline std::string toString() const noexcept { return "\"" + std::string(GetString(m_propertyID)) + "\": \"" + getString() + "\""; }
 private:
 	PropertyVariableType m_variableType;
 	union VariableUnion
@@ -582,13 +580,13 @@ private:
 class PropertySet
 {
 public:
-	PropertySet(Properties::PropertyID p_propertyID = Properties::Null)
+	PropertySet(Properties::PropertyID p_propertyID = Properties::Null) noexcept
 		: m_propertyID(p_propertyID), m_optimizedForSearch(false), m_numPropertySets(0), m_numProperties(0) { }
 	~PropertySet() { }
 
 	// Adds an instance of property to internal array; takes only the arguments of property
 	template<class... T_Args>
-	inline void addProperty(T_Args&&... p_args)
+	inline void addProperty(T_Args&&... p_args) noexcept
 	{
 		m_properties.emplace_back(std::forward<T_Args>(p_args)...);
 		m_numProperties++;
@@ -597,7 +595,7 @@ public:
 
 	// Adds an instance of property set to internal array; takes only the arguments of property set
 	template<class... T_Args>
-	inline PropertySet &addPropertySet(T_Args&&... p_args)
+	inline PropertySet &addPropertySet(T_Args&&... p_args) noexcept
 	{
 		m_propertySets.emplace_back(std::forward<T_Args>(p_args)...);
 		m_numPropertySets++;
@@ -606,7 +604,7 @@ public:
 	}
 	
 	// Adds an instance of property to internal array
-	inline void addProperty(Property &&p_property)
+	inline void addProperty(Property &&p_property) noexcept
 	{
 		m_properties.push_back(p_property);
 		m_numProperties++;
@@ -614,7 +612,7 @@ public:
 	}
 	
 	// Adds an instance of property set to internal array
-	inline PropertySet &addPropertySet(PropertySet &&p_propertySet)
+	inline PropertySet &addPropertySet(PropertySet &&p_propertySet) noexcept
 	{
 		m_propertySets.push_back(p_propertySet);
 		m_numPropertySets++;
@@ -641,7 +639,7 @@ public:
 
 	// Finds a property with a matching ID and returns it. Uses faster search
 	// of the property set is optimized for search (by calling optimizeForSearch())
-	const inline Property &getPropertyByID(Properties::PropertyID p_propertyID) const
+	const inline Property &getPropertyByID(const Properties::PropertyID p_propertyID) const noexcept
 	{
 		if(m_optimizedForSearch)
 		{
@@ -667,7 +665,7 @@ public:
 	
 	// Finds a property set with a matching ID and returns it. Uses faster search
 	// of the property set is optimized for search (by calling optimizeForSearch())
-	const inline PropertySet &getPropertySetByID(Properties::PropertyID p_propertyID) const
+	const inline PropertySet &getPropertySetByID(const Properties::PropertyID p_propertyID) const noexcept
 	{
 		if(m_optimizedForSearch)
 		{
@@ -691,7 +689,7 @@ public:
 	}
 	
 	// Returns a property by index; checks for out of bounds index and returns null property in that case
-	const inline Property &getProperty(size_t p_index) const
+	const inline Property &getProperty(const size_t p_index) const noexcept
 	{
 		// Check if index is in bounds
 		if(p_index >= 0 && p_index < m_numProperties)
@@ -701,7 +699,7 @@ public:
 	}
 
 	// Returns a property set by index; checks for out of bounds index and returns null property set in that case
-	const inline PropertySet &getPropertySet(size_t p_index) const
+	const inline PropertySet &getPropertySet(const size_t p_index) const noexcept
 	{
 		// Check if index is in bounds
 		if(p_index >= 0 && p_index < m_numPropertySets)
@@ -712,26 +710,26 @@ public:
 
 	// Returns a property by index without checking if index is valid.
 	// Unsafe - can go out of bounds, use for performance critical code only
-	const inline Property &getPropertyUnsafe(size_t p_index) const { return m_properties[p_index]; }
+	const inline Property &getPropertyUnsafe(const size_t p_index) const noexcept { return m_properties[p_index]; }
 
 	// Returns a property set by index without checking if index is valid.
 	// Unsafe - can go out of bounds, use for performance critical code only
-	const inline PropertySet &getPropertySetUnsafe(size_t p_index) const { return m_propertySets[p_index]; }
+	const inline PropertySet &getPropertySetUnsafe(const size_t p_index) const noexcept { return m_propertySets[p_index]; }
 
 	// Array subscription operator; retrieves an individual property.
 	// Unsafe - can go out of bounds, use for performance critical code only
-	const inline Property &operator[] (size_t p_index) const { return m_properties[p_index]; }
+	const inline Property &operator[] (const size_t p_index) const noexcept { return m_properties[p_index]; }
 
 	// Equal and less than operators accepting property ID enum
-	const inline bool operator==(const Properties::PropertyID &p_propertyID) const { return (m_propertyID == p_propertyID); }
-	const inline bool operator<(const Properties::PropertyID &p_propertyID) const { return (m_propertyID < p_propertyID); }
+	const inline bool operator==(const Properties::PropertyID &p_propertyID) const noexcept { return (m_propertyID == p_propertyID); }
+	const inline bool operator<(const Properties::PropertyID &p_propertyID) const noexcept { return (m_propertyID < p_propertyID); }
 
 	// Equal and less than operators accepting property set class
-	const inline bool operator==(const PropertySet &p_propertySet) const { return (m_propertyID == p_propertySet.m_propertyID); }
-	const inline bool operator<(const PropertySet &p_propertySet) const { return (m_propertyID < p_propertySet.m_propertyID); }
+	const inline bool operator==(const PropertySet &p_propertySet) const noexcept { return (m_propertyID == p_propertySet.m_propertyID); }
+	const inline bool operator<(const PropertySet &p_propertySet) const noexcept { return (m_propertyID < p_propertySet.m_propertyID); }
 
 	// Adds two PropertySets together (including their pripertySets and individual properties)
-	inline PropertySet operator+(const PropertySet &p_propertySet) const 
+	inline PropertySet operator+(const PropertySet &p_propertySet) const noexcept
 	{
 		// Create a new propertySet that is a copy of the currentProperty set
 		PropertySet combinedSet(*this);
@@ -752,13 +750,93 @@ public:
 	}
 
 	// Setters
-	const inline void setPropertyID(Properties::PropertyID p_propertyID) { m_propertyID = p_propertyID; }
+	const inline void setPropertyID(const Properties::PropertyID p_propertyID) { m_propertyID = p_propertyID; }
 
 	// Getters
 	inline size_t getNumProperties() const { return m_numProperties; }
 	inline size_t getNumPropertySets() const { return m_numPropertySets; }
 	const inline Properties::PropertyID getPropertyID() const { return m_propertyID; }
 
+	// Sets the passed bool to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, bool &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getBool();
+	}
+
+	// Sets the passed int to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, int &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getInt();
+	}
+
+	// Sets the passed float to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, float &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getFloat();
+	}
+
+	// Sets the passed double to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, double &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getDouble();
+	}
+
+	// Sets the passed ivec2 to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, glm::ivec2 &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getVec2i();
+	}
+
+	// Sets the passed vec2 to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, glm::vec2 &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getVec2f();
+	}
+
+	// Sets the passed vec3 to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, glm::vec3 &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getVec3f();
+	}
+
+	// Sets the passed vec4 to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, glm::vec4 &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getVec4f();
+	}
+
+	// Sets the passed string to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, std::string &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getString();
+	}
+
+	// Sets the passed PropertyID to the value of a property matching the passed ID, if the property is present
+	const inline void getValueByID(const Properties::PropertyID p_propertyID, Properties::PropertyID &p_value) const noexcept
+	{
+		auto &valueProperty = getPropertyByID(p_propertyID);
+		if(valueProperty)
+			p_value = valueProperty.getPropertyID();
+	}
+
 	// Bool operator; returns true if the property is valid and false if the property is null
 	inline explicit operator bool() const { return m_propertyID != Properties::Null; }
 

+ 0 - 29
Praxis3D/Source/RigidBodyComponent.h

@@ -53,22 +53,11 @@ public:
 	}
 	ErrorCode init() final override
 	{
-		// Mark the object as loaded, because there is nothing to be specifically loaded, at least for now
-		//setLoadedToMemory(true);
-		//setLoadedToVideoMemory(true);
-
 		return ErrorCode::Success;
 	}
 
 	void loadToMemory()
 	{
-		auto luaError = ErrorCode::Success;// m_luaScript.init();
-
-		if(luaError != ErrorCode::Success)
-			ErrHandlerLoc().get().log(luaError, ErrorSource::Source_LuaComponent, m_name);
-		//else
-		//	m_luaScriptLoaded = true;
-
 		setActive(true);
 	}
 
@@ -80,24 +69,6 @@ public:
 
 			postChanges(Systems::Changes::Spatial::LocalTransformNoScale);
 		}
-
-		//std::cout << m_motionState.getWorldTransform()[3].x << " : " << m_motionState.getWorldTransform()[3].y << " : " << m_motionState.getWorldTransform()[3].z << std::endl;
-
-		/*/ Perform updates only the if the script is loaded
-		if(m_luaScriptLoaded)
-		{
-			// Get the current spatial data
-			m_luaSpatialData.setSpatialData(*m_spatialData);
-
-			// Update the lua script
-			m_luaScript.update(p_deltaTime);
-
-			// Get the changes from the lua script
-			auto changes = m_luaScript.getChanges();
-
-			// Post the new changes
-			postChanges(changes);
-		}*/
 	}
 
 	ErrorCode importObject(const PropertySet &p_properties) final override

+ 78 - 12
Praxis3D/Source/SceneLoader.cpp

@@ -393,11 +393,77 @@ void SceneLoader::importFromProperties(AudioComponentsConstructionInfo &p_constr
 	{
 		switch(p_properties.getPropertyID())
 		{
-		case Properties::PropertyID::Audio:
-		{
-			
-		}
-		break;
+			case Properties::PropertyID::SoundComponent:
+			{
+				if(p_constructionInfo.m_soundConstructionInfo == nullptr)
+					p_constructionInfo.m_soundConstructionInfo = new SoundComponent::SoundComponentConstructionInfo();
+
+				p_constructionInfo.m_soundConstructionInfo->m_name = p_name + Config::componentVar().component_name_separator + GetString(Properties::PropertyID::SoundComponent);
+
+				// Get the sound filename
+				auto const &filename = p_properties.getPropertyByID(Properties::Filename).getString();
+
+				if(!filename.empty())
+				{
+					// Get the sound type
+					auto const &type = p_properties.getPropertyByID(Properties::Type).getID();
+
+					// Load values based on the type of sound
+					switch(type)
+					{
+					case Properties::Ambient:
+
+						p_constructionInfo.m_soundConstructionInfo->m_soundType = SoundComponent::SoundType::SoundType_Ambient;
+
+						p_properties.getValueByID(Properties::Loop, p_constructionInfo.m_soundConstructionInfo->m_loop);
+						p_properties.getValueByID(Properties::Spatialized, p_constructionInfo.m_soundConstructionInfo->m_spatialized);
+						p_properties.getValueByID(Properties::StartPlaying, p_constructionInfo.m_soundConstructionInfo->m_startPlaying);
+						p_properties.getValueByID(Properties::Volume, p_constructionInfo.m_soundConstructionInfo->m_volume);
+						p_constructionInfo.m_soundConstructionInfo->m_soundFilename = filename;
+
+						break;
+
+					case Properties::Music:
+
+						p_constructionInfo.m_soundConstructionInfo->m_soundType = SoundComponent::SoundType::SoundType_Music;
+
+						p_properties.getValueByID(Properties::Loop, p_constructionInfo.m_soundConstructionInfo->m_loop);
+						p_properties.getValueByID(Properties::Spatialized, p_constructionInfo.m_soundConstructionInfo->m_spatialized);
+						p_properties.getValueByID(Properties::StartPlaying, p_constructionInfo.m_soundConstructionInfo->m_startPlaying);
+						p_properties.getValueByID(Properties::Volume, p_constructionInfo.m_soundConstructionInfo->m_volume);
+						p_constructionInfo.m_soundConstructionInfo->m_soundFilename = filename;
+
+						break;
+
+					case Properties::SoundEffect:
+
+						p_constructionInfo.m_soundConstructionInfo->m_soundType = SoundComponent::SoundType::SoundType_SoundEffect;
+
+						p_properties.getValueByID(Properties::Loop, p_constructionInfo.m_soundConstructionInfo->m_loop);
+						p_properties.getValueByID(Properties::Spatialized, p_constructionInfo.m_soundConstructionInfo->m_spatialized);
+						p_properties.getValueByID(Properties::StartPlaying, p_constructionInfo.m_soundConstructionInfo->m_startPlaying);
+						p_properties.getValueByID(Properties::Volume, p_constructionInfo.m_soundConstructionInfo->m_volume);
+						p_constructionInfo.m_soundConstructionInfo->m_soundFilename = filename;
+
+						break;
+
+					default:
+
+						ErrHandlerLoc().get().log(ErrorCode::Property_missing_type, p_name, ErrorSource::Source_SoundComponent);
+						delete p_constructionInfo.m_soundConstructionInfo;
+						p_constructionInfo.m_soundConstructionInfo = nullptr;
+
+						break;
+					}
+				}
+				else
+				{
+					ErrHandlerLoc().get().log(ErrorCode::Property_no_filename, p_name, ErrorSource::Source_SoundComponent);
+					delete p_constructionInfo.m_soundConstructionInfo;
+					p_constructionInfo.m_soundConstructionInfo = nullptr;
+				}
+			}
+			break;
 		}
 	}
 }
@@ -435,8 +501,8 @@ void SceneLoader::importFromProperties(GraphicsComponentsConstructionInfo &p_con
 
 					p_constructionInfo.m_lightConstructionInfo->m_lightComponentType = LightComponent::LightComponentType::LightComponentType_directional;
 
-					p_constructionInfo.m_lightConstructionInfo->m_color = p_properties.getPropertyByID(Properties::Color).getVec3f();
-					p_constructionInfo.m_lightConstructionInfo->m_intensity = p_properties.getPropertyByID(Properties::Intensity).getFloat();
+					p_properties.getValueByID(Properties::Color, p_constructionInfo.m_lightConstructionInfo->m_color);
+					p_properties.getValueByID(Properties::Intensity, p_constructionInfo.m_lightConstructionInfo->m_intensity);
 
 					break;
 
@@ -444,8 +510,8 @@ void SceneLoader::importFromProperties(GraphicsComponentsConstructionInfo &p_con
 
 					p_constructionInfo.m_lightConstructionInfo->m_lightComponentType = LightComponent::LightComponentType::LightComponentType_point;
 
-					p_constructionInfo.m_lightConstructionInfo->m_color = p_properties.getPropertyByID(Properties::Color).getVec3f();
-					p_constructionInfo.m_lightConstructionInfo->m_intensity = p_properties.getPropertyByID(Properties::Intensity).getFloat();
+					p_properties.getValueByID(Properties::Color, p_constructionInfo.m_lightConstructionInfo->m_color);
+					p_properties.getValueByID(Properties::Intensity, p_constructionInfo.m_lightConstructionInfo->m_intensity);
 
 					break;
 
@@ -453,9 +519,9 @@ void SceneLoader::importFromProperties(GraphicsComponentsConstructionInfo &p_con
 
 					p_constructionInfo.m_lightConstructionInfo->m_lightComponentType = LightComponent::LightComponentType::LightComponentType_spot;
 
-					p_constructionInfo.m_lightConstructionInfo->m_color = p_properties.getPropertyByID(Properties::Color).getVec3f();
-					p_constructionInfo.m_lightConstructionInfo->m_cutoffAngle = p_properties.getPropertyByID(Properties::CutoffAngle).getFloat();
-					p_constructionInfo.m_lightConstructionInfo->m_intensity = p_properties.getPropertyByID(Properties::Intensity).getFloat();
+					p_properties.getValueByID(Properties::Color, p_constructionInfo.m_lightConstructionInfo->m_color);
+					p_properties.getValueByID(Properties::CutoffAngle, p_constructionInfo.m_lightConstructionInfo->m_cutoffAngle);
+					p_properties.getValueByID(Properties::Intensity, p_constructionInfo.m_lightConstructionInfo->m_intensity);
 
 					break;
 

+ 95 - 0
Praxis3D/Source/SoundComponent.h

@@ -0,0 +1,95 @@
+#pragma once
+
+#include <fmod/core/fmod.hpp>
+#include <fmod/core/fmod_errors.h>
+#include <fmod/core/fmod_common.h>
+
+#include "InheritanceObjects.h"
+
+class SoundComponent : public SystemObject
+{
+	friend class AudioScene;
+public:
+	enum SoundType : unsigned int
+	{
+		SoundType_Null = 0,
+		SoundType_Music,
+		SoundType_Ambient,
+		SoundType_SoundEffect
+	};
+
+	struct SoundComponentConstructionInfo : public SystemObject::SystemObjectConstructionInfo
+	{
+		SoundComponentConstructionInfo()
+		{
+			m_soundType = SoundType::SoundType_Music;
+			m_volume = 1.0f;
+			m_loop = false;
+			m_spatialized = false;
+			m_startPlaying = false;
+		}
+
+		std::string m_soundFilename;
+		SoundType m_soundType;
+		float m_volume;
+		bool m_loop,
+			 m_spatialized,
+			 m_startPlaying;
+	};
+
+	SoundComponent(SystemScene *p_systemScene, std::string p_name, const EntityID p_entityID, std::size_t p_id = 0) : SystemObject(p_systemScene, p_name, Properties::PropertyID::SoundComponent, p_entityID)
+	{
+		m_sound = nullptr;
+		m_channel = nullptr;
+		m_soundType = SoundType::SoundType_Null;
+		m_volume = 1.0f;
+		m_loop = false;
+		m_spatialized = false;
+		m_startPlaying = false;
+		m_playing = false;
+
+		m_soundExInfo = new FMOD_CREATESOUNDEXINFO();
+		m_soundExInfo->cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+	}
+	~SoundComponent()
+	{
+
+	}
+	ErrorCode init() final override
+	{
+		return ErrorCode::Success;
+	}
+
+	void loadToMemory()
+	{
+		setActive(true);
+	}
+
+	void update(const float p_deltaTime)
+	{
+
+	}
+
+	BitMask getSystemType() final override { return Systems::Audio; }
+
+	BitMask getDesiredSystemChanges() final override { return Systems::Changes::Audio::All; }
+	BitMask getPotentialSystemChanges() final override { return Systems::Changes::None; }
+
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+	{
+
+	}
+
+private:
+	FMOD::Sound *m_sound;
+	FMOD::Channel *m_channel;
+	FMOD_CREATESOUNDEXINFO *m_soundExInfo;
+
+	SoundType m_soundType;
+	std::string m_soundFilename;
+	float m_volume;
+	bool m_loop,
+		 m_spatialized,
+		 m_startPlaying,
+		 m_playing;
+};

+ 29 - 3
Praxis3D/Source/System.h

@@ -60,30 +60,52 @@ public:
 		: m_initialized(false), m_system(p_system), m_sceneLoader(p_sceneLoader) { }
 	//~SystemScene();
 
+	// Gets the parent system
 	SystemBase *getSystem() { return m_system; }
 
+	// Initializes the scene
 	virtual ErrorCode init() = 0;
 
+	// Internal scene data-driven setup based on passed properties
 	virtual ErrorCode setup(const PropertySet &p_properties) = 0;
+	
+	// Activation is called when the engine play state containing this scene is made current (activated)
+	virtual void activate() { }
+
+	// Deactivation is called when the engine play state changes and this scene isn't current anymore
+	virtual void deactivate() { }
 
+	// Called every frame with the last frame's delta time
 	virtual void update(const float p_deltaTime) = 0;
 
+	// Load all the scene objects at once
 	virtual ErrorCode preload() = 0;
 
+	// Start loading all the scene objects in the background threads and return (without waiting)
 	virtual void loadInBackground() = 0;
 
-	// Exports all the data of the scene (including all objects within) as a PropertySet (for example, used for saving to map file)
-	//virtual PropertySet exportObject() { return PropertySet(Properties::Null); }
-
+	// Create all the components that belong to this scene, that are contained in ComponentsConstructionInfo; return a vector of all created components
 	virtual std::vector<SystemObject*> createComponents(const EntityID p_entityID, const ComponentsConstructionInfo &p_constructionInfo, const bool p_startLoading = true);
+
+	// Destroy an object that belongs to this system scene
 	virtual ErrorCode destroyObject(SystemObject *p_systemObject) = 0;
+
+	// Returns a null system object that is used as a fall-back when an object creation fails
 	virtual SystemObject *getNullObject();
 
+	// Called whenever there are changes pending for this scene; always called at the end of frame
 	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
 
+	// Return the parent system task
 	virtual SystemTask *getSystemTask() = 0;
+
+	// Return the system type that this scene belongs to
 	virtual Systems::TypeID getSystemType() = 0;
+
+	// Return the data changes this scene is subscribed to
 	virtual BitMask getDesiredSystemChanges() { return Systems::Changes::None; };
+
+	// Return the parent scene loader
 	inline SceneLoader *getSceneLoader() { return m_sceneLoader; }
 
 protected:
@@ -199,6 +221,10 @@ public:
 
 	virtual bool isPrimaryThreadOnly() = 0;
 
+	virtual void activate() { m_systemScene->activate(); }
+
+	virtual void deactivate() { m_systemScene->deactivate(); }
+
 protected:
 	SystemScene *m_systemScene;
 };

+ 23 - 120
Praxis3D/Source/TaskManager.cpp

@@ -3,138 +3,21 @@
 #include <tbb/blocked_range.h>
 #include <tbb/parallel_for.h>
 
-//#include "Global.h"
 #include "EngineDefinitions.h"
 #include "ClockLocator.h"
 #include "TaskManager.h"
 
+// Initialize static variables in TaskManagerGlobal namespace
 namespace TaskManagerGlobal
 {
-	class GenericCallbackData
-	{
-	public:
-		GenericCallbackData(void *p_param) : m_param(p_param) { }
-
-	protected:
-		void *m_param;
-	};
-
-	template<class ClassPointer>
-	class GenericCallbackTask : public tbb::task, public GenericCallbackData
-	{
-	public:
-		GenericCallbackTask(ClassPointer p_ptr, void *p_param) : GenericCallbackData(p_param), m_ptr(p_ptr) { }
-
-		virtual tbb::task *execute()
-		{
-			//TODO ERROR
-			assert(m_ptr != nullptr);
-
-			m_ptr(m_param);
-
-			return NULL;
-		}
-
-	protected:
-		ClassPointer m_ptr;
-	};
-
-	class SynchronizeTask : public tbb::task
-	{
-	public:
-		SynchronizeTask() { }
-
-		tbb::task *execute()
-		{
-			// TODO ERRORS
-			assert(m_callback != NULL);
-			assert(m_allCallbacksInvokedEvent != NULL);
-
-			m_callback(m_callbackParam);
-
-			if(InterlockedDecrement(&m_callbacksCount) == 0)
-			{
-				SetEvent(m_allCallbacksInvokedEvent);
-			}
-			else
-			{
-				WaitForSingleObject(m_allCallbacksInvokedEvent, INFINITE);
-			}
-
-			return NULL;
-		}
-
-		static void prepareCallback(TaskManager::JobFunct p_func, void *p_param, unsigned int p_count)
-		{
-			m_callback = p_func;
-			m_callbackParam = p_param;
-			m_callbacksCount = p_count;
-			ResetEvent(m_allCallbacksInvokedEvent);
-		}
-
-	protected:
-		friend class TaskManager;
-		static void	*m_callbackParam;
-		static volatile long m_callbacksCount;
-		static void *m_allCallbacksInvokedEvent;
-		static TaskManager::JobFunct m_callback;
-	};
-
-	class StallTask : public tbb::task
-	{
-	public:
-		StallTask(TaskManager *p_taskManager, void *p_waitFor) : m_taskManager(p_taskManager), m_waitFor(p_waitFor) { }
-
-		tbb::task *execute()
-		{
-			if(m_taskManager->isPrimaryThread())
-			{
-				// Cannot stall a primary task, so stall some other task
-				m_taskManager->addStallTask();
-
-				// Wait a bit to give some time for some thread to pick up the stall task
-				// TODO change hardcoded value
-				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.1));
-			}
-			else
-			{
-				//TODO ERROR
-				assert(m_waitFor != nullptr);
-				WaitForSingleObject(m_waitFor, INFINITE);
-			}
-			return NULL;
-		}
-
-	protected:
-		void		*m_waitFor;
-		TaskManager *m_taskManager;
-	};
-
-	class ParallelFor : public GenericCallbackData
-	{
-	public:
-		ParallelFor(TaskManager::ParallelForFunc p_pfCallback, void *p_param) : GenericCallbackData(p_param), m_parallelForCallback(p_pfCallback) { }
-
-		void operator () (const tbb::blocked_range<unsigned int> & p_right) const
-		{
-			m_parallelForCallback(m_param, p_right.begin(), p_right.end());
-		}
-
-	private:
-		TaskManager::ParallelForFunc m_parallelForCallback;
-	};
-
 	void *SynchronizeTask::m_callbackParam = nullptr;
 	volatile long SynchronizeTask::m_callbacksCount = 0;
 	void *SynchronizeTask::m_allCallbacksInvokedEvent = nullptr;
 	TaskManager::JobFunct SynchronizeTask::m_callback = nullptr;
-
-	//static TaskManager *g_taskManager = nullptr;
 }
 
 TaskManager::TaskManager()
 {
-
 	m_stallPoolParent = nullptr;
 	m_systemTasksRoot = nullptr;
 	m_tbbScheduler = nullptr;
@@ -333,9 +216,29 @@ void TaskManager::issueJobsForSystemTasks(SystemTask **p_tasks, unsigned int p_c
 				//if(getPerformanceHint(p_tasks[currentTask]) == (PerformanceHint)perfHint)
 				//{
 					// This task can be run on an arbitrary thread - allocate it 
-					TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct> *systemTask
+					//TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct> *systemTask
+					//	= new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot))
+					//	TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct>(systemTaskCallback, p_tasks[currentTask]);
+
+					//std::bind(p_func, currentTask, std::forward<T_Type>(p_args)...)
+
+					//auto test = std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime);
+
+					//TaskManagerGlobal::GenericCallbackTask2<std::function<void(SystemTask *)>> *systemTask = new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot)) TaskManagerGlobal::GenericCallbackTask2<std::function<void(SystemTask *)>>(std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime));
+					//TaskManagerGlobal::GenericCallbackTask2<std::function<void()>> *systemTask = new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot)) TaskManagerGlobal::GenericCallbackTask2<std::function<void()>>(std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime));
+
+					//TaskManagerGlobal::GenericCallbackTask3<void(SystemTask::*)(SystemTask*, float), SystemTask*, float> *systemTask = new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot))
+					//	TaskManagerGlobal::GenericCallbackTask3<void(SystemTask::*)(SystemTask*, float), SystemTask*, float>(&SystemTask::update, p_tasks[currentTask], p_deltaTime);
+
+					//auto test = std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime);
+
+					TaskManagerGlobal::GenericCallbackTaskFunctor<decltype(std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime))> *systemTask
 						= new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot))
-						TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct>(systemTaskCallback, p_tasks[currentTask]);
+						TaskManagerGlobal::GenericCallbackTaskFunctor<decltype(std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime))>(std::bind(&SystemTask::update, p_tasks[currentTask], p_deltaTime));
+
+
+					//TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct> *systemTask
+					//	= new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot))TaskManagerGlobal::GenericCallbackTask<TaskManager::JobFunct>(systemTaskCallback2, std::function<void(SystemTask*, float)>(&SystemTask::update), p_tasks[currentTask], p_deltaTime);
 
 					// TODO ASSERT ERROR
 					assert(systemTask != nullptr);

+ 219 - 151
Praxis3D/Source/TaskManager.h

@@ -11,6 +11,7 @@
 #include <vector>
 #include "Window.h"
 
+#include "EngineDefinitions.h"
 #include "System.h"
 #include "SpinWait.h"
 
@@ -62,12 +63,108 @@ public:
 	// and their subtasks are complete. Work on tasks dedicated to the primary thread
 	void waitForSystemTasks(SystemTask **p_tasks, unsigned int p_count);
 
+	// Called from primary thread to wait until specified tasks spawned with issueJobsForSystemTasks
+	// and their subtasks are complete. Work on tasks dedicated to the primary thread
+	// Templated version
+	template<typename T_Func, typename... T_Args>
+	void waitForSystemTasks(SystemTask **p_tasks, unsigned int p_count, T_Func &p_func, T_Args&&... p_args)
+	{
+		//TODO ERROR
+		assert(isPrimaryThread());
+		assert(p_count > 0);
+		assert(p_count <= Systems::Types::Max);
+
+		// Execute the tasks we are waiting, now
+		// Save the tasks we aren't waiting, for next time
+
+		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+		for(std::vector<SystemTask *>::iterator iterator = m_primaryThreadSystemTaskList.begin(); iterator != m_primaryThreadSystemTaskList.end(); iterator++)
+		{
+			// Check if we are waiting for this thread
+			if(std::find(p_tasks, p_tasks + p_count, *iterator))
+			{
+				// If we are, execute it on the primary thread
+				p_func(*iterator, std::forward<T_Args>(p_args)...);
+			}
+			else
+			{
+				// If we aren't, save it for next time
+				m_tempTasksList.push_back(*iterator);
+			}
+		}
+
+		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+		m_primaryThreadSystemTaskList.clear();
+		m_primaryThreadSystemTaskList.swap(m_tempTasksList);
+
+		// Wait for the parallel calculation
+		m_systemTasksRoot->wait_for_all();
+	}
+
 	// Does a callback once from each thread used by the TaskManager; Calls one at a time, and only once
 	void nonStandardPerThreadCallback(JobFunct p_callback, void *p_data);
 
 	// Assigns threads to tasks and executed them; deals with tasks that are to be run on primary thread
 	void issueJobsForSystemTasks(SystemTask **p_tasks, unsigned int p_count, float p_deltaTime);
 
+	// Assigns threads to tasks and executed them; deals with tasks that are to be run on primary thread
+	// Templated version
+	template<typename T_Func, typename... T_Args>
+	void issueJobsForSystemTasks(SystemTask **p_tasks, unsigned int p_count, T_Func &p_func, T_Args&&... p_args)
+	{
+		//TODO ERROR
+		assert(isPrimaryThread());
+		assert(p_count > 0);
+
+		//m_deltaTime = p_deltaTime;
+
+		updateThreadPoolSize();
+
+		assert(m_systemTasksRoot != nullptr);
+
+		// Set reference count to 1, to support wait_for_all
+		m_systemTasksRoot->set_ref_count(1);
+
+		// Schedule tasks based on their performance hint order
+		tbb::task_list taskList;
+		unsigned int affinityCount = (unsigned int)m_affinityIDs.size();
+
+		// TODO: implement performance hint
+
+		for(unsigned int currentTask = 0; currentTask < p_count; currentTask++)
+		{
+#ifdef SETTING_MULTITHREADING_ENABLED
+			if(p_tasks[currentTask]->isPrimaryThreadOnly())
+			{
+				m_primaryThreadSystemTaskList.push_back(p_tasks[currentTask]);
+			}
+			else
+			{
+				auto test = std::bind(p_func, p_tasks[currentTask], std::forward<T_Args>(p_args)...);
+
+				TaskManagerGlobal::GenericCallbackTaskFunctor<decltype(test)> *systemTask
+					= new(m_systemTasksRoot->allocate_additional_child_of(*m_systemTasksRoot))
+					TaskManagerGlobal::GenericCallbackTaskFunctor<decltype(test)>(test);
+				
+				// TODO ASSERT ERROR
+				assert(systemTask != nullptr);
+
+				// Affinity will increase the chances that each SystemTask will be assigned
+				// to a unique thread, regardless of PerformanceHint
+				systemTask->set_affinity(m_affinityIDs[currentTask % affinityCount]);
+				taskList.push_back(*systemTask);
+			}
+#else
+			m_primaryThreadSystemTaskList.push_back(p_tasks[currentTask]);
+#endif
+		}
+
+		// We only spawn system tasks here. They in their turn will spawn descendant tasks.
+		// Waiting for the whole bunch completion happens in WaitForSystemTasks.
+		m_systemTasksRoot->spawn(taskList);
+	}
+
 	// TBB paralle_for wrapper for job tasks
 	void parallelFor(SystemTask *p_systemTask, ParallelForFunc p_jobFunc, void *p_param, unsigned int p_begin, unsigned int p_end, unsigned int p_minGrainSize = 1);
 
@@ -78,10 +175,8 @@ public:
 		// If multi-threading is enabled 
 #ifdef SETTING_MULTITHREADING_ENABLED
 		m_backgroundTaskGroup.run(p_func);
-
 #else
 		p_func();
-
 #endif
 	}
 
@@ -93,11 +188,9 @@ public:
 		// If multi-threading is enabled 
 #ifdef SETTING_MULTITHREADING_ENABLED
 		tbb::parallel_for(p_first, p_last, p_step, p_func);
-
 #else
 		for(Index i = p_first; i < p_last; i += p_step)
 			p_func(i);
-
 #endif
 	}
 
@@ -106,8 +199,8 @@ public:
 	{
 		// TODO: implement job instruction hints to issue tasks on threads more efficiently 
 
-		//return m_numOfThreads;
-		return PerformanceHint::Task_NoPerformanceHint;
+		return m_numOfThreads;
+		//return PerformanceHint::Task_NoPerformanceHint;
 	}
 
 	// Waits for the background threads to finish their work
@@ -155,164 +248,139 @@ private:
 	unsigned int m_numOfRequestedThreads;
 };
 
-/*#include <tbb\tbb.h>
-#include <wtypes.h>
-
-#include "TaskSet.h"
-
-typedef void(*TaskSetFunc)(void*, int, unsigned int, unsigned int);
-typedef unsigned int TaskSetHandle;
-
-#define TASKSETHANDLE_INVALID			0xFFFFFFFF
-#define MAX_SUCCESSORS                  5
-#define MAX_TASKSETS                    256
-#define MAX_TASKSETNAMELENGTH           512
-
-tbb::atomic<INT>						g_contextIdCount;
-tbb::enumerable_thread_specific<INT>	g_contextId;
-
-class TbbContextId;
-class GenericTask;
-class TaskSet;
-
-class TaskManager
-{
-public:
-TaskManager();
-~TaskManager();
-
-void init();
-void shutdown();
-
-bool createTaskSet(TaskSetFunc p_func, void *p_arg, unsigned int p_taskCount, TaskSetHandle *p_depends,
-unsigned int p_dependsCount, OPTIONAL char* p_name, OUT TaskSetHandle *p_outHandle);
-
-void releaseHandle(TaskSetHandle p_taskSet);
-void releaseHandles(TaskSetHandle *p_taskSetList, unsigned int p_taskSetCount);
-
-void waitForSet(TaskSetHandle p_taskSet);
-void waitForAll();
-
-bool isSetComplete(TaskSetHandle p_set);
-
-private:
-friend class GenericTask;
-
-TaskSetHandle allocateTaskSet();
-
-void completeTaskSet(TaskSetHandle p_set);
-
-TaskSet			*m_taskSets[MAX_TASKSETS];
-TbbContextId	*m_tbbContextId;
-void			*m_tbbInit;
-unsigned int	m_nextFreeSet;
-};
-
-class TbbContextId : public tbb::task_scheduler_observer
-{
-void on_scheduler_entry(bool)
-{
-INT context = g_contextIdCount.fetch_and_increment();
-g_contextId.local() = context;
-}
-
-public:
-TbbContextId()
-{
-g_contextIdCount = 0;
-observe(true);
-}
-};
-
-class SpinLock
-{
-public:
-SpinLock() : m_lock(0)	{ }
-~SpinLock()				{ }
-
-void lock()
-{
-while (_InterlockedCompareExchange((long*)&m_lock, 1, 0) == 1)
+namespace TaskManagerGlobal
 {
+	// Used to store a single parameter for generic callback
+	class GenericCallbackData
+	{
+	public:
+		GenericCallbackData(void *p_param) : m_param(p_param) { }
 
-}
-}
-
-void unlock()
-{
-m_lock = 0;
-}
-
-private:
-volatile unsigned int m_lock;
-};
+	protected:
+		void *m_param;
+	};
 
-class GenericTask : public tbb::task
-{
-public:
-GenericTask();
-GenericTask(TaskSetFunc p_func, void* p_arg, unsigned int p_id, unsigned int p_size, char* p_setName, TaskSetHandle p_taskSet) :
-m_func(p_func), m_arg(p_arg), m_id(p_id), m_size(p_size), m_setName(p_setName), m_taskSet(p_taskSet)
-{
+	// Stores a function pointer (usually TaskManager::JobFunct) and a single parameter
+	template<typename T_Func>
+	class GenericCallbackTask : public tbb::task, public GenericCallbackData
+	{
+	public:
+		GenericCallbackTask(T_Func p_ptr, void *p_param) : GenericCallbackData(p_param), m_ptr(p_ptr) { }
 
-}
+		tbb::task *execute()
+		{
+			//TODO ERROR
+			assert(m_ptr != nullptr);
 
-task* execute();
-//{
-//	m_func(m_arg, g_contextId.local(), m_id, m_size);
-//	g_taskManager.completeTaskSet(m_taskSet);
-//}
+			m_ptr(m_param);
 
-private:
-TaskSetFunc		m_func;
-void*			m_arg;
-unsigned int	m_id;
-unsigned int	m_size;
-char*			m_setName;
-TaskSetHandle		m_taskSet;
-};
+			return NULL;
+		}
 
-class TaskSet : public tbb::task
-{
-public:
-TaskSet() :
-m_func(NULL),
-m_arg(0),
-m_size(0),
-m_taskSet(TASKSETHANDLE_INVALID),
-m_hasBeenWaitedOn(false)
-{
-m_setName[0] = 0;
-memset(m_successors, 0, sizeof(m_successors));
-}
+	protected:
+		T_Func m_ptr;
+	};
 
-~TaskSet() { }
+	// Stores a function pointer (to be used with std::bind, so the function pointer already contains all the parameters it needs)
+	template<typename T_Func>
+	class GenericCallbackTaskFunctor : public tbb::task
+	{
+	public:
+		GenericCallbackTaskFunctor(T_Func &p_func) : m_func(p_func) { }
 
-task* execute()
-{
-set_ref_count(m_size + 1);
+		tbb::task *execute()
+		{
+			m_func();
 
-for (unsigned int i = 0; i < m_size; i++)
-{
-spawn(*new(allocate_child()) GenericTask(m_func, m_arg, i, m_size, m_setName, m_taskSet));
-}
+			return NULL;
+		}
 
-return NULL;
-}
+	private:
+		T_Func m_func;
+	};
 
-TaskSet			*m_successors[MAX_SUCCESSORS];
-TaskSetHandle	m_taskSet;
-bool			m_hasBeenWaitedOn;
+	class SynchronizeTask : public tbb::task
+	{
+	public:
+		SynchronizeTask() { }
+
+		tbb::task *execute()
+		{
+			// TODO ERRORS
+			assert(m_callback != NULL);
+			assert(m_allCallbacksInvokedEvent != NULL);
+
+			m_callback(m_callbackParam);
+
+			if(InterlockedDecrement(&m_callbacksCount) == 0)
+			{
+				SetEvent(m_allCallbacksInvokedEvent);
+			}
+			else
+			{
+				WaitForSingleObject(m_allCallbacksInvokedEvent, INFINITE);
+			}
+
+			return NULL;
+		}
+
+		static void prepareCallback(TaskManager::JobFunct p_func, void *p_param, unsigned int p_count)
+		{
+			m_callback = p_func;
+			m_callbackParam = p_param;
+			m_callbacksCount = p_count;
+			ResetEvent(m_allCallbacksInvokedEvent);
+		}
+
+	protected:
+		friend class TaskManager;
+		static void *m_callbackParam;
+		static volatile long m_callbacksCount;
+		static void *m_allCallbacksInvokedEvent;
+		static TaskManager::JobFunct m_callback;
+	};
 
-TaskSetFunc	m_func;
-void		*m_arg;
+	class StallTask : public tbb::task
+	{
+	public:
+		StallTask(TaskManager *p_taskManager, void *p_waitFor) : m_taskManager(p_taskManager), m_waitFor(p_waitFor) { }
+
+		tbb::task *execute()
+		{
+			if(m_taskManager->isPrimaryThread())
+			{
+				// Cannot stall a primary task, so stall some other task
+				m_taskManager->addStallTask();
+
+				// Wait a bit to give some time for some thread to pick up the stall task
+				// TODO change hardcoded value
+				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.1));
+			}
+			else
+			{
+				//TODO ERROR
+				assert(m_waitFor != nullptr);
+				WaitForSingleObject(m_waitFor, INFINITE);
+			}
+			return NULL;
+		}
+
+	protected:
+		void *m_waitFor;
+		TaskManager *m_taskManager;
+	};
 
-volatile unsigned int	m_startCount;
-volatile unsigned int	m_completionCount;
-volatile unsigned int	m_refCount;
+	class ParallelFor : public GenericCallbackData
+	{
+	public:
+		ParallelFor(TaskManager::ParallelForFunc p_pfCallback, void *p_param) : GenericCallbackData(p_param), m_parallelForCallback(p_pfCallback) { }
 
-unsigned int	m_size;
-SpinLock		m_successorsLock;
+		void operator () (const tbb::blocked_range<unsigned int> &p_right) const
+		{
+			m_parallelForCallback(m_param, p_right.begin(), p_right.end());
+		}
 
-char	m_setName[MAX_TASKSETNAMELENGTH];
-};*/
+	private:
+		TaskManager::ParallelForFunc m_parallelForCallback;
+	};
+}

+ 6 - 5
Praxis3D/Source/TaskScheduler.cpp

@@ -3,12 +3,13 @@
 #include "TaskScheduler.h"
 
 // TODO DATA DRIVEN
-const float TaskScheduler::m_defaultClockFrequency = 1.0f / 120.0f;      // Set the timer to 120Hz
+//const float TaskScheduler::m_defaultClockFrequency = 1.0f / 120.0f;      // Set the timer to 120Hz
 
-TaskScheduler::TaskScheduler(TaskManager *p_taskManager) : m_taskManager(p_taskManager),
-m_clockFrequency(m_defaultClockFrequency),
-m_executionTimer(nullptr),
-m_multithreadingEnabled(true) // TODO DATA DRIVEN
+TaskScheduler::TaskScheduler(TaskManager *p_taskManager) 
+	: m_taskManager(p_taskManager),
+	m_clockFrequency(1.0f / Config::engineVar().task_scheduler_clock_frequency),
+	m_executionTimer(nullptr),
+	m_multithreadingEnabled(true) // TODO DATA DRIVEN
 {
 	m_multithreadingEnabled = (p_taskManager != nullptr);
 }

+ 80 - 0
Praxis3D/Source/TaskScheduler.h

@@ -5,6 +5,7 @@
 #include "System.h"
 #include "Universal.h"
 
+class SystemScene;
 class TaskManager;
 
 class TaskScheduler
@@ -15,8 +16,87 @@ public:
 
 	void setScene(const UniversalScene *p_scene);
 
+	// Calls update (passing the delta time) on all registered scenes
 	void execute(float p_deltaTime);
 
+	// Calls any member function of SystemTask that is passed as an argument, and forwards all passed parameters
+	template<typename T_Func, typename... T_Args>
+	void execute(T_Func &p_func, T_Args&&... p_args)
+	{
+		// If multithreading is enabled, execute tasks over threads in parallel
+		if(m_multithreadingEnabled)
+		{
+			// Create temp containers to hold current tasks (to execute in this time-step)
+			SystemTask *tasksToExecute[Systems::TypeID::NumberOfSystems];
+			unsigned int numTasksToExecute = 0;
+
+			// Iterate over all the system scenes and get their tasks
+			for(auto it = m_systemScenes.begin(); it != m_systemScenes.end(); it++)
+			{
+				// Get the scene
+				SystemScene *currentScene = it->second;
+
+				// Get the scene's task (and increment the count)
+				tasksToExecute[numTasksToExecute++] = currentScene->getSystemTask();
+			}
+
+			// Execute the tasks in parallel, by passing them to the task manager
+			m_taskManager->issueJobsForSystemTasks(tasksToExecute, numTasksToExecute, p_func, std::forward<T_Args>(p_args)...);
+
+			m_taskManager->waitForSystemTasks(tasksToExecute, numTasksToExecute, p_func, std::forward<T_Args>(p_args)...);
+		}
+		// If multithreading is disabled, execute tasks in serial
+		else
+		{
+			// Iterate over all the system scenes and update them
+			for(UniversalScene::SystemSceneMap::iterator it = m_systemScenes.begin(); it != m_systemScenes.end(); it++)
+			{
+				// Get the scene
+				SystemScene *currentScene = it->second;
+
+				// Execute the task straight away (hence in serial)
+				p_func(currentScene->getSystemTask(), std::forward<T_Args>(p_args)...);
+			}
+		}
+	}
+
+	// Calls any member function of SystemTask that is passed as an argument, and forwards all passed parameters
+	template <typename... T_Type>
+	void executeViaBackgroundThreads(std::function<void(SystemTask *, T_Type...)> p_func, T_Type&&... p_args)
+	{
+		// Contains tasks that can only be executed in the primary thread
+		std::vector<SystemTask*> primaryThreadTasks;
+
+		// Iterate over all the system scenes and get their tasks
+		for(auto it = m_systemScenes.begin(); it != m_systemScenes.end(); it++)
+		{
+			// Get the scene
+			SystemScene *currentScene = it->second;
+
+			// Get the scene's task (and increment the count)
+			SystemTask *currentTask = currentScene->getSystemTask();
+
+			// If the task is only to be executed in primary thread, add it to an array; otherwise execute the member function in a background thread
+			if(currentTask->isPrimaryThreadOnly())
+				primaryThreadTasks.push_back(currentTask);
+			else
+				m_taskManager->startBackgroundThread(std::bind(p_func, currentTask, std::forward<T_Type>(p_args)...));
+		}
+
+		// Temporary elevate the primary thread priority
+		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+		// Execute all tasks that are set for primary thread
+		for(decltype(primaryThreadTasks.size()) i = 0, size = primaryThreadTasks.size(); i < size; i++)
+			p_func(primaryThreadTasks[i], std::forward<T_Type>(p_args)...);
+
+		// Return the primary thread priority to normal
+		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+
+		// Wait for previously spawned tasks to finish
+		m_taskManager->waitForBackgroundThreads();
+	}
+
 protected:
 	static const float m_defaultClockFrequency;
 	TaskManager *m_taskManager;

+ 9 - 0
Praxis3D/Source/WorldScene.cpp

@@ -87,6 +87,9 @@ EntityID WorldScene::createEntity(const ComponentsConstructionInfo &p_constructi
 			break;
 		}
 
+	// Add AUDIO components
+	std::vector<SystemObject*> audioComponents = m_sceneLoader->getSystemScene(Systems::Audio)->createComponents(newEntity, p_constructionInfo, p_startLoading);
+
 	// Add RENDERING components
 	std::vector<SystemObject*> renderingComponents = m_sceneLoader->getSystemScene(Systems::Graphics)->createComponents(newEntity, p_constructionInfo, p_startLoading);
 
@@ -127,6 +130,12 @@ EntityID WorldScene::createEntity(const ComponentsConstructionInfo &p_constructi
 				m_sceneLoader->getChangeController()->createObjectLink(scriptingComponents[scriptingIndex], physicsComponents[physicsIndex]);
 		}
 
+		// Link SCRIPTING -> AUDIO
+		for(decltype(audioComponents.size()) audioIndex = 0, audioSize = audioComponents.size(); audioIndex < audioSize; audioIndex++)
+		{
+			m_sceneLoader->getChangeController()->createObjectLink(scriptingComponents[scriptingIndex], audioComponents[audioIndex]);
+		}
+
 		// Link SCRIPTING -> GUI
 		for(decltype(guiComponents.size()) guiIndex = 0, guiSize = guiComponents.size(); guiIndex < guiSize; guiIndex++)
 		{

+ 5 - 0
Praxis3D/imgui.ini

@@ -63,6 +63,11 @@ Pos=60,60
 Size=500,300
 Collapsed=0
 
+[Window][FMOD LOGO]
+Pos=44,1015
+Size=176,68
+Collapsed=0
+
 [Table][0x9A4BDFDE,4]
 RefScale=13
 Column 0  Sort=0v