Bladeren bron

Added ambient occlusion mapping in geometryPass shader, lightPass shader
Added checks for current engine state, to not move the camera while a modifier (CTRL) is pressed during Editor state, in Camera lua script
Added saving the last opened directory in file browser dialog so it opens on the last opened path, in GuiScene, EditorWindow
Added all loaded shaders, models and Lua scripts to the asset browser, in EditorWindow
Added a way to reload and change settings of loaded shaders in the asset browser, in EditorWindow, RendererScene
Added a way to reload an already loaded shader, after changing its source code in a shader file, in RendererBackend, RendererScene, ShaderLoader, ShaderUniformUpdater
Added ambient light and projection Z-buffer settings to the scene settings window, in EditorWindow
Added ambient light intensity and Z-buffer limit settings, their changes and loading from file, in EditorWindow, RendererScene, RendererFrontend
Added ambient light intensity uniform value to shaders, in ShaderUniform, ShaderUniformUpdater, lightPass shader
Added Lua callbacks for getting the current engine state, in LuaScript
Added more Config variables
Added more Change types
Incremented engine version to 0.1.2 and commit date to 2023-12-16
Fixed a bug of using Sequence property ID instead of GUISequenceComponent, SceneLoader

Paul A 2 jaren geleden
bovenliggende
commit
31f7c13464

+ 1 - 0
.gitignore

@@ -43,3 +43,4 @@ compileData.scor.incl
 compileData.scor.t0000
 *.glb
 *.ilk
+*.aps

+ 15 - 4
Praxis3D/Data/Maps/componentTest.pmap

@@ -87,6 +87,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -143,7 +144,7 @@
 				{
 					"Active": "true",
 					"LocalPosition": "0.0f, 0.5f, 0.0f",
-					"LocalRotation": "0.0f, 45f, 0.0f",
+					"LocalRotation": "0.0f, 44.9999f, 0.0f",
 					"LocalRotationQuaternion": "0.92388f, 0.0f, 0.382683f, 0.0f",
 					"LocalScale": "1.0f, 1.0f, 1.0f"
 				}
@@ -184,6 +185,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -225,7 +227,7 @@
 					"CollisionShape": 
 					{
 						"Type": "Box",
-						"Size": "1.96f, 1.96f, 1.96f"
+						"Size": "1.92f, 1.92f, 1.92f"
 					}
 				}
 			},
@@ -267,6 +269,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -329,6 +332,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -447,7 +451,7 @@
 				{
 					"Active": "true",
 					"LocalPosition": "0.0f, 0.0f, 0.0f",
-					"LocalRotation": "-40.0f, 0.0f, 0.0f",
+					"LocalRotation": "-40f, 0.0f, 0.0f",
 					"LocalRotationQuaternion": "0.939693f, -0.34202f, 0.0f, 0.0f",
 					"LocalScale": "1.0f, 1.0f, 1.0f"
 				}
@@ -606,6 +610,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -647,7 +652,7 @@
 					"CollisionShape": 
 					{
 						"Type": "Box",
-						"Size": "1.96f, 1.96f, 1.96f"
+						"Size": "1.92f, 1.92f, 1.92f"
 					}
 				}
 			},
@@ -698,6 +703,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -765,6 +771,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -845,6 +852,7 @@
 									"AlphaThreshold": "0.0f",
 									"EmissiveIntensity": "0.0f",
 									"HeightScale": "0.0f",
+									"WrapMode": "Repeat",
 									"Materials": 
 									{
 										"Diffuse": 
@@ -928,6 +936,9 @@
 		{
 			"Scene": 
 			{
+				"AmbientIntensity": "0.2f",
+				"ZFar": "40000.0f",
+				"ZNear": "1.0f",
 				"ObjectPoolSize": 
 				{
 					"CameraComponent": "1",

+ 1 - 1
Praxis3D/Data/Maps/mainMenu.pmap

@@ -39,7 +39,7 @@
 			},
 			"GUI":
 			{
-				"Sequence":
+				"GUISequenceComponent":
 				{
 
 				}

+ 28 - 11
Praxis3D/Data/Scripts/Camera_free_object_spawn.lua

@@ -12,7 +12,8 @@ function init ()
 	create(Types.KeyCommand, 'rightKey')
 	create(Types.KeyCommand, 'upKey')
 	create(Types.KeyCommand, 'downKey')
-	create(Types.KeyCommand, 'sprintKey')
+	create(Types.KeyCommand, 'sprintKey')	
+	create(Types.KeyCommand, 'ctrlKey')
 	create(Types.KeyCommand, 'mouseLeftKey')
 	create(Types.KeyCommand, 'mouseRightKey')
 	
@@ -24,6 +25,7 @@ function init ()
 	upKey:bind(inputVariables.up_key)
 	downKey:bind(inputVariables.down_key)
 	sprintKey:bind(inputVariables.sprint_key)
+	ctrlKey:bindByName('Key_leftctrl')
 	mouseLeftKey:bindByName('Mouse_left')
 	mouseRightKey:bindByName('Mouse_right')
 	
@@ -49,6 +51,8 @@ function init ()
 	end
 		
 	ErrHandlerLoc.logErrorCode(ErrorCode.Initialize_success, getLuaFilename())
+	
+	movementKeysActive = true;
 end
 
 function update (p_deltaTime)
@@ -75,10 +79,23 @@ function update (p_deltaTime)
 		localTransformMat4 = localTransformMat4:rotate(toRadianF(horizontalAngleF), Vec3.new(0.0, 1.0, 0.0))
 	end
 	
-	if mouseRightKey:isActivated() then
-		setMouseCapture(true)
-	else
-		setMouseCapture(false)
+	-- Perform only when in Editor mode
+	if (getEngineState() == EngineStateType.Editor) then
+	
+		-- Stop capturing movement keys when CTRL is pressed (for example, when saving a scene with CTRL+S shortcut)
+		if ctrlKey:isActivated() then
+			movementKeysActive = false
+		else
+			movementKeysActive = true
+		end
+		
+		-- Capture mouse when right mouse button is pressed only, since uncaptured mouse is required to interact with the Editor GUI
+		if mouseRightKey:isActivated() then
+			setMouseCapture(true)
+		else
+			setMouseCapture(false)
+		end
+		
 	end
 		
 	-- Get the view direction that is facing forward
@@ -102,26 +119,26 @@ function update (p_deltaTime)
 	
 	-- Adjust camera position based on key presses and view direction
 	-- Forwards / backwards - Z direction
-	if forwardKey:isActivated() then
+	if movementKeysActive and forwardKey:isActivated() then
 		positionVec3 = positionVec3 - forwardDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end	
-	if backwardKey:isActivated() then
+	if movementKeysActive and backwardKey:isActivated() then
 		positionVec3 = positionVec3 + forwardDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end
 	
 	-- Left / right - X direction
-	if rightKey:isActivated() then
+	if movementKeysActive and rightKey:isActivated() then
 		positionVec3 = positionVec3 + rightDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end	
-	if leftKey:isActivated() then
+	if movementKeysActive and leftKey:isActivated() then
 		positionVec3 = positionVec3 - rightDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end	
 	
 	-- Up / down - Y direction
-	if upKey:isActivated() then
+	if movementKeysActive and upKey:isActivated() then
 		positionVec3 = positionVec3 + upDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end	
-	if downKey:isActivated() then
+	if movementKeysActive and downKey:isActivated() then
 		positionVec3 = positionVec3 - upDirectionVec3:mulF(finalMovementSpeedF * p_deltaTime)
 	end
 	

+ 1 - 0
Praxis3D/Data/Shaders/atmosphericScatteringPass_ground_simple.frag

@@ -694,6 +694,7 @@ void main()
 	vec3 normal = normalize(texture(normalMap, texCoord).xyz);
 	
 	float distanceToFrag = length(cameraPosVec - worldPos) / kLengthUnitInMeters;
+		
 	vec3 cameraPosition = cameraPosVec / kLengthUnitInMeters;
 	worldPos = worldPos / kLengthUnitInMeters;
 	

+ 21 - 21
Praxis3D/Data/Shaders/geometryPass.frag

@@ -310,17 +310,14 @@ vec2 parallaxOcclusionMapping(vec2 texCoords, vec3 viewDir, float p_LOD)
 }
 
 vec2 simpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
-{ 
-    //float height =  texture(heightTexture, p_texCoords).r;     
-    //return p_texCoords - (p_viewDir.xy ) * (height * 0.01);
-	
+{
 	float height =  getHeight(p_texCoords);    
     vec2 p = p_viewDir.xy  * (height * 0.02);
     return p_texCoords - p;
 }
 
 void main(void)
-{ 	
+{
 	float height = getHeight(texCoord);
 	vec2 newCoords = texCoord;
 	
@@ -346,32 +343,35 @@ void main(void)
 	if(diffuse.a < alphaThreshold)
 		discard;
 	
-	// Get roughness and metalness values, and emissive color
-	float roughness = getRoughness(newCoords);
-	float metalness = getMetalness(newCoords);
+	// Get roughness and metalness values with the new coordinates
+	// R - roughness
+	// G - metalness
+	// B - height
+	// A - ambient occlusion
+	vec4 combinedTextureValues = texture(combinedTexture, newCoords).rgba;
+	
+	// Get the emissive color with the new coordinates
 	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
 	
 	// Use emissive alpha channel as an intensity multiplier
-	emissiveColor *= emissiveMultiplier;
-	// Apply emissive color only if it's above the threshold
-	if(emissiveColor.a > emissiveThreshold)
-	{
-		// Use emissive alpha channel as an intensity multiplier
-		//emissiveColor *= emissiveColor.a * emissiveMultiplier;
-	}
-	else
-	{
-		//emissiveColor = vec4(0.0);
-	}
+	emissiveColor *= emissiveMultiplier;// * emissiveColor.a;
+	
+	// Write roughness, metalness to the material properties buffer
+	matPropertiesBuffer = vec4(
+	max(combinedTextureValues.x, MIN_ROUGHNESS), 	// R - roughness
+	combinedTextureValues.y, 						// G - metalness
+	combinedTextureValues.w, 						// B - ambient occlusion
+	1.0);											// A - unused for now
 	
 	// Write diffuse color to the diffuse buffer
 	diffuseBuffer = diffuse;
-	// Write roughness, metalness to the material properties buffer
-	matPropertiesBuffer = vec4(roughness, metalness, 1.0, 1.0);
+	
 	// Write emissive color into the emissive buffer
 	emissiveBuffer = emissiveColor;
+	
 	// Write fragment's position in world space	to the position buffer
 	positionBuffer = fragPos;
+	
 	// Perform normal mapping and write the new normal to the normal buffer
 	normalBuffer = TBN * normalize(texture(normalTexture, newCoords).rgb * 2.0 - 1.0);
 }

+ 39 - 10
Praxis3D/Data/Shaders/lightPass.frag

@@ -72,6 +72,7 @@ uniform float gamma;
 uniform int numPointLights;
 uniform int numSpotLights;
 
+uniform float ambientLightIntensity;
 uniform DirectionalLight directionalLight;
 
 // Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
@@ -153,7 +154,7 @@ vec3 fresnelSchlick(float p_cosTheta, vec3 p_F0)
     return p_F0 + (1.0 - p_F0) * pow(1.0 - p_cosTheta, 5.0);
 } 
 
-vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, float p_lightDistance, vec3 p_F0, float p_roughness, float p_metalic)
+vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, float p_lightDistance, vec3 p_F0, float p_roughness, float p_metalic, float p_ambientOcclusion)
 {	
 	/*/ Get specular and diffuse lighting
 	vec3 specularColor = LightingFuncGGX_REF(p_normal, p_fragToEye, p_lightDirection, p_roughnessSqrt, p_F0);
@@ -174,6 +175,7 @@ vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_
 	
 	vec3 kS = F;
 	vec3 kD = vec3(1.0) - kS;
+	//vec3 kDambient = kD * (1.0 - p_metalic * 0.5);
 	kD *= 1.0 - p_metalic;
 	
 	vec3 numerator = NDF * G * F;
@@ -182,7 +184,10 @@ vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_
 	
 	// Combine diffuse, specular, radiance with albedo color and return it
 	float NdotL = max(dot(p_normal, p_lightDirection), 0.0);
-	return (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+	//return (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+	lightColor += radiance * p_ambientOcclusion * ambientLightIntensity * (kD * p_albedoColor);
+	return lightColor;
 }
 
 vec2 calcTexCoord(void)
@@ -207,10 +212,12 @@ void main(void)
 	// Calculate view direction (fragment to eye vector)
 	fragmentToEye = normalize(cameraPosVec - worldPos);
 	
-	// Extract roughness and metalness values
+	// Extract roughness, metalness and ambient occlusion values
 	float roughnessSqrt = matProperties.x;
 	float metalic = matProperties.y;
-	
+	float ambientOcclusion = matProperties.z;
+	ambientOcclusion *= ambientOcclusion;
+
 	// Calculate F0, with minimum IOR as 0.04
 	vec3 f0 = mix(vec3(0.04), diffuseColor, metalic);
 	
@@ -231,7 +238,7 @@ void main(void)
 		float dirLightIntensity = directionalLight.m_intensity * mix(g_sunSetIntensityMod, g_sunNoonIntensityMod, dirLightFactorSqrt);
 	
 		// Add ambient lighting
-		finalLightColor += diffuseColor * dirLightIntensity * 0.003;
+		//finalLightColor += diffuseColor * dirLightColor * dirLightIntensity * ambientLightIntensity * ambientOcclusion * (1.0 - metalic * metalic);
 	
 		// Add directional lighting
 		finalLightColor += calcLightColor(
@@ -243,7 +250,8 @@ void main(void)
 			1.0, 
 			f0, 
 			roughnessSqrt, 
-			metalic) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
+			metalic,
+			ambientOcclusion) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
 	}
 		
 	for(int i = 0; i < numPointLights; i++)
@@ -254,7 +262,17 @@ void main(void)
 		lightDirection = normalize(lightDirection);
 		
 		// Light color multiplied by intensity and divided by attenuation
-		finalLightColor += (calcLightColor(diffuseColor, normal, fragmentToEye, pointLights[i].m_color, lightDirection, lightDistance, f0, roughnessSqrt, metalic) * pointLights[i].m_intensity);
+		finalLightColor += (calcLightColor(
+			diffuseColor, 
+			normal, 
+			fragmentToEye, 
+			pointLights[i].m_color, 
+			lightDirection, 
+			lightDistance, 
+			f0, 
+			roughnessSqrt, 
+			metalic, 
+			ambientOcclusion) * pointLights[i].m_intensity);
 	}
 	
 	for(int i = 0; i < numSpotLights; i++)
@@ -274,7 +292,17 @@ void main(void)
 			lightDirection = normalize(lightDirection);
 			
 			// Light color multiplied by intensity
-			vec3 lightColor = (calcLightColor(diffuseColor, normal, fragmentToEye, spotLights[i].m_color, lightDirection, lightDistance, f0, roughnessSqrt, metalic) * spotLights[i].m_intensity);
+			vec3 lightColor = (calcLightColor(
+				diffuseColor, 
+				normal, 
+				fragmentToEye, 
+				spotLights[i].m_color, 
+				lightDirection, 
+				lightDistance, 
+				f0, 
+				roughnessSqrt, 
+				metalic, 
+				ambientOcclusion) * spotLights[i].m_intensity);
 			
 			// Light restriction from cone
 			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
@@ -284,6 +312,7 @@ void main(void)
 	}
 	
 	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
-	//colorBuffer = vec4(0.0, 1.0, 0.0, 1.0);
-	//colorBuffer = vec4(diffuseColor + emissiveColor, 1.0);
+	//colorBuffer = vec4(ambientOcclusion, ambientOcclusion, ambientOcclusion, 1.0);
+	//colorBuffer = vec4(matProperties.b, matProperties.b, matProperties.b, 1.0);
+	//colorBuffer = vec4(diffuseColor / PI, 1.0);
 }

+ 2 - 0
Praxis3D/Source/Config.cpp

@@ -163,6 +163,7 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, render_to_texture_resolution_y);
 	AddVariablePredef(m_graphicsVar, tonemap_method);
 	AddVariablePredef(m_graphicsVar, alpha_threshold);
+	AddVariablePredef(m_graphicsVar, ambient_light_intensity);
 	AddVariablePredef(m_graphicsVar, emissive_multiplier);
 	AddVariablePredef(m_graphicsVar, emissive_threshold);
 	AddVariablePredef(m_graphicsVar, eye_adaption_rate);
@@ -419,6 +420,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, texelSize);
 	AddVariablePredef(m_shaderVar, numOfTexels);
 	AddVariablePredef(m_shaderVar, mipLevel);
+	AddVariablePredef(m_shaderVar, ambientLightIntensity);
 	AddVariablePredef(m_shaderVar, dirLightColor);
 	AddVariablePredef(m_shaderVar, dirLightDirection);
 	AddVariablePredef(m_shaderVar, dirLightIntensity);

+ 33 - 9
Praxis3D/Source/Config.h

@@ -36,6 +36,7 @@ enum DataType : uint32_t
 	DataType_GUIPassFunctors,			// FunctorSequence
 	DataType_RenderToTexture,			// bool
 	DataType_RenderToTextureResolution, // glm::ivec2
+	DataType_LoadShader,				// ShaderLoader::ShaderProgram
 	DataType_LoadTexture2D,				// TextureLoader2D::Texture2DHandle
 	DataType_UnloadTexture2D,			// TextureLoader2D::Texture2DHandle
 	DataType_LoadTexture3D,
@@ -43,9 +44,11 @@ enum DataType : uint32_t
 	DataType_UnloadModel,
 	DataType_ModelsProperties,			// ModelComponent::ModelsProperties
 	// GUI
+	DataType_AboutWindow,				// bool
 	DataType_EnableGUISequence,			// bool
 	DataType_EditorWindow,				// EditorWindowSettings
 	DataType_FileBrowserDialog,			// FileBrowserDialog
+	DataType_SettingsWindow,			// bool
 	// Physics
 	DataType_SimulationActive,			// bool
 	// Scripting
@@ -162,14 +165,22 @@ namespace Systems
 			static constexpr BitMask Shared19	= (BitMask)1 << 19;
 			static constexpr BitMask Shared20	= (BitMask)1 << 20;
 			static constexpr BitMask Shared21	= (BitMask)1 << 21;
+			static constexpr BitMask Shared23	= (BitMask)1 << 23;
+			static constexpr BitMask Shared24	= (BitMask)1 << 24;
+			static constexpr BitMask Shared25	= (BitMask)1 << 25;
+			static constexpr BitMask Shared26	= (BitMask)1 << 26;
+			static constexpr BitMask Shared27	= (BitMask)1 << 27;
+			static constexpr BitMask Shared28	= (BitMask)1 << 28;
+			static constexpr BitMask Shared29	= (BitMask)1 << 29;
+			static constexpr BitMask Shared30	= (BitMask)1 << 30;
 		}
 		namespace Unique
 		{
-			static constexpr BitMask Unique1 = (BitMask)1 << 30;
-			static constexpr BitMask Unique2 = (BitMask)1 << 31;
-			static constexpr BitMask Unique3 = (BitMask)1 << 32;
-			static constexpr BitMask Unique4 = (BitMask)1 << 33;
-			static constexpr BitMask Unique5 = (BitMask)1 << 34;
+			static constexpr BitMask Unique1 = (BitMask)1 << 50;
+			static constexpr BitMask Unique2 = (BitMask)1 << 51;
+			static constexpr BitMask Unique3 = (BitMask)1 << 52;
+			static constexpr BitMask Unique4 = (BitMask)1 << 53;
+			static constexpr BitMask Unique5 = (BitMask)1 << 54;
 		}
 		namespace Type
 		{
@@ -240,9 +251,10 @@ namespace Systems
 		}
 		namespace Graphics
 		{
-			static constexpr BitMask Lighting				= Changes::Type::Graphics + Changes::Common::Shared21;
-			static constexpr BitMask Camera					= Changes::Type::Graphics + Changes::Common::Shared20;
-			static constexpr BitMask Framebuffers			= Changes::Type::Graphics + Changes::Common::Shared19;
+			static constexpr BitMask Lighting				= Changes::Type::Graphics + Changes::Common::Shared30;
+			static constexpr BitMask Camera					= Changes::Type::Graphics + Changes::Common::Shared29;
+			static constexpr BitMask Framebuffers			= Changes::Type::Graphics + Changes::Common::Shared28;
+			static constexpr BitMask Scene					= Changes::Type::Graphics + Changes::Common::Shared27;
 
 			static constexpr BitMask Target					= Changes::Type::Graphics + Changes::Graphics::Camera + Changes::Common::Shared1;
 			static constexpr BitMask UpVector				= Changes::Type::Graphics + Changes::Graphics::Camera + Changes::Common::Shared2;
@@ -266,7 +278,12 @@ namespace Systems
 			static constexpr BitMask AllBuffers				= PositionBuffer | DiffuseBuffer | NormalBuffer | EmissiveBuffer | MatPropertiesBuffer | 
 																IntermediateBuffer | FinalBuffer | RenderToTextureBuffer;
 
-			static constexpr BitMask All					= AllCamera | AllLighting | AllBuffers;
+			static constexpr BitMask AmbientIntensity		= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared16;
+			static constexpr BitMask ZFar					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared17;
+			static constexpr BitMask ZNear					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared18;
+			static constexpr BitMask AllScene				= AmbientIntensity | ZFar | ZNear;
+
+			static constexpr BitMask All					= AllCamera | AllLighting | AllBuffers | AllScene;
 		}
 		namespace GUI
 		{
@@ -355,6 +372,7 @@ namespace Properties
 	Code(WorldScale,) \
 	/* Graphics */ \
 	Code(AlphaThreshold, ) \
+	Code(AmbientIntensity, ) \
 	Code(AmbientOcclusion, ) \
 	Code(Attenuation,) \
 	Code(Camera,) \
@@ -421,6 +439,8 @@ namespace Properties
 	Code(TextureScale,) \
 	Code(VertexShader,) \
 	Code(WrapMode,) \
+	Code(ZFar,) \
+	Code(ZNear,) \
 	/* Graphics rendering passes */ \
 	Code(AtmScatteringRenderPass,) \
 	Code(BloomRenderPass,) \
@@ -865,6 +885,7 @@ public:
 			render_to_texture_resolution_y = 900;
 			tonemap_method = 6;
 			alpha_threshold = 0.0f;
+			ambient_light_intensity = 0.3f;
 			bloom_intensity = 1.0f;
 			bloom_knee = 0.1f;
 			bloom_threshold = 1.5f;
@@ -927,6 +948,7 @@ public:
 		int render_to_texture_resolution_y;
 		int tonemap_method;
 		float alpha_threshold;
+		float ambient_light_intensity;
 		float bloom_intensity;
 		float bloom_knee;
 		float bloom_threshold;
@@ -1403,6 +1425,7 @@ public:
 			numOfTexels = "numOfTexels";
 			mipLevel = "mipLevel";
 
+			ambientLightIntensity = "ambientLightIntensity";
 			dirLightColor = "directionalLight.m_color";
 			dirLightDirection = "directionalLight.m_direction";
 			dirLightIntensity = "directionalLight.m_intensity";
@@ -1507,6 +1530,7 @@ public:
 		std::string numOfTexels;
 		std::string mipLevel;
 
+		std::string ambientLightIntensity;
 		std::string dirLightColor;
 		std::string dirLightDirection;
 		std::string dirLightIntensity;

+ 302 - 61
Praxis3D/Source/EditorWindow.cpp

@@ -1356,9 +1356,12 @@ void EditorWindow::update(const float p_deltaTime)
                                                 m_fileBrowserDialog.m_filter = "Model files (.obj .3ds .fbx){.obj,.OBJ,.3ds,.3DS,.fbx,.FBX},All files{.*}";
                                                 m_fileBrowserDialog.m_title = "Open a model file";
                                                 m_fileBrowserDialog.m_name = "OpenModelFileFileDialog";
-                                                m_fileBrowserDialog.m_rootPath = Config::filepathVar().model_path;
                                                 m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_None;
 
+                                                // Set the root path only if it isn't saved from the last file dialog
+                                                if(m_previouslyOpenedFileBrowser != m_currentlyOpenedFileBrowser)
+                                                    m_fileBrowserDialog.m_rootPath = Config::filepathVar().model_path;
+
                                                 // Tell the GUI scene to open the file browser
                                                 m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
                                             }
@@ -1539,9 +1542,12 @@ void EditorWindow::update(const float p_deltaTime)
                                                                 m_fileBrowserDialog.m_filter = "Texture files (.png .tga .tif .tiff .jpg .jpeg .bmp){.png,.PNG,.tga,.TGA,.tif,.tiff,.jpg,.jpeg,.bmp},All files{.*}";
                                                                 m_fileBrowserDialog.m_title = "Open a texture file";
                                                                 m_fileBrowserDialog.m_name = "OpenTextureFileFileDialog";
-                                                                m_fileBrowserDialog.m_rootPath = Config::filepathVar().texture_path;
                                                                 m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_None;
 
+                                                                // Set the root path only if it isn't saved from the last file dialog
+                                                                if(m_previouslyOpenedFileBrowser != m_currentlyOpenedFileBrowser)
+                                                                    m_fileBrowserDialog.m_rootPath = Config::filepathVar().texture_path;
+
                                                                 // Tell the GUI scene to open the file browser
                                                                 m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
                                                             }
@@ -1905,9 +1911,12 @@ void EditorWindow::update(const float p_deltaTime)
                                         m_fileBrowserDialog.m_filter = "Audio files (.wav .flac .mp3 .ogg){.wav,.flac,.mp3,.ogg},All files{.*}";
                                         m_fileBrowserDialog.m_title = "Open an audio file";
                                         m_fileBrowserDialog.m_name = "OpenAudioFileFileDialog";
-                                        m_fileBrowserDialog.m_rootPath = Config::filepathVar().sound_path;
                                         m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_None;
 
+                                        // Set the root path only if it isn't saved from the last file dialog
+                                        if(m_previouslyOpenedFileBrowser != m_currentlyOpenedFileBrowser)
+                                            m_fileBrowserDialog.m_rootPath = Config::filepathVar().sound_path;
+
                                         // Tell the GUI scene to open the file browser
                                         m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
                                     }
@@ -2518,22 +2527,14 @@ void EditorWindow::update(const float p_deltaTime)
                     drawLeftAlignedLabelText("Luminance multiplier:", inputWidgetOffset);
                     ImGui::DragFloat("##LuminanceMultiplierDrag", &Config::m_graphicsVar.luminance_multiplier, 0.001f, 0.0f, 100.0f, "%.5f");
 
-                    ImGui::SeparatorText("Parallax settings:");
+                    ImGui::SeparatorText("Graphics settings:");
 
                     // Draw PARALLAX LOD
                     drawLeftAlignedLabelText("Parallax LOD:", inputWidgetOffset);
-                   ImGui::DragFloat("##ParallaxLODDrag", &Config::m_graphicsVar.LOD_parallax_mapping, 0.1f, 0.0f, 100000.0f, "%.5f");
+                    ImGui::DragFloat("##ParallaxLODDrag", &Config::m_graphicsVar.LOD_parallax_mapping, 0.1f, 0.0f, 100000.0f, "%.5f");
 
                     ImGui::SeparatorText("Renderer settings:");
 
-                    // Draw Z NEAR
-                    drawLeftAlignedLabelText("Z buffer near:", inputWidgetOffset);
-                    ImGui::DragFloat("##ZNearDrag", &Config::m_graphicsVar.z_near, 0.001f, 0.0f, 10000000.0f, "%.5f");
-
-                    // Draw Z FAR
-                    drawLeftAlignedLabelText("Z buffer far:", inputWidgetOffset);
-                    ImGui::DragFloat("##ZFarrag", &Config::m_graphicsVar.z_far, 0.001f, 0.0f, 10000000.0f, "%.5f");
-
                     // Draw OBJECTS LOADER PER FRAME
                     drawLeftAlignedLabelText("Object loads per frame:", inputWidgetOffset);
                     ImGui::InputInt("##ObjectsLoadedPerFrameInput", &Config::m_rendererVar.objects_loaded_per_frame);
@@ -2621,17 +2622,46 @@ void EditorWindow::update(const float p_deltaTime)
 
                 if(ImGui::BeginTabItem("Models"))
                 {
-                    for(decltype(m_modelAssets.size()) i = 0, size = m_modelAssets.size(); i < size; i++)
+                    auto contentRegionWidth = ImGui::GetContentRegionAvail().x;
+
+                    ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
+                    ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
+
+                    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
+
+                    if(ImGui::BeginChild("##ModelAssetsSelection", ImVec2(contentRegionWidth * 0.2f, 0), true))
                     {
-                        ImGui::Text(m_modelAssets[i].second.c_str());
+                        ImGui::SeparatorText("Models:");
+                        for(decltype(m_modelAssets.size()) i = 0, size = m_modelAssets.size(); i < size; i++)
+                        {
+                            if(ImGui::Selectable(m_modelAssets[i].second.c_str(), m_selectedModel != nullptr ? m_modelAssets[i].first->getUniqueID() == m_selectedModel->getUniqueID() : false))
+                            {
+                                m_selectedModel = m_modelAssets[i].first;
+                            }
+                        }
                     }
+                    ImGui::EndChild();
+
+                    ImGui::PopStyleVar(4); // ImGuiStyleVar_ChildBorderSize, ImGuiStyleVar_SeparatorTextAlign, ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
+                    ImGui::PopStyleColor(); // ImGuiCol_Border
+
                     ImGui::EndTabItem();
                 }
 
                 if(ImGui::BeginTabItem("Shaders"))
                 {
+                    if(!m_selectedShaderFilename.empty())
+                    {
+                        m_selectedProgram->setShaderFilename(static_cast<ShaderType>(m_selectedShaderType), m_selectedShaderFilename);
+                        m_selectedProgram->reloadToMemory();
+                        m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_LoadShader, (void *)m_selectedProgram);
+
+                        m_selectedShaderFilename.clear();
+                    }
+
                     auto contentRegionWidth = ImGui::GetContentRegionAvail().x;
-                    auto singleWindowWidth = contentRegionWidth / 3.0f;
 
                     ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
                     ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
@@ -2640,15 +2670,15 @@ void EditorWindow::update(const float p_deltaTime)
                     ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
                     ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
 
-                    if(ImGui::BeginChild("##ShaderAssetsProgramSelection", ImVec2(contentRegionWidth * 0.25f, 0), true))
+                    if(ImGui::BeginChild("##ShaderAssetsProgramSelection", ImVec2(contentRegionWidth * 0.2f, 0), true))
                     {
                         ImGui::SeparatorText("Shader programs:");
                         for(decltype(m_shaderAssets.size()) shaderIndex = 0, size = m_shaderAssets.size(); shaderIndex < size; shaderIndex++)
                         {
-                            if(ImGui::Selectable(m_shaderAssets[shaderIndex].second.c_str(), m_selectedProgramShader == shaderIndex))
+                            if(ImGui::Selectable(m_shaderAssets[shaderIndex].second.c_str(), m_selectedProgram != nullptr ? m_shaderAssets[shaderIndex].first->getCombinedFilename() == m_selectedProgram->getCombinedFilename() : false))
                             {
-                                m_selectedProgramShader = (int)shaderIndex;
-                                m_selectedShader = -1;
+                                m_selectedProgram = m_shaderAssets[shaderIndex].first;
+                                m_selectedShaderType = -1;
                             }
                         }
                     }
@@ -2656,18 +2686,18 @@ void EditorWindow::update(const float p_deltaTime)
 
                     ImGui::SameLine();
 
-                    if(m_selectedProgramShader >= 0 && m_selectedProgramShader < m_shaderAssets.size())
+                    if(m_selectedProgram != nullptr)
                     {
-                        if(ImGui::BeginChild("##ShaderAssetsShaderSelection", ImVec2(contentRegionWidth * 0.25f, 0), true))
+                        if(ImGui::BeginChild("##ShaderAssetsShaderSelection", ImVec2(contentRegionWidth * 0.2f, 0), true))
                         {
                             ImGui::SeparatorText("Shaders:");
                             for(unsigned int shaderType = 0; shaderType < ShaderType::ShaderType_NumOfTypes; shaderType++)
                             {
-                                if(!m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(shaderType).empty())
+                                if(!m_selectedProgram->getShaderFilename(shaderType).empty())
                                 {
-                                    if(ImGui::Selectable(m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(shaderType).c_str(), m_selectedShader == shaderType))
+                                    if(ImGui::Selectable(m_selectedProgram->getShaderFilename(shaderType).c_str(), m_selectedShaderType == shaderType))
                                     {
-                                        m_selectedShader = (int)shaderType;
+                                        m_selectedShaderType = (int)shaderType;
                                     }
                                 }
                             }
@@ -2679,7 +2709,7 @@ void EditorWindow::update(const float p_deltaTime)
 
                     ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
 
-                    if(m_selectedShader >= 0 && m_selectedShader < ShaderType::ShaderType_NumOfTypes)
+                    if(m_selectedShaderType >= 0 && m_selectedShaderType < ShaderType::ShaderType_NumOfTypes)
                     {
                         ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
                         ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
@@ -2689,69 +2719,101 @@ void EditorWindow::update(const float p_deltaTime)
                             ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
                             ImGui::SeparatorText("Shader settings:");
 
-                            // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
-                            float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;                
+                            auto buttonWidth = ImGui::GetContentRegionAvail().x / 4.0f;
 
-                            // Calculate button width so they span across the whole window width
-                            auto buttonWidth = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x * 2.0f) / 3.0f;
+                            // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
+                            float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
 
-                            if(ImGui::Button("Open in text editor", ImVec2(buttonWidth, 0)))
+                            // Draw SHADER FILENAME
+                            auto shaderFilename = m_selectedProgram->getShaderFilename(m_selectedShaderType);
+                            drawLeftAlignedLabelText("Filename:", inputWidgetOffset, calcTextSizedButtonOffset(1) - inputWidgetOffset - m_imguiStyle.FramePadding.x);
+                            if(ImGui::InputText("##ShaderFilenameInput", &shaderFilename, ImGuiInputTextFlags_EnterReturnsTrue))
                             {
-
+                                m_selectedProgram->setShaderFilename(static_cast<ShaderType>(m_selectedShaderType), shaderFilename);
+                                m_selectedProgram->reloadToMemory();
+                                m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_LoadShader, (void *)m_selectedProgram);
                             }
 
-                            ImGui::SameLine();
-
-                            if(ImGui::Button("Open in file explorer", ImVec2(buttonWidth, 0)))
+                            // Draw OPEN button
+                            ImGui::SameLine(calcTextSizedButtonOffset(1));
+                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_OpenFile], "##ShaderFileOpenFileButton", "Open a shader file"))
                             {
-                                ShellExecuteA(NULL, "explore", (Filesystem::getCurrentDirectory() + "\\" + Config::filepathVar().shader_path + Utilities::stripFilePath(m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(m_selectedShader))).c_str(), NULL, NULL, SW_SHOWDEFAULT);
-                            }
+                                // Only open the file browser if it's not opened already
+                                if(m_currentlyOpenedFileBrowser == FileBrowserActivated::FileBrowserActivated_None)
+                                {
+                                    // Set the file browser activation to Shader File
+                                    m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_ShaderFile;
 
-                            ImGui::SameLine();
+                                    // Define file browser variables
+                                    m_fileBrowserDialog.m_filter = "Shader files (.frag .vert .geom .tesc .tese .comp .glsl .vs .fs){.frag,.vert,.geom,.tesc,.tese,.comp,.glsl,.vs,.fs},All files{.*}";
+                                    m_fileBrowserDialog.m_title = "Open a shader file";
+                                    m_fileBrowserDialog.m_name = "OpenShaderFileFileDialog";
+                                    m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_None;
 
-                            if(ImGui::Button("Reload shader program", ImVec2(buttonWidth, 0)))
-                            {
+                                    // Set the root path only if it isn't saved from the last file dialog
+                                    if(m_previouslyOpenedFileBrowser != m_currentlyOpenedFileBrowser)
+                                        m_fileBrowserDialog.m_rootPath = Config::filepathVar().shader_path;
 
+                                    // Tell the GUI scene to open the file browser
+                                    m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
+                                }
                             }
 
-                            ImGui::Separator();
+                            const std::string shaderSelectionPopupName = "##ShaderSelectionPopup";
 
-                            // Draw SHADER FILENAME
-                            auto shaderFilename = m_shaderAssets[m_selectedProgramShader].first->getShaderFilename(m_selectedShader);
-                            drawLeftAlignedLabelText("Filename:", inputWidgetOffset, calcTextSizedButtonOffset(1) - inputWidgetOffset - m_imguiStyle.FramePadding.x);
-                            if(ImGui::InputText("##ShaderFilenameInput", &shaderFilename, ImGuiInputTextFlags_EnterReturnsTrue))
+                            // Draw OPEN ASSET LIST button
+                            ImGui::SameLine(calcTextSizedButtonOffset(0));
+                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_OpenAssetList], "##ShaderOpenAssetListButton", "Choose a shader from the loaded assets"))
                             {
+                                // Open the pop-up with the shader asset list
+                                ImGui::OpenPopup(shaderSelectionPopupName.c_str());
                             }
 
-                            // Draw OPEN button
-                            ImGui::SameLine(calcTextSizedButtonOffset(1));
-                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_OpenFile], "##ShaderFileOpenFileButton", "Open a shader file"))
+                            // Draw SHADER ASSET LIST
+                            if(ImGui::BeginPopup(shaderSelectionPopupName.c_str()))
                             {
-                            }
+                                // Remove selection border and align text vertically
+                                ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.0f, 0.5f));
+                                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
 
-                            // Draw RELOAD button
-                            ImGui::SameLine(calcTextSizedButtonOffset(0));
-                            if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Reload], "##ShaderFileReloadButton", "Reload the shader"))
-                            {
+                                for(decltype(m_shaderAssets.size()) shaderAssetIndex = 0, shaderAssetSize = m_shaderAssets.size(); shaderAssetIndex < shaderAssetSize; shaderAssetIndex++)
+                                {
+                                    for(unsigned int shaderAssetType = 0; shaderAssetType < ShaderType::ShaderType_NumOfTypes; shaderAssetType++)
+                                    {
+                                        if(!m_shaderAssets[shaderAssetIndex].first->getShaderFilename(shaderAssetType).empty())
+                                        {
+                                            if(ImGui::Selectable(m_shaderAssets[shaderAssetIndex].first->getShaderFilename(shaderAssetType).c_str(), 
+                                                m_shaderAssets[shaderAssetIndex].first->getShaderFilename(shaderAssetType) == m_selectedProgram->getShaderFilename(m_selectedShaderType)))
+                                            {
+                                                m_selectedProgram->setShaderFilename(static_cast<ShaderType>(m_selectedShaderType), m_shaderAssets[shaderAssetIndex].first->getShaderFilename(shaderAssetType));
+                                                m_selectedProgram->reloadToMemory();
+                                                m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_LoadShader, (void *)m_selectedProgram);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                ImGui::PopStyleVar(2); //ImGuiStyleVar_SelectableTextAlign, ImGuiStyleVar_FramePadding
+                                ImGui::EndPopup();
                             }
 
                             // Draw SHADER TYPE
-                            auto shaderType = m_selectedShader;
-                            drawLeftAlignedLabelText("Shader type:", inputWidgetOffset);
+                            auto shaderType = m_selectedShaderType;
+                            drawLeftAlignedLabelText("Shader type:", inputWidgetOffset, ImGui::GetWindowWidth() - inputWidgetOffset - m_imguiStyle.FramePadding.x);
                             if(ImGui::Combo("##ShaderTypePicker", &shaderType, &m_shaderTypeStrings[0], (int)m_shaderTypeStrings.size()))
                             {
                             }
 
                             // Draw DEFAULT SHADER
-                            auto defaultShader = m_shaderAssets[m_selectedProgramShader].first->isDefaultProgram();
-                            drawLeftAlignedLabelText("Default shader:", inputWidgetOffset);
+                            auto defaultShader = m_selectedProgram->isDefaultProgram();
+                            drawLeftAlignedLabelText("Default shader:", inputWidgetOffset, ImGui::GetWindowWidth() - inputWidgetOffset - m_imguiStyle.FramePadding.x);
                             if(ImGui::Checkbox("##DefaultShaderCheck", &defaultShader))
                             {
                             }                           
                             
                             // Draw LOADED-TO-VIDEO-MEMORY
-                            auto loadedToVideoMemory = m_shaderAssets[m_selectedProgramShader].first->isLoadedToVideoMemory();
-                            drawLeftAlignedLabelText("Loaded to video memory:", inputWidgetOffset);
+                            auto loadedToVideoMemory = m_selectedProgram->isLoadedToVideoMemory();
+                            drawLeftAlignedLabelText("Loaded to video memory:", inputWidgetOffset, ImGui::GetWindowWidth() - inputWidgetOffset - m_imguiStyle.FramePadding.x);
                             if(ImGui::Checkbox("##LoadedToVideoMemoryCheck", &loadedToVideoMemory))
                             {
                             }
@@ -2760,7 +2822,7 @@ void EditorWindow::update(const float p_deltaTime)
                             if(ImGui::TreeNodeEx("Uniform updater settings:", ImGuiTreeNodeFlags_Framed)) // ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Leaf
                             {
                                 // Get uniform updater
-                                auto const &uniformUpdater = m_shaderAssets[m_selectedProgramShader].first->getUniformUpdater();
+                                auto const &uniformUpdater = m_selectedProgram->getUniformUpdater();
 
                                 // Draw UPDATES PER FRAME
                                 auto numUpdatesPerFrame = uniformUpdater.getNumUpdatesPerFrame();
@@ -2816,6 +2878,42 @@ void EditorWindow::update(const float p_deltaTime)
                         else
                             ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
                         ImGui::EndChild();
+
+                        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
+                        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
+
+                        ImGui::SameLine();
+
+                        if(ImGui::BeginChild("##ShaderAssetsActions", ImVec2(contentRegionWidth * 0.1f, 0), true))
+                        {
+                            ImGui::SeparatorText("Actions:");
+
+                            // Calculate button width so they span across the whole window width
+                            auto buttonWidth = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x * 2.0f);
+
+                            if(ImGui::Button("Edit", ImVec2(buttonWidth, 20.0f)))
+                            {
+
+                            }
+
+                            if(ImGui::Button("Reload", ImVec2(buttonWidth, 20.0f)))
+                            {
+                                m_selectedProgram->reloadToMemory();
+                                m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_LoadShader, (void *)m_selectedProgram);
+                            }
+
+                            ImGui::NewLine();
+
+                            if(ImGui::Button("Open directory", ImVec2(buttonWidth, 20.0f)))
+                            {
+                                ShellExecuteA(NULL, "explore", (Filesystem::getCurrentDirectory() + "\\" + Config::filepathVar().shader_path + Utilities::stripFilePath(m_selectedProgram->getShaderFilename(m_selectedShaderType))).c_str(), NULL, NULL, SW_SHOWDEFAULT);
+                            }
+
+
+                        }
+                        ImGui::EndChild();
+
+                        ImGui::PopStyleVar(2); // ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
                     }
 
                     ImGui::PopStyleVar(2); // ImGuiStyleVar_ChildBorderSize, ImGuiStyleVar_SeparatorTextAlign
@@ -2826,6 +2924,31 @@ void EditorWindow::update(const float p_deltaTime)
 
                 if(ImGui::BeginTabItem("Scripts"))
                 {
+                    auto contentRegionWidth = ImGui::GetContentRegionAvail().x;
+
+                    ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.26f, 0.26f, 0.26f, 1.0f));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
+                    ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
+
+                    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, m_imguiStyle.FramePadding.y));
+                    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, m_imguiStyle.ItemSpacing.y));
+
+                    if(ImGui::BeginChild("##LuaScriptAssetsSelection", ImVec2(contentRegionWidth * 0.2f, 0), true))
+                    {
+                        ImGui::SeparatorText("Lua scripts:");
+                        for(decltype(m_luaScriptAssets.size()) i = 0, size = m_luaScriptAssets.size(); i < size; i++)
+                        {
+                            if(ImGui::Selectable(m_luaScriptAssets[i].second.c_str(), m_selectedLuaScript != NULL_ENTITY_ID ? m_luaScriptAssets[i].first == m_selectedLuaScript : false))
+                            {
+                                m_selectedLuaScript = m_luaScriptAssets[i].first;
+                            }
+                        }
+                    }
+                    ImGui::EndChild();
+
+                    ImGui::PopStyleVar(4); // ImGuiStyleVar_ChildBorderSize, ImGuiStyleVar_SeparatorTextAlign, ImGuiStyleVar_FramePadding, ImGuiStyleVar_ItemSpacing
+                    ImGui::PopStyleColor(); // ImGuiCol_Border
+
                     ImGui::EndTabItem();
                 }
 
@@ -3120,11 +3243,17 @@ void EditorWindow::update(const float p_deltaTime)
 
                             // If the Lua script filename was changed, send a notification to the LUA Component
                             m_selectedEntity.m_luaScriptFilenameModified = true;
+
+                            // Remember the last opened file browser type
+                            m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
                         }
                         else
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3143,6 +3272,9 @@ void EditorWindow::update(const float p_deltaTime)
                         m_systemScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_SceneReload, EngineStateType::EngineStateType_Editor, m_fileBrowserDialog.m_filename));
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3168,6 +3300,9 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3197,6 +3332,9 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3229,6 +3367,9 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3261,6 +3402,9 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3289,6 +3433,9 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.resetAll();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3320,6 +3467,37 @@ void EditorWindow::update(const float p_deltaTime)
                             ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
                     }
 
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
+                    // Reset the file browser and mark the file browser as not opened
+                    m_fileBrowserDialog.reset();
+                    m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
+                }
+            }
+            break;
+        case EditorWindow::FileBrowserActivated_ShaderFile:
+            {
+                // If the file browser was activated and it is now closed, process the result
+                if(m_fileBrowserDialog.m_closed)
+                {
+                    if(m_fileBrowserDialog.m_success)
+                    {
+                        // Get the current directory path
+                        const std::string currentDirectory = Filesystem::getCurrentDirectory() + "\\" + Config::filepathVar().shader_path;
+
+                        // Check if the selected file is within the current directory
+                        if(m_fileBrowserDialog.m_filePathName.rfind(currentDirectory, 0) == 0)
+                        {
+                            m_selectedShaderFilename = m_fileBrowserDialog.m_filePathName.substr(currentDirectory.size());
+                        }
+                        else
+                            ErrHandlerLoc::get().log(ErrorCode::Editor_path_outside_current_dir, ErrorSource::Source_GUIEditor);
+                    }
+
+                    // Remember the last opened file browser type
+                    m_previouslyOpenedFileBrowser = m_currentlyOpenedFileBrowser;
+
                     // Reset the file browser and mark the file browser as not opened
                     m_fileBrowserDialog.reset();
                     m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
@@ -3547,6 +3725,27 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
 
     ImGui::SeparatorText("Graphics scene settings:");
 
+    // Draw AMBIENT LIGHT INTENSITY
+    drawLeftAlignedLabelText("Ambient light intensity:", inputWidgetOffset);
+    if(ImGui::DragFloat("##AmbientLightIntensityDrag", &p_sceneData.m_ambientIntensity, 0.001f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
+    {
+        m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::AmbientIntensity);
+    }
+    
+    // Draw Z FAR
+    drawLeftAlignedLabelText("Projection Z far:", inputWidgetOffset);
+    if(ImGui::DragFloat("##ZBufferFarDrag", &p_sceneData.m_zFar, 0.1f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
+    {
+        m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::ZFar);
+    }
+
+    // Draw Z NEAR
+    drawLeftAlignedLabelText("Projection Z near:", inputWidgetOffset);
+    if(ImGui::DragFloat("##ZBufferNearDrag", &p_sceneData.m_zNear, 0.0001f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
+    {
+        m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::ZNear);
+    }
+
     // Calculate rendering passes window height and cap it to a max height value
     float renderPassWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y) * (p_sceneData.m_renderingPasses.size() + 2);
     renderPassWindowHeight = renderPassWindowHeight > Config::GUIVar().editor_render_pass_max_height ? Config::GUIVar().editor_render_pass_max_height : renderPassWindowHeight;
@@ -3806,6 +4005,11 @@ void EditorWindow::updateSceneData(SceneData &p_sceneData)
     for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
         p_sceneData.m_volume[i] = audioScene->getVolume(static_cast<AudioBusType>(i));
 
+    // Set graphics data
+    p_sceneData.m_ambientIntensity = graphicsScene->getSceneObjects().m_ambientIntensity;
+    p_sceneData.m_zFar = graphicsScene->getSceneObjects().m_zFar;
+    p_sceneData.m_zNear = graphicsScene->getSceneObjects().m_zNear;
+
     // Add rendering passes
     p_sceneData.m_renderingPasses.clear();
     p_sceneData.m_renderingPasses = graphicsScene->getRenderingPasses();
@@ -4029,7 +4233,10 @@ void EditorWindow::updateComponentList()
 }
 
 void EditorWindow::updateAssetLists()
-{    
+{
+    // Get the entity registry from World Scene
+    auto &entityRegistry = static_cast<WorldScene *>(m_systemScene->getSceneLoader()->getSystemScene(Systems::World))->getEntityRegistry();
+
     //	 ____________________________
     //	|							 |
     //	|	   TEXTURE ASSETS        |
@@ -4050,6 +4257,10 @@ void EditorWindow::updateAssetLists()
             m_textureAssetLongestName = texturePool[i]->getFilename();
     }
 
+    // Sort the texture list based on the texture name
+    std::stable_sort(m_textureAssets.begin(), m_textureAssets.end(),
+        [](const std::pair<const Texture2D *, std::string> &p_a, const std::pair<const Texture2D *, std::string> &p_b) -> bool { return p_a.second < p_b.second; });
+
 
     //	 ____________________________
     //	|							 |
@@ -4071,6 +4282,10 @@ void EditorWindow::updateAssetLists()
             m_modelAssetLongestName = modelPool[i]->getFilename();
     }
 
+    // Sort the model list based on the model name
+    std::stable_sort(m_modelAssets.begin(), m_modelAssets.end(),
+        [](const std::pair<const Model *, std::string> &p_a, const std::pair<const Model *, std::string> &p_b) -> bool { return p_a.second < p_b.second; });
+
 
     //	 ____________________________
     //	|							 |
@@ -4117,6 +4332,32 @@ void EditorWindow::updateAssetLists()
         if(m_shaderAssetLongestName.size() < shaderName.size())
             m_shaderAssetLongestName = shaderName;
     }
+
+    // Sort the shader list based on the shader name
+    std::stable_sort(m_shaderAssets.begin(), m_shaderAssets.end(),
+        [](const std::pair<ShaderLoader::ShaderProgram *, std::string> &p_a, const std::pair<ShaderLoader::ShaderProgram *, std::string> &p_b) -> bool { return p_a.second < p_b.second; });
+    
+
+    //	 ____________________________
+    //	|							 |
+    //	|	  LUA SCRIPT ASSETS      |
+    //	|____________________________|
+    //
+    // Clear lua script asset array
+    m_luaScriptAssets.clear();
+
+    auto luaComponentView = entityRegistry.view<LuaComponent>();
+    for(auto entity : luaComponentView)
+    {
+        auto &luaComponent = luaComponentView.get<LuaComponent>(entity);
+
+        // Add the entity and lua script filename entry
+        m_luaScriptAssets.push_back(std::make_pair(entity, Utilities::stripFilename(luaComponent.getLuaScript()->getLuaScriptFilename())));
+    }
+
+    // Sort the lua script list based on the lua filename
+    std::stable_sort(m_luaScriptAssets.begin(), m_luaScriptAssets.end(),
+        [](const std::pair<EntityID, std::string> &p_a, const std::pair<EntityID, std::string> &p_b) -> bool { return p_a.second < p_b.second; });
 }
 
 void EditorWindow::generateNewMap(PropertySet &p_newSceneProperties, SceneData &p_sceneData)

+ 79 - 33
Praxis3D/Source/EditorWindow.h

@@ -50,10 +50,13 @@ public:
 		m_colorEditFlags = ImGuiColorEditFlags_Float;
 		m_browseButtonWidth = 60.0f;
 		m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
+		m_previouslyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_None;
 		m_fileBrowserDialog.m_name = "EditorFileBrowserDialog";
 
-		m_selectedProgramShader = -1;
-		m_selectedShader = -1;
+		m_selectedLuaScript = NULL_ENTITY_ID;
+		m_selectedModel = nullptr;
+		m_selectedProgram = nullptr;
+		m_selectedShaderType = -1;
 
 		m_nextEntityToSelect = NULL_ENTITY_ID;
 		m_pendingEntityToSelect = false;
@@ -130,26 +133,26 @@ public:
 			switch(p_changedBits)
 			{
 				case Systems::Changes::Graphics::Color:
-				{
-					switch(m_selectedEntity.m_lightType)
 					{
-					case LightComponent::LightComponentType::LightComponentType_directional:
-						return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
-						break;
-					case LightComponent::LightComponentType::LightComponentType_point:
-						return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
-						break;
-					case LightComponent::LightComponentType::LightComponentType_spot:
-						return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
-						break;
+						switch(m_selectedEntity.m_lightType)
+						{
+							case LightComponent::LightComponentType::LightComponentType_directional:
+								return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
+								break;
+							case LightComponent::LightComponentType::LightComponentType_point:
+								return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
+								break;
+							case LightComponent::LightComponentType::LightComponentType_spot:
+								return m_selectedEntity.m_componentData.m_graphicsComponents.m_lightConstructionInfo->m_color;
+								break;
+						}
 					}
-				}
-				break;
+					break;
 				case Systems::Changes::Physics::CollisionShapeSize:
-				{
-					return m_selectedEntity.m_componentData.m_physicsComponents.m_rigidBodyConstructionInfo->m_collisionShapeSize;
-				}
-				break;
+					{
+						return m_selectedEntity.m_componentData.m_physicsComponents.m_rigidBodyConstructionInfo->m_collisionShapeSize;
+					}
+					break;
 				case Systems::Changes::Physics::Gravity:
 					{
 						return m_currentSceneData.m_gravity;
@@ -345,6 +348,24 @@ public:
 				}
 				break;
 
+			case Systems::Changes::Graphics::AmbientIntensity:
+				{
+					return m_currentSceneData.m_ambientIntensity;
+				}
+				break;
+
+			case Systems::Changes::Graphics::ZFar:
+				{
+					return m_currentSceneData.m_zFar;
+				}
+				break;
+
+			case Systems::Changes::Graphics::ZNear:
+				{
+					return m_currentSceneData.m_zNear;
+				}
+				break;
+
 			case Systems::Changes::Physics::Friction:
 				{
 					return m_selectedEntity.m_componentData.m_physicsComponents.m_rigidBodyConstructionInfo->m_friction;
@@ -618,6 +639,15 @@ private:
 	{
 		SceneData()
 		{
+			m_loadInBackground = false;
+			m_modified = true;
+
+			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
+				m_volume[i] = 1.0f;
+
+			m_ambientIntensity = 0.0f;
+			m_zFar = 0.0f;
+			m_zNear = 0.0f;
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Geometry);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Lighting);
@@ -627,21 +657,25 @@ private:
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_GUI);
 
-			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
-				m_volume[i] = 1.0f;
-
 			m_gravity = glm::vec3(0.0f, -9.8f, 0.0f);
-			m_loadInBackground = false;
-			m_modified = true;
 		}
 
-		RenderingPasses m_renderingPasses;
-		std::vector<std::string> m_audioBanks;
+		// General
+		bool m_loadInBackground;
+		bool m_modified;
 
+		// Audio scene
+		std::vector<std::string> m_audioBanks;
 		float m_volume[AudioBusType::AudioBusType_NumOfTypes];
+
+		// Graphics scene
+		float m_ambientIntensity;
+		float m_zFar;
+		float m_zNear;
+		RenderingPasses m_renderingPasses;
+
+		// Physics scene
 		glm::vec3 m_gravity;
-		bool m_loadInBackground;
-		bool m_modified;
 	};
 
 	enum ButtonTextureType : unsigned int
@@ -676,7 +710,8 @@ private:
 		FileBrowserActivated_ModelFile,
 		FileBrowserActivated_TextureFile,
 		FileBrowserActivated_AudioBankFile,
-		FileBrowserActivated_PrefabFile
+		FileBrowserActivated_PrefabFile,
+		FileBrowserActivated_ShaderFile
 	};
 	enum MainMenuButtonType : unsigned int
 	{
@@ -775,6 +810,12 @@ private:
 		return m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x + (m_buttonSizedByFont.x + m_imguiStyle.FramePadding.x * 3) * p_buttonIndex;
 	}
 
+	int getShaderAssetIndex(const ShaderLoader::ShaderProgram *p_selectedProgram) const
+	{
+		for(decltype(m_shaderAssets.size()) shaderAssetIndex = 0, shaderAssetSize = m_shaderAssets.size(); shaderAssetIndex < shaderAssetSize; shaderAssetIndex++)
+			if(m_shaderAssets[shaderAssetIndex].first->getCombinedFilename() == p_selectedProgram->getCombinedFilename())
+				return (int)shaderAssetIndex;
+	}
 	void clearEntityAndComponentPool()
 	{
 		if(!m_entityAndComponentPool.empty())
@@ -930,6 +971,7 @@ private:
 	ImGuiColorEditFlags m_colorEditFlags;
 	float m_browseButtonWidth;
 	FileBrowserActivated m_currentlyOpenedFileBrowser;
+	FileBrowserActivated m_previouslyOpenedFileBrowser;
 	FileBrowserDialog m_fileBrowserDialog;
 	const ImVec2 m_playPauseButtonSize;
 	ImVec2 m_assetSelectionPopupImageSize;
@@ -943,13 +985,17 @@ private:
 
 	// Assets variables
 	std::vector<std::pair<const Texture2D *, std::string>> m_textureAssets;
-	std::vector<std::pair<const Model *, std::string>> m_modelAssets;
-	std::vector<std::pair<const ShaderLoader::ShaderProgram *, std::string>> m_shaderAssets;
+	std::vector<std::pair<Model *, std::string>> m_modelAssets;
+	std::vector<std::pair<ShaderLoader::ShaderProgram *, std::string>> m_shaderAssets;
+	std::vector<std::pair<EntityID, std::string>> m_luaScriptAssets;
 	std::string m_textureAssetLongestName;
 	std::string m_modelAssetLongestName;
 	std::string m_shaderAssetLongestName;
-	int m_selectedProgramShader;
-	int m_selectedShader;
+	std::string m_selectedShaderFilename;
+	EntityID m_selectedLuaScript;
+	Model *m_selectedModel;
+	ShaderLoader::ShaderProgram *m_selectedProgram;
+	int m_selectedShaderType;
 
 	// Texture inspector variables
 	Texture2D const * m_selectedTexture;

+ 27 - 3
Praxis3D/Source/GUIScene.cpp

@@ -16,6 +16,8 @@ GUIScene::GUIScene(SystemBase *p_system, SceneLoader *p_sceneLoader) : SystemSce
 	 m_GUITask = nullptr;
 	 m_editorWindow = nullptr;
 	 m_GUISequenceEnabled = true;
+	 m_showAboutWindow = false;
+	 m_showSettingsWindow = false;
 }
 
 GUIScene::~GUIScene()
@@ -169,6 +171,10 @@ void GUIScene::update(const float p_deltaTime)
 						browserDialog->m_filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
 						browserDialog->m_filename = ImGuiFileDialog::Instance()->GetCurrentFileName();
 						browserDialog->m_success = true;
+
+						// Save the last root path, so that the next time the browser dialog is opened, it is set to the last opened directory
+						if(browserDialog->m_saveLastRootPath)
+							browserDialog->m_rootPath = browserDialog->m_filePath;
 					}
 
 					// Close the dialog and remove it from the queue
@@ -191,6 +197,12 @@ void GUIScene::update(const float p_deltaTime)
 			m_editorWindow->update(p_deltaTime);
 		}
 
+		//	 ____________________________
+		//	|							 |
+		//	|		ABOUT WINDOW		 |
+		//	|____________________________|
+		//
+
 		//	 ____________________________
 		//	|							 |
 		//	|	GUI SEQUENCE COMPONENTS	 |
@@ -361,6 +373,12 @@ void GUIScene::receiveData(const DataType p_dataType, void *p_data, const bool p
 {
 	switch(p_dataType)
 	{
+		case DataType::DataType_AboutWindow:
+			{
+
+			}
+			break;
+
 		case DataType::DataType_DeleteComponent:
 			{
 				// Get the world scene required for getting the entity registry and deleting components
@@ -391,20 +409,20 @@ void GUIScene::receiveData(const DataType p_dataType, void *p_data, const bool p
 					delete componentData;
 			}
 			break;
-		case DataType_EnableGUISequence:
+		case DataType::DataType_EnableGUISequence:
 			{
 				setGUISequenceEnabled(static_cast<bool>(p_data));
 			}
 			break;
 
-		case DataType_FileBrowserDialog:
+		case DataType::DataType_FileBrowserDialog:
 			{
 				// Cast the sent data into the intended type and add it to the file-browser dialog queue
 				m_fileBrowserDialogs.push(static_cast<FileBrowserDialog *>(p_data));
 			}
 			break;
 
-		case DataType_EditorWindow:
+		case DataType::DataType_EditorWindow:
 			{
 				// Cast the sent data into the intended type
 				EditorWindowSettings *editorWindowSettings = static_cast<EditorWindowSettings *>(p_data);
@@ -441,6 +459,12 @@ void GUIScene::receiveData(const DataType p_dataType, void *p_data, const bool p
 			}
 			break;
 
+		case DataType::DataType_SettingsWindow:
+			{
+
+			}
+			break;
+
 		default:
 			assert(p_deleteAfterReceiving == true && "Memory leak - unhandled orphaned void data pointer in receiveData");
 			break;

+ 7 - 1
Praxis3D/Source/GUIScene.h

@@ -47,6 +47,7 @@ struct FileBrowserDialog
 		m_opened = false;
 		m_closed = false;
 		m_success = false;
+		m_saveLastRootPath = true;
 		m_rootPath = ".";
 		m_numOfSelectableFiles = 1;
 		m_flags = FileBrowserDialogFlags_None;
@@ -85,6 +86,7 @@ struct FileBrowserDialog
 		m_opened = false;
 		m_closed = false;
 		m_success = false;
+		m_saveLastRootPath = true;
 	}
 
 	std::string m_name,
@@ -103,7 +105,8 @@ struct FileBrowserDialog
 
 	bool m_opened,
 		 m_closed,
-		 m_success;
+		 m_success,
+		 m_saveLastRootPath;
 };
 
 class GUIScene : public SystemScene
@@ -175,4 +178,7 @@ private:
 	std::queue<FileBrowserDialog*> m_fileBrowserDialogs;
 	EditorWindow *m_editorWindow;
 	bool m_GUISequenceEnabled;
+
+	bool m_showAboutWindow;
+	bool m_showSettingsWindow;
 };

+ 3 - 1
Praxis3D/Source/LuaScript.cpp

@@ -283,6 +283,7 @@ void LuaScript::setFunctions()
 	// Engine functions
 	m_luaState.set_function("setEngineRunning", [](const bool p_v1) -> const void {Config::m_engineVar.running = p_v1; });
 	m_luaState.set_function("setEngineState", [this](const EngineStateType p_v1) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(EngineChangeType::EngineChangeType_StateChange, p_v1)); });
+	m_luaState.set_function("getEngineState", [this]() -> EngineStateType { return Config::engineVar().engineState; });
 
 	m_luaState.set_function("sendEngineChange", sol::overload([this](const EngineChangeType p_v1) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(p_v1)); },
 		[this](const EngineChangeType p_v1, const EngineStateType p_v2) -> const void { m_scriptScene->getSceneLoader()->getChangeController()->sendEngineChange(EngineChangeData(p_v1, p_v2)); },
@@ -370,7 +371,8 @@ void LuaScript::setUsertypes()
 		"gl_context_minor_version", &Config::EngineVariables::gl_context_minor_version,
 		"object_directory_init_pool_size", &Config::EngineVariables::object_directory_init_pool_size,
 		"smoothing_tick_samples", &Config::EngineVariables::smoothing_tick_samples,
-		"running", &Config::EngineVariables::running);
+		"running", &Config::EngineVariables::running,
+		"engineState", &Config::EngineVariables::engineState);
 
 	m_luaState.new_usertype<Config::GameplayVariables>("GameplayVariables",
 		"camera_freelook_speed", &Config::GameplayVariables::camera_freelook_speed);

+ 7 - 5
Praxis3D/Source/RendererBackend.h

@@ -655,7 +655,7 @@ protected:
 
 			case LoadObject_Shader:
 				{
-					unsigned int shaderHandles[ShaderType_NumOfTypes] = {};
+					unsigned int shaderHandles[ShaderType_NumOfTypes] = { 0 };
 					unsigned int shaderTypes[ShaderType_NumOfTypes] = {
 						GL_COMPUTE_SHADER,
 						GL_FRAGMENT_SHADER,
@@ -668,7 +668,8 @@ protected:
 					glGetError();
 
 					// Create shader program handle
-					p_command.m_handle = glCreateProgram();
+					if(p_command.m_handle == 0)
+						p_command.m_handle = glCreateProgram();
 
 					// Check for errors
 					GLenum glError = glGetError();
@@ -742,6 +743,7 @@ protected:
 
 									// Check for errors
 									glError = glGetError();
+
 									// If compilation failed
 									if(shaderCompileResult == 0)
 									{
@@ -778,6 +780,7 @@ protected:
 
 										// Check for errors
 										glError = glGetError();
+
 										if(glError != GL_NO_ERROR)
 										{
 											// Reset the shader handle
@@ -807,14 +810,12 @@ protected:
 						// If shader loading was successful
 						if(shaderLinkingResult)
 						{
-
 							glUseProgram(p_command.m_handle);
 
 							// Generate uniform update list, after the shader has been compiled
 							p_command.m_objectData.m_shaderData.m_uniformUpdater.generateUpdateList();
 
 							// Update some of the uniforms (that do not change frame to frame)
-							//p_command.m_objectData.m_shaderData.m_uniformUpdater.generateUpdateList();
 							p_command.m_objectData.m_shaderData.m_uniformUpdater.updateTextureUniforms();
 							p_command.m_objectData.m_shaderData.m_uniformUpdater.updateBlockBindingPoints();
 							p_command.m_objectData.m_shaderData.m_uniformUpdater.updateSSBBindingPoints();
@@ -852,13 +853,14 @@ protected:
 							ErrHandlerLoc::get().log(ErrorCode::Shader_link_failed, ErrorSource::Source_ShaderLoader, errorMessageTemp);
 						}
 
-						// Iterate over all shaders and detach them
+						// Iterate over all shaders, detach and delete them
 						for(unsigned int i = 0; i < ShaderType_NumOfTypes; i++)
 						{
 							// If shader is valid
 							if(shaderHandles[i] != 0)
 							{
 								glDetachShader(p_command.m_handle, shaderHandles[i]);
+								glDeleteShader(shaderHandles[i]);
 							}
 						}
 					}

+ 6 - 3
Praxis3D/Source/RendererFrontend.cpp

@@ -330,10 +330,10 @@ void RendererFrontend::renderFrame(SceneObjects &p_sceneObjects, const float p_d
 	}
 
 	// If Z-buffer near or far values have changed, flag projection matrix for updating
-	if(m_zFar != Config::graphicsVar().z_far || m_zNear != Config::graphicsVar().z_near)
+	if(m_zFar != p_sceneObjects.m_zFar || m_zNear != p_sceneObjects.m_zNear)
 	{
-		m_zFar = Config::graphicsVar().z_far;
-		m_zNear = Config::graphicsVar().z_near;
+		m_zFar = p_sceneObjects.m_zFar;
+		m_zNear = p_sceneObjects.m_zNear;
 		projectionMatrixNeedsUpdating = true;
 	}
 
@@ -454,6 +454,9 @@ void RendererFrontend::renderFrame(SceneObjects &p_sceneObjects, const float p_d
 	// Set the camera target vector
 	m_frameData.m_cameraTarget = normalize(glm::vec3(0.0f, 0.0f, -1.0f) * glm::mat3(p_sceneObjects.m_cameraViewMatrix));
 	
+	// Set ambient intensity multiplier
+	m_frameData.m_ambientIntensity = p_sceneObjects.m_ambientIntensity;
+
 	// Prepare the geometry buffer for a new frame and a geometry pass
 	m_backend.getGeometryBuffer()->initFrame();
 	

+ 0 - 4
Praxis3D/Source/RendererFrontend.h

@@ -372,8 +372,4 @@ protected:
 	// An array of all active rendering passes
 	std::vector<RenderPass*> m_activeRenderPasses;
 	RenderPass* m_allRenderPasses[RenderPassType::RenderPassType_NumOfTypes];
-
-	//RenderingPasses m_renderingPassesTypes;
-	//RenderPass *m_initializedRenderingPasses[RenderPassType::RenderPassType_NumOfTypes];
-	//bool m_renderPassBeingUsed[RenderPassType::RenderPassType_NumOfTypes];
 };

+ 51 - 2
Praxis3D/Source/RendererScene.cpp

@@ -56,6 +56,23 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 	worldScene->reserve<GraphicsLoadToMemoryComponent>(modelComponentPoolSize + shaderComponentPoolSize);
 	worldScene->reserve<GraphicsLoadToVideoMemoryComponent>(modelComponentPoolSize + shaderComponentPoolSize);
 
+	// Load graphics property data
+	for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+	{
+		switch(p_properties[i].getPropertyID())
+		{
+			case Properties::AmbientIntensity:
+				m_sceneObjects.m_ambientIntensity = p_properties[i].getFloat();
+				break;
+			case Properties::ZFar:
+				m_sceneObjects.m_zFar = p_properties[i].getFloat();
+				break;
+			case Properties::ZNear:
+				m_sceneObjects.m_zNear = p_properties[i].getFloat();
+				break;
+		}
+	}
+
 	// Load the rendering passes
 	auto &renderPassesProperty = p_properties.getPropertySetByID(Properties::RenderPasses);
 	if(renderPassesProperty)
@@ -102,6 +119,11 @@ void RendererScene::exportSetup(PropertySet &p_propertySet)
 	// Get the world scene required for getting the pool sizes
 	WorldScene *worldScene = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World));
 
+	// Add graphics data
+	p_propertySet.addProperty(Properties::AmbientIntensity, m_sceneObjects.m_ambientIntensity);
+	p_propertySet.addProperty(Properties::ZFar, m_sceneObjects.m_zFar);
+	p_propertySet.addProperty(Properties::ZNear, m_sceneObjects.m_zNear);
+
 	// Add object pool sizes
 	auto &objectPoolSizePropertySet = p_propertySet.addPropertySet(Properties::ObjectPoolSize);
 	objectPoolSizePropertySet.addProperty(Properties::CameraComponent, (int)worldScene->getPoolSize<CameraComponent>());
@@ -841,7 +863,20 @@ ErrorCode RendererScene::destroyObject(SystemObject *p_systemObject)
 
 void RendererScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
 {
+	if(CheckBitmask(p_changeType, Systems::Changes::Graphics::AmbientIntensity))
+	{
+		m_sceneObjects.m_ambientIntensity = p_subject->getFloat(this, Systems::Changes::Graphics::AmbientIntensity);
+	}
 
+	if(CheckBitmask(p_changeType, Systems::Changes::Graphics::ZFar))
+	{
+		m_sceneObjects.m_zFar = p_subject->getFloat(this, Systems::Changes::Graphics::ZFar);
+	}
+
+	if(CheckBitmask(p_changeType, Systems::Changes::Graphics::ZNear))
+	{
+		m_sceneObjects.m_zNear = p_subject->getFloat(this, Systems::Changes::Graphics::ZNear);
+	}
 }
 
 void RendererScene::receiveData(const DataType p_dataType, void *p_data, const bool p_deleteAfterReceiving)
@@ -931,8 +966,10 @@ void RendererScene::receiveData(const DataType p_dataType, void *p_data, const b
 			break;
 
 		case DataType::DataType_RenderToTexture:
-			m_renderToTexture = static_cast<bool>(p_data);
-			m_renderTask->m_renderer.setRenderFinalToTexture(m_renderToTexture);
+			{
+				m_renderToTexture = static_cast<bool>(p_data);
+				m_renderTask->m_renderer.setRenderFinalToTexture(m_renderToTexture);
+			}
 			break;
 
 		case DataType::DataType_RenderToTextureResolution:
@@ -947,6 +984,18 @@ void RendererScene::receiveData(const DataType p_dataType, void *p_data, const b
 			}
 			break;
 
+		case DataType::DataType_LoadShader:
+			{
+				ShaderLoader::ShaderProgram *shaderProgram = static_cast<ShaderLoader::ShaderProgram *>(p_data);
+
+				if(shaderProgram != nullptr)
+				{
+					shaderProgram->setLoadedToVideoMemory(false);
+					m_sceneObjects.m_loadToVideoMemory.emplace_back(shaderProgram);
+				}
+			}
+			break;
+
 		case DataType::DataType_LoadTexture2D:
 			{
 				TextureLoader2D::Texture2DHandle *textureHandle = static_cast<TextureLoader2D::Texture2DHandle *>(p_data);

+ 9 - 1
Praxis3D/Source/RendererScene.h

@@ -84,7 +84,7 @@ struct LoadableComponentContainer
 // Used to store processed objects, so they can be sent to the renderer
 struct SceneObjects
 {
-	SceneObjects() : m_processDrawing(true) { }
+	SceneObjects() : m_processDrawing(true), m_ambientIntensity(Config::graphicsVar().ambient_light_intensity), m_zFar(Config::graphicsVar().z_far), m_zNear(Config::graphicsVar().z_near) { }
 
 	// ECS registry views
 	decltype(std::declval<entt::basic_registry<EntityID>>().view<ModelComponent, SpatialComponent>(entt::exclude<ShaderComponent, GraphicsLoadToMemoryComponent, GraphicsLoadToVideoMemoryComponent>)) m_models;
@@ -103,6 +103,13 @@ struct SceneObjects
 
 	// Should the scene be drawn (otherwise only load commands are processed)
 	bool m_processDrawing;
+
+	// Ambient light intensity multiplier
+	float m_ambientIntensity;
+
+	// Z-buffer limits
+	float m_zFar;
+	float m_zNear;
 };
 
 class RendererScene : public SystemScene
@@ -223,6 +230,7 @@ public:
 	const unsigned int getUnsignedInt(const Observer *p_observer, BitMask p_changedBits) const;
 	SystemTask *getSystemTask() { return m_renderTask; }
 	Systems::TypeID getSystemType() { return Systems::Graphics; }
+	const inline SceneObjects &getSceneObjects() const { return m_sceneObjects; }
 	inline SceneObjects &getSceneObjects() { return m_sceneObjects; }
 	const inline RenderingPasses &getRenderingPasses() const { return m_renderingPasses; }
 	const glm::mat4 &getViewMatrix() const;

+ 1 - 1
Praxis3D/Source/SceneLoader.cpp

@@ -777,7 +777,7 @@ void SceneLoader::importFromProperties(GUIComponentsConstructionInfo &p_construc
 	{
 		switch(p_properties.getPropertyID())
 		{
-			case Properties::PropertyID::Sequence:
+			case Properties::PropertyID::GUISequenceComponent:
 			{
 				if(p_constructionInfo.m_guiSequenceConstructionInfo == nullptr)
 					p_constructionInfo.m_guiSequenceConstructionInfo = new GUISequenceComponent::GUISequenceComponentConstructionInfo();

+ 0 - 1
Praxis3D/Source/SceneLoader.h

@@ -81,7 +81,6 @@ private:
 	void exportToProperties(const ScriptComponentsConstructionInfo &p_constructionInfo, PropertySet &p_properties);
 	void exportToProperties(const WorldComponentsConstructionInfo &p_constructionInfo, PropertySet &p_properties);
 
-
 	// Mutex used to block calls from other threads while import operation is in progress
 	SpinWait m_mutex;
 

+ 5 - 0
Praxis3D/Source/ShaderLoader.cpp

@@ -147,6 +147,11 @@ ShaderLoader::ShaderProgram *ShaderLoader::load(const PropertySet &p_properties)
 	return &m_defaultProgram;
 }
 
+ErrorCode ShaderLoader::reload(ShaderProgram *p_shaderProgram)
+{
+	return ErrorCode();
+}
+
 ShaderLoader::ShaderProgram::~ShaderProgram()
 {
 	delete m_uniformUpdater;

+ 18 - 0
Praxis3D/Source/ShaderLoader.h

@@ -146,6 +146,7 @@ public:
 		friend class CommandBuffer;
 		friend class ShaderLoader;
 		friend class RendererFrontend;
+		friend class RendererScene;
 
 	public:
 		inline void addShader(ShaderType p_shaderType, const std::string &p_filename)
@@ -168,6 +169,9 @@ public:
 				{
 					if(!m_shaderFilename[i].empty())
 					{
+						// Make sure the shader source string is empty
+						m_shaderSource[i].clear();
+
 						// Load shader's source code from a file
 						std::ifstream sourceStream(Config::PathsVariables().shader_path + m_shaderFilename[i], std::ios::in);
 
@@ -197,6 +201,15 @@ public:
 			return returnError;
 		}
 
+		inline ErrorCode reloadToMemory()
+		{
+			// Reset the loaded flag
+			m_loadedToMemory = false;
+
+			// Load shader to memory
+			return loadToMemory();
+		}
+
 		// Returns true if the program contains any tessellation shaders
 		const inline bool isTessellated() const { return m_tessellated; }
 
@@ -208,6 +221,9 @@ public:
 		const inline bool isDefaultProgram() const		{ return m_defaultShader;		}
 		const inline bool isLoadedToVideoMemory() const { return m_loadedToVideoMemory; }
 		
+		// Setters
+		inline void setShaderFilename(const ShaderType p_shaderType, const std::string &p_filename) { m_shaderFilename[p_shaderType] = p_filename; }
+
 		// Comparator operators
 		const inline bool operator==(const std::string &p_filename) const { return (m_combinedFilename == p_filename); }
 		const inline bool operator==(const unsigned int p_programHandle) const { return (m_programHandle == p_programHandle); }
@@ -285,6 +301,8 @@ public:
 	inline ShaderProgram *load() { return &m_defaultProgram; }
 	ShaderProgram *load(const PropertySet &p_properties);
 
+	ErrorCode reload(ShaderProgram *p_shaderProgram);
+
 	const inline std::vector<ShaderProgram *> &getObjectPool() const { return m_shaderPrograms; }
 
 private:

+ 6 - 0
Praxis3D/Source/ShaderUniformUpdater.cpp

@@ -11,6 +11,9 @@ ErrorCode ShaderUniformUpdater::generateUpdateList()
 {
 	ErrorCode returnError = ErrorCode::Success;
 
+	// Make sure the update lists are empty
+	clearUpdateList();
+
 	// Make sure shader handle is up to date
 	m_shaderHandle = m_shader.getShaderHandle();
 
@@ -118,6 +121,9 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new FogDensityUniform(m_shaderHandle));
 	uniformList.push_back(new FogColorUniform(m_shaderHandle));
 
+	// Ambient light
+	uniformList.push_back(new AmbientLightIntensityUniform(m_shaderHandle));
+
 	// Directional light
 	uniformList.push_back(new DirLightColorUniform(m_shaderHandle));
 	uniformList.push_back(new DirLightDirectionUniform(m_shaderHandle));

+ 26 - 0
Praxis3D/Source/ShaderUniformUpdater.h

@@ -42,6 +42,32 @@ public:
 	inline size_t getNumSSBBufferBlocks() const	{ return m_numSSBBBlockUpdates;		}
 
 private:
+	// Clears all the internal arrays that are populated by generateUpdateList()
+	const inline void clearUpdateList()
+	{
+		// Delete all uniforms
+		for(auto *uniform : m_updatesPerFrame)
+			delete uniform;
+		for(auto *uniform : m_updatesPerModel)
+			delete uniform;
+		for(auto *uniform : m_updatesPerMesh)
+			delete uniform;
+		for(auto *uniform : m_textureUpdates)
+			delete uniform;
+		for(auto *uniform : m_uniformBlockUpdates)
+			delete uniform;
+		for(auto *uniform : m_SSBBlockUpdates)
+			delete uniform;
+
+		// Clear arrays
+		m_updatesPerFrame.clear();
+		m_updatesPerModel.clear();
+		m_updatesPerMesh.clear();
+		m_textureUpdates.clear();
+		m_uniformBlockUpdates.clear();
+		m_SSBBlockUpdates.clear();
+	}
+
 	// Checks which uniforms are used in the shader, and generates a list of valid ones
 	ErrorCode generateUpdateList();
 

+ 20 - 0
Praxis3D/Source/ShaderUniforms.h

@@ -571,6 +571,26 @@ private:
 	float m_bloomIntensity;
 };
 
+class AmbientLightIntensityUniform : public BaseUniform
+{
+public:
+	AmbientLightIntensityUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().ambientLightIntensity, p_shaderHandle), m_intensity(-1.0f)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_intensity != p_uniformData.m_frameData.m_ambientIntensity)
+		{
+			m_intensity = p_uniformData.m_frameData.m_ambientIntensity;
+
+			glUniform1f(m_uniformHandle, m_intensity);
+		}
+	}
+
+private:
+	float m_intensity;
+};
 class DirLightColorUniform : public BaseUniform
 {
 public:

+ 3 - 1
Praxis3D/Source/UniformData.h

@@ -7,6 +7,7 @@ struct UniformFrameData
 {
 	UniformFrameData()
 	{
+		m_ambientIntensity = Config::graphicsVar().ambient_light_intensity;
 		m_numPointLights = 0;
 		m_numSpotLights = 0;
 		m_deltaTime = 0.0f;
@@ -31,9 +32,10 @@ struct UniformFrameData
 
 	// Parameters of directional light, since there can be only one of it
 	DirectionalLightDataSet m_directionalLight;
+	float m_ambientIntensity;
 
 	// Delta time of the last frame
-	float		m_deltaTime;
+	float m_deltaTime;
 
 	// Current number of lights in the light buffers
 	unsigned int m_numPointLights,

+ 2 - 2
Praxis3D/Source/Version.h

@@ -3,11 +3,11 @@
 #define VERSION_TO_STRING_HELPER(v) #v
 #define VERSION_TO_STRING(v) VERSION_TO_STRING_HELPER(v)
 
-#define PRAXIS3D_COMMIT_DATE 2023-12-13
+#define PRAXIS3D_COMMIT_DATE 2023-12-16
 
 #define PRAXIS3D_VERSION_MAJOR 0
 #define PRAXIS3D_VERSION_MINOR 1
-#define PRAXIS3D_VERSION_PATCH 1
+#define PRAXIS3D_VERSION_PATCH 2
 
 #define PRAXIS3D_VERSION ((PRAXIS3D_VERSION_MAJOR << 16) | (PRAXIS3D_VERSION_MINOR << 8) | PRAXIS3D_VERSION_PATCH)
 #define PRAXIS3D_VERSION_STRING VERSION_TO_STRING(PRAXIS3D_VERSION_MAJOR) "." VERSION_TO_STRING(PRAXIS3D_VERSION_MINOR) "." VERSION_TO_STRING(PRAXIS3D_VERSION_PATCH)

+ 80 - 7
Praxis3D/imgui.ini

@@ -85,7 +85,7 @@ Collapsed=0
 
 [Window][##LeftWindow]
 Pos=0,69
-Size=301,1077
+Size=301,1057
 Collapsed=0
 DockId=0x00000001,0
 
@@ -96,14 +96,14 @@ Collapsed=0
 DockId=0x00000008,0
 
 [Window][##BottomWindow]
-Pos=0,1148
-Size=1824,230
+Pos=0,1128
+Size=1824,250
 Collapsed=0
 DockId=0x00000006,0
 
 [Window][##CenterWindow]
 Pos=303,69
-Size=1521,1077
+Size=1521,1057
 Collapsed=0
 DockId=0x00000004,0
 
@@ -223,10 +223,15 @@ Size=801,605
 Collapsed=0
 
 [Window][##LoadingStatusWindow]
-Pos=1031,584
+Pos=1031,574
 Size=64,70
 Collapsed=0
 
+[Window][Open a shader file##OpenShaderFileFileDialog]
+Pos=515,262
+Size=920,703
+Collapsed=0
+
 [Table][0x9A4BDFDE,4]
 RefScale=13
 Column 0  Sort=0v
@@ -1551,14 +1556,82 @@ Column 0  Sort=0v
 RefScale=13
 Column 0  Sort=0v
 
+[Table][0x705A6537,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0953CC40,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF5F8AB3D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8B14BA51,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA87F0417,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x529C32FA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5985D455,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0C8FB3E2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x7A2A2530,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x182906FC,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x22CD9D57,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBE8F3EB8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6D2CAB57,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x79BB349F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0D21AEC3,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x75FA07FE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x9CBFCE48,4]
+RefScale=13
+Column 0  Sort=0v
+
 [Docking][Data]
 DockSpace         ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,69 Size=2335,1309 Split=X
   DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1409,1011 Split=Y
-    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,779 Split=X
+    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,1057 Split=X
       DockNode    ID=0x00000001 Parent=0x00000005 SizeRef=301,1011 Selected=0xB1B21E90
       DockNode    ID=0x00000003 Parent=0x00000005 SizeRef=1106,1011 Split=X
         DockNode  ID=0x00000002 Parent=0x00000003 SizeRef=332,1042 Selected=0xD00C9CD4
         DockNode  ID=0x00000004 Parent=0x00000003 SizeRef=1586,1042 CentralNode=1 Selected=0xFB1B6F35
-    DockNode      ID=0x00000006 Parent=0x00000007 SizeRef=1920,230 Selected=0x51C0BD29
+    DockNode      ID=0x00000006 Parent=0x00000007 SizeRef=1920,250 Selected=0x51C0BD29
   DockNode        ID=0x00000008 Parent=0x8B93E3BD SizeRef=509,1011 Selected=0x96D2BCA2