Browse Source

Rendering optimizations; fixed bloom order; added individual face-cull settings

Added an optimization to reduce the amount of texture binds, by setting the object type in a draw command, in RendererBackend
Added an optimization to reduce material buffer updates, by checking for data changes, in RendererBackend
Added an optimization to reduce face culling calls, by remembering the last used state, in RendererBackend
Added face culling settings for drawing and shadowmapping for each individual model, in RendererBackend, GraphicsDataSets, GeometryPass, ShadowMappingPass, ModelComponent, SceneLoader, EditorWindow
Added error checking, logging and capturing for Lua scripts, in LuaScript
Added a separate exposure adaptation pass, that is performed before the bloom pass, instead of after it, in BloomPass, LuminancePass
Added a separate tonemapping rendering pass, in TonemappingPass, RendererScene, RendererFrontend
Added an alternative CSM pass of rendering each layer separately, without a geometry shader, in ShadowMappingPass
Added color pallets for GUI elements, in CommonDefinitions, GUISystem, EditorWindow
Added right-mouse-click options window for entity hierarchy list, in EditorWindow
Added a way to export an entity as a prefab, in EditorWindow
Added a way to duplicate an entity, in EditorWindow
Added a way to enable and disable specific components of all children of an entity, in EditorWindow
Added colored console output window, in EditorWindow, ErrorHandler
Added invisible child windows to all editor windows, so that the tabs stay in place, when scrolling down, in EditorWindow
Added engine versioning to map file, in SceneLoader
Added an option for centering the engine window upon startup, in Window, Config
Added more error codes and error strings
Added more config variables
Fixed a bug of spin-wait mutex overflowing a buffer when initializing a critical section, in SpinWait
Fixed a bug of the text editor leaving char termination characters at the end of the file, when saving, in ImGuiColorTextEdit
Fixed a bug of spawning too many impact sound events and causing lag, in AudioScene
Fixed a bug of external Lua variable definitions not being sent to a LuaComponent, in EditorWindow
Fixed a bug of issuing too many errors for reaching a limit on static/dynamic collisions, in PhysicsScene
Updated engine version to v0.2.5
Paul A 1 year ago
parent
commit
9f121ab3ae
79 changed files with 5809 additions and 3160 deletions
  1. 3 1
      .gitignore
  2. 3 0
      Dependencies/include/ImGuiColorTextEdit/TextEditor.cpp
  3. 299 27
      Praxis3D/Data/Maps/default.pmap
  4. BIN
      Praxis3D/Data/Materials/Default/GUI/logo3.png
  5. 19 0
      Praxis3D/Data/Scripts/Constant_rotation_two_axes.lua
  6. 1 0
      Praxis3D/Data/Scripts/Example_map_startup_window.lua
  7. 16 0
      Praxis3D/Data/Scripts/MainMenu_image_buttons.lua
  8. 1 1
      Praxis3D/Data/Shaders/atmosphericScatteringPass_sky.frag
  9. 5 0
      Praxis3D/Data/Shaders/bloomDownscale.comp
  10. 4 0
      Praxis3D/Data/Shaders/bloomUpscale.comp
  11. 0 0
      Praxis3D/Data/Shaders/csmPassLayered.frag
  12. 0 1
      Praxis3D/Data/Shaders/csmPassLayered.geom
  13. 0 1
      Praxis3D/Data/Shaders/csmPassLayered.vert
  14. 43 0
      Praxis3D/Data/Shaders/csmPassSingle.frag
  15. 42 0
      Praxis3D/Data/Shaders/csmPassSingle.vert
  16. 111 0
      Praxis3D/Data/Shaders/exposureAdaptation.frag
  17. 14 0
      Praxis3D/Data/Shaders/exposureAdaptation.vert
  18. 51 45
      Praxis3D/Data/Shaders/finalPass.frag
  19. 5 1
      Praxis3D/Data/Shaders/finalPass.vert
  20. 19 7
      Praxis3D/Data/Shaders/geometryPass.frag
  21. 16 4
      Praxis3D/Data/Shaders/geometryPass.vert
  22. 204 39
      Praxis3D/Data/Shaders/lightPass.frag
  23. 8 12
      Praxis3D/Data/Shaders/lightPass.vert
  24. 0 477
      Praxis3D/Data/Shaders/lightPass_CSM.frag
  25. 4 0
      Praxis3D/Data/Shaders/luminanceAverage.comp
  26. 4 0
      Praxis3D/Data/Shaders/luminanceHistogram.comp
  27. 30 0
      Praxis3D/Data/Shaders/passthrough.frag
  28. 6 14
      Praxis3D/Data/Shaders/passthrough.vert
  29. 90 99
      Praxis3D/Data/Shaders/tonemapping.frag
  30. 4 0
      Praxis3D/Data/Shaders/tonemapping.vert
  31. 9 6
      Praxis3D/Data/config.ini
  32. 3 0
      Praxis3D/Data/error-strings-eng.data
  33. 3 1
      Praxis3D/Praxis3D.vcxproj
  34. 6 0
      Praxis3D/Praxis3D.vcxproj.filters
  35. 36 27
      Praxis3D/Source/AudioScene.cpp
  36. 8 1
      Praxis3D/Source/BloomPass.h
  37. 1 3
      Praxis3D/Source/CSMFramebuffer.h
  38. 13 0
      Praxis3D/Source/CommonDefinitions.h
  39. 16 6
      Praxis3D/Source/Config.cpp
  40. 47 20
      Praxis3D/Source/Config.h
  41. 183 114
      Praxis3D/Source/Containers.h
  42. 10 10
      Praxis3D/Source/DeferredRenderer.cpp
  43. 2161 1907
      Praxis3D/Source/EditorWindow.cpp
  44. 153 2
      Praxis3D/Source/EditorWindow.h
  45. 6 0
      Praxis3D/Source/Engine.cpp
  46. 5 2
      Praxis3D/Source/EngineDefinitions.h
  47. 4 0
      Praxis3D/Source/ErrorCodes.h
  48. 46 37
      Praxis3D/Source/ErrorHandler.cpp
  49. 66 5
      Praxis3D/Source/ErrorHandler.h
  50. 10 8
      Praxis3D/Source/FinalPass.h
  51. 74 0
      Praxis3D/Source/GUISystem.cpp
  52. 12 0
      Praxis3D/Source/GUISystem.h
  53. 51 12
      Praxis3D/Source/GeometryPass.h
  54. 28 0
      Praxis3D/Source/GraphicsDataSets.h
  55. 7 1
      Praxis3D/Source/LuaScript.cpp
  56. 76 21
      Praxis3D/Source/LuaScript.h
  57. 49 15
      Praxis3D/Source/LuminancePass.h
  58. 27 65
      Praxis3D/Source/ModelComponent.h
  59. 19 5
      Praxis3D/Source/PhysicsScene.cpp
  60. 8 0
      Praxis3D/Source/PhysicsScene.h
  61. 1 1
      Praxis3D/Source/PropertySet.h
  62. 87 31
      Praxis3D/Source/RendererBackend.cpp
  63. 51 0
      Praxis3D/Source/RendererBackend.h
  64. 59 53
      Praxis3D/Source/RendererFrontend.cpp
  65. 6 3
      Praxis3D/Source/RendererFrontend.h
  66. 20 0
      Praxis3D/Source/RendererScene.cpp
  67. 3 0
      Praxis3D/Source/RendererScene.h
  68. 105 14
      Praxis3D/Source/SceneLoader.cpp
  69. 6 0
      Praxis3D/Source/SceneLoader.h
  70. 36 9
      Praxis3D/Source/ShaderUniforms.h
  71. 106 13
      Praxis3D/Source/ShadowMappingPass.h
  72. 8 6
      Praxis3D/Source/SpinWait.cpp
  73. 4 1
      Praxis3D/Source/SpinWait.h
  74. 3 3
      Praxis3D/Source/TaskManager.h
  75. 108 0
      Praxis3D/Source/TonemappingPass.h
  76. 2 2
      Praxis3D/Source/Version.h
  77. 11 3
      Praxis3D/Source/Window.cpp
  78. 4 0
      Praxis3D/Source/WorldScene.h
  79. 1050 24
      Praxis3D/imgui.ini

+ 3 - 1
.gitignore

@@ -46,4 +46,6 @@ compileData.scor.t0000
 *.fspackage
 !Praxis3D/Data/Materials/Default/**
 !Praxis3D/Data/Models/Default/**
-!Praxis3D/Data/Sounds/Default/**
+!Praxis3D/Data/Sounds/Default/**
+*.exp
+Builds/x64/Release/Praxis3D.exp

+ 3 - 0
Dependencies/include/ImGuiColorTextEdit/TextEditor.cpp

@@ -385,6 +385,9 @@ std::string TextEditor::GetText(const Coordinates& aStart, const Coordinates& aE
 		}
 	}
 
+	// Erase the NULL characters that are left because the text editor works with chars internally
+	result.erase(std::find(result.begin(), result.end(), '\0'), result.end());
+
 	return result;
 }
 

File diff suppressed because it is too large
+ 299 - 27
Praxis3D/Data/Maps/default.pmap


BIN
Praxis3D/Data/Materials/Default/GUI/logo3.png


+ 19 - 0
Praxis3D/Data/Scripts/Constant_rotation_two_axes.lua

@@ -3,6 +3,7 @@
 	
 	Inputs:
 	rotationSpeed - (float) speed of rotation
+	rotationMax	  - (float) stop rotating when the accumulated angle maximum is reached
 	rotationAxis1 - (vec3f) first axis of rotation	
 	rotationAxis2 - (vec3f) second axis of rotation
 	syncWithPhysicsSimulation - (bool) stop rotating when the physics simulation is paused
@@ -13,6 +14,14 @@ function init ()
 	create(Types.SpatialDataManager, 'spatialData');
 	
 	doRotation = true
+	limitRotation = false
+	totalAngleRotated = 0.0
+	totalAngleLimit = 0.0
+	
+	if rotationMax then
+		limitRotation = true
+		totalAngleLimit = rotationMax
+	end
 	
 	speed = rotationSpeed
 	if speed == 0 then
@@ -29,6 +38,12 @@ function update (p_deltaTime)
 		doRotation = getPhysicsSimulationRunning()
 	end
 	
+	if limitRotation then
+		if totalAngleRotated > totalAngleLimit then
+			doRotation = false
+		end
+	end
+	
 	if doRotation then
 		-- Get current spatial data and its inverse
 		localTransformMat4 = spatialData:getLocalTransform()
@@ -44,6 +59,10 @@ function update (p_deltaTime)
 		
 		-- Update spatial data with the new matrix
 		spatialData:setLocalTransform(localTransformMat4)
+		
+		if limitRotation then
+			totalAngleRotated = totalAngleRotated + angle
+		end
 	end
 	
 end

+ 1 - 0
Praxis3D/Data/Scripts/Example_map_startup_window.lua

@@ -65,6 +65,7 @@ function update (p_deltaTime)
 		
 		-- Set GUI colors
 		GUI.PushStyleColor(ImGuiCol.WindowBg, 0.102, 0.102, 0.102, 0.9)
+		GUI.PushStyleColor(ImGuiCol.ChildBg, 0.0, 0.0, 0.0, 0.0)
 		
 		-- Set the GUI window to be fullscreen
 		GUI.SetNextWindowPos(0, 0)

+ 16 - 0
Praxis3D/Data/Scripts/MainMenu_image_buttons.lua

@@ -162,6 +162,22 @@ function init ()
 	ErrHandlerLoc.logErrorCode(ErrorCode.Initialize_success, getLuaFilename())
 end
 	
+function drawLoadingScreen()
+	-- Draw the background color
+	GUI.PushStyleColor(ImGuiCol.WindowBg, 0.102, 0.102, 0.102, 255.0)
+	GUI.SetNextWindowPos(0, 0)
+	GUI.SetNextWindowSize(screenSize.x, screenSize.y)
+	GUI.Begin('##LoadingScreen', bitwiseOr(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings, ImGuiWindowFlags.NoMouseInputs))
+	
+	-- Set the CONTINUE button position to be centered at the bottom of the screen
+	GUI.SetCursorPosX((screenSize.x) * 0.5)
+	GUI.SetCursorPosY((screenSize.y) * 0.5)
+	GUI.Text('Loading...')
+	
+	GUI.End()
+	GUI.PopStyleColor()
+end
+	
 function update (p_deltaTime)
 	-- Make sure the mouse is released, so the buttons can be pressed
 	setMouseCapture(false)

+ 1 - 1
Praxis3D/Data/Shaders/atmosphericScatteringPass_sky.frag

@@ -643,5 +643,5 @@ void main()
 	
 	//radiance = vec3(1.0) - exp(-radiance / atmScatteringParam.m_whitePoint);
 	//color = vec4(vec3(1.0) - exp(-radiance / atmScatteringParam.m_whitePoint * directionalLight.m_intensity), 1.0);
-	color = vec4(radiance / atmScatteringParam.m_whitePoint * directionalLight.m_intensity * 1.0, 1.0);
+	color = vec4(radiance / atmScatteringParam.m_whitePoint * directionalLight.m_intensity * 0.5, 1.0);
 }

+ 5 - 0
Praxis3D/Data/Shaders/bloomDownscale.comp

@@ -1,3 +1,7 @@
+/*
+	Bloom downscale shader, compute (bloomDownscale.comp)
+	Performs downscaling going from the largest to the smallest mipmap level, while also averaging the color (using on Karis-Average)
+*/
 #version 460
 
 #define GROUP_SIZE         8
@@ -76,6 +80,7 @@ void main()
         vec2 uv_offset = vec2(i % TILE_SIZE, i / TILE_SIZE) * texelSize;
         
         vec4 color = textureLod(inputColorMap, uv + uv_offset, mipLevel);
+		
         storeLDS(i, color);
     }
 

+ 4 - 0
Praxis3D/Data/Shaders/bloomUpscale.comp

@@ -1,3 +1,7 @@
+/*
+	Bloom upscale shader, compute (bloomUpscale.comp)
+	Performs upscaling going from the smallest to the largest mipmap level, while also bluring the color
+*/
 #version 460
 
 #define GROUP_SIZE         8

+ 0 - 0
Praxis3D/Data/Shaders/csmPass.frag → Praxis3D/Data/Shaders/csmPassLayered.frag


+ 0 - 1
Praxis3D/Data/Shaders/csmPass.geom → Praxis3D/Data/Shaders/csmPassLayered.geom

@@ -30,7 +30,6 @@ in vec2 v_texCoord[];
 out vec2 g_texCoord;
 #endif
 
-
 void main()
 {
 	// Go over each vertex

+ 0 - 1
Praxis3D/Data/Shaders/csmPass.vert → Praxis3D/Data/Shaders/csmPassLayered.vert

@@ -31,7 +31,6 @@ out vec2 v_texCoord;
 #endif
 
 uniform mat4 modelMat;
-uniform mat4 MVP;
 
 void main(void) 
 {

+ 43 - 0
Praxis3D/Data/Shaders/csmPassSingle.frag

@@ -0,0 +1,43 @@
+#version 430 core
+
+#define NUM_OF_MATERIAL_TYPES 4
+#define MATERIAL_TYPE_DIFFUSE 0
+#define MATERIAL_TYPE_NORMAL 1
+#define MATERIAL_TYPE_EMISSIVE 2
+#define MATERIAL_TYPE_COMBINED 3
+
+// Discard fragments based on transparency
+#define ALPHA_DISCARD 1
+
+#if ALPHA_DISCARD
+uniform sampler2D diffuseTexture;
+uniform float alphaThreshold;
+
+struct MaterialData
+{
+	vec4 m_color;
+	vec2 m_scale;
+	vec2 m_framing;
+};
+
+layout (std140) uniform MaterialDataBuffer
+{
+	MaterialData m_materialData[NUM_OF_MATERIAL_TYPES];
+};
+
+in vec2 texCoord;
+#endif
+
+void main()
+{
+#if ALPHA_DISCARD
+
+	// Get fragment alpha value
+	float alpha = texture(diffuseTexture, texCoord).a * m_materialData[MATERIAL_TYPE_DIFFUSE].m_color.a;
+	
+	// Discard fragment if the diffuse alpha color value is smaller than alpha threshold
+	if(alpha < alphaThreshold)
+		discard;
+
+#endif
+}

+ 42 - 0
Praxis3D/Data/Shaders/csmPassSingle.vert

@@ -0,0 +1,42 @@
+#version 430 core
+
+#define NUM_OF_MATERIAL_TYPES 4
+#define MATERIAL_TYPE_DIFFUSE 0
+#define MATERIAL_TYPE_NORMAL 1
+#define MATERIAL_TYPE_EMISSIVE 2
+#define MATERIAL_TYPE_COMBINED 3
+
+// Discard fragments based on transparency
+#define ALPHA_DISCARD 1
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+
+#if ALPHA_DISCARD
+struct MaterialData
+{
+	vec4 m_color;
+	vec2 m_scale;
+	vec2 m_framing;
+};
+
+layout (std140) uniform MaterialDataBuffer
+{
+	MaterialData m_materialData[NUM_OF_MATERIAL_TYPES];
+};
+
+layout(location = 2) in vec2 textureCoord;
+
+out vec2 texCoord;
+#endif
+
+uniform mat4 modelMat;
+
+void main(void) 
+{
+	gl_Position = modelMat * vec4(vertexPosition, 1.0);
+	
+#if ALPHA_DISCARD
+	texCoord = (textureCoord + m_materialData[MATERIAL_TYPE_DIFFUSE].m_framing) * m_materialData[MATERIAL_TYPE_DIFFUSE].m_scale;
+#endif
+}

+ 111 - 0
Praxis3D/Data/Shaders/exposureAdaptation.frag

@@ -0,0 +1,111 @@
+/*
+	Exposure adaptation shader, fragment (exposureAdaptation.frag)
+	Adjusts exposure by converting the color from RGB to Yxy color space and adjusting the luminosity component based on average scene luminance value.
+*/
+#version 430 core
+
+out vec4 outputColor;
+
+uniform ivec2 screenSize;
+uniform float luminanceMultiplier;
+
+uniform sampler2D inputColorMap;
+uniform sampler2D averageLuminanceTexture;
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+float log10(const float p_x)
+{
+    //log10(x) = log(x) / log(10) = (1 / log(10)) * log(x)
+    const float d = 1.0 / log(10.0);
+    return d * log(p_x);
+}
+
+vec3 splatVec3(const float p_x) 
+{ 
+	return vec3(p_x, p_x, p_x); 
+}
+
+// Convert RGB to CIE 1931 XYZ color space
+vec3 convertRGB2XYZ(const vec3 p_rgb)
+{
+	// Reference:
+	// RGB/XYZ Matrices
+	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+	vec3 xyz;
+	xyz.x = dot(vec3(0.4124564, 0.3575761, 0.1804375), p_rgb);
+	xyz.y = dot(vec3(0.2126729, 0.7151522, 0.0721750), p_rgb);
+	xyz.z = dot(vec3(0.0193339, 0.1191920, 0.9503041), p_rgb);
+	return xyz;
+}
+
+// Convert XYZ to RGB color space
+vec3 convertXYZ2RGB(const vec3 p_xyz)
+{
+	vec3 rgb;
+	rgb.x = dot(vec3( 3.2404542, -1.5371385, -0.4985314), p_xyz);
+	rgb.y = dot(vec3(-0.9692660,  1.8760108,  0.0415560), p_xyz);
+	rgb.z = dot(vec3( 0.0556434, -0.2040259,  1.0572252), p_xyz);
+	return rgb;
+}
+
+// Convert XYZ to Yxy color space
+vec3 convertXYZ2Yxy(const vec3 p_xyz)
+{
+	// Reference:
+	// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html
+	float inv = 1.0 / dot(p_xyz, vec3(1.0, 1.0, 1.0));
+	return vec3(p_xyz.y, p_xyz.x * inv, p_xyz.y * inv);
+}
+
+// Convert Yxy to XYZ color space
+vec3 convertYxy2XYZ(const vec3 p_Yxy)
+{
+	// Reference:
+	// http://www.brucelindbloom.com/index.html?Eqn_xyY_to_XYZ.html
+	vec3 xyz;
+	xyz.x = p_Yxy.x * p_Yxy.y / p_Yxy.z;
+	xyz.y = p_Yxy.x;
+	xyz.z = p_Yxy.x * (1.0 - p_Yxy.y - p_Yxy.z) / p_Yxy.z;
+	return xyz;
+}
+
+// Convert RGB to Yxy color space
+vec3 convertRGB2Yxy(const vec3 p_rgb)
+{
+	return convertXYZ2Yxy(convertRGB2XYZ(p_rgb));
+}
+
+// Convert Yxy to RGB color space
+vec3 convertYxy2RGB(const vec3 p_Yxy)
+{
+	return convertXYZ2RGB(convertYxy2XYZ(p_Yxy));
+}
+
+void main(void)
+{	
+	// Calculate screen-space texture coordinates, for buffer access
+	const vec2 texCoord = calcTexCoord();
+
+	// Get the color of the current fragment
+	vec3 fragmentColor = texture2D(inputColorMap, texCoord).xyz;
+
+	// Get the average scene luminance
+	const float luminance = texture2D(averageLuminanceTexture, vec2(0.0, 0.0)).r * luminanceMultiplier;
+	
+	// Do not adjust the exposure on black areas, as it can introduce
+	if(fragmentColor != vec3(0.0))
+	{
+		// Perform exposure compensation by converting the color from RGB to Yxy color space and adjusting the luminosity component
+		vec3 colorYxy = convertRGB2Yxy(fragmentColor);
+		colorYxy.x /= (9.6 * luminance + 0.0001);
+		fragmentColor = convertYxy2RGB(colorYxy);
+		//fragmentColor = max(convertYxy2RGB(colorYxy), vec3(0.0));
+	}
+	
+	// Write the color to the framebuffer
+	outputColor = vec4(fragmentColor, 1.0);
+}

+ 14 - 0
Praxis3D/Data/Shaders/exposureAdaptation.vert

@@ -0,0 +1,14 @@
+/*
+	Exposure adaptation shader, vertex (exposureAdaptation.vert)
+	Adjusts exposure by converting the color from RGB to Yxy color space and adjusting the luminosity component based on average scene luminance value.
+*/
+#version 430 core
+
+void main(void) 
+{	
+	// Determine texture coordinates
+	const vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
+}

+ 51 - 45
Praxis3D/Data/Shaders/finalPass.frag

@@ -1,3 +1,7 @@
+/*
+	Final pass shader, fragment (finalPass.frag)
+	Performs FXAA anti-aliasing. Also used to copy color data to the default framebuffer to show on screen.
+*/
 #version 430 core
 
 // Settings for FXAA.
@@ -32,60 +36,61 @@ vec3 fxaa(const sampler2D p_colorMap, const vec2 p_texCoord)
 	vec3 colorCenter = texture(p_colorMap, p_texCoord).rgb;
 	
 	// Luma at the current fragment
-	float lumaCenter = rgb2luma(colorCenter);
+	const float lumaCenter = rgb2luma(colorCenter);
 	
 	// Luma at the four direct neighbours of the current fragment.
-	float lumaDown = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(0, -1)).rgb);
-	float lumaUp = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(0, 1)).rgb);
-	float lumaLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, 0)).rgb);
-	float lumaRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, 0)).rgb);
+	const float lumaDown = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(0, -1)).rgb);
+	const float lumaUp = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(0, 1)).rgb);
+	const float lumaLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, 0)).rgb);
+	const float lumaRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, 0)).rgb);
 	
 	// Find the maximum and minimum luma around the current fragment.
-	float lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight)));
-	float lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight)));
+	const float lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight)));
+	const float lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight)));
 
 	// Compute the delta.
-	float lumaRange = lumaMax - lumaMin;
+	const float lumaRange = lumaMax - lumaMin;
 	
 	// If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA.
 	if(lumaRange < max(FXAA_EDGE_THRESHOLD_MIN, lumaMax * FXAA_EDGE_THRESHOLD_MAX))
 		return colorCenter;
 	
 	// Query the 4 remaining corners lumas.
-	float lumaDownLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, -1)).rgb);
-	float lumaUpRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, 1)).rgb);
-	float lumaUpLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, 1)).rgb);
-	float lumaDownRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, -1)).rgb);
+	const float lumaDownLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, -1)).rgb);
+	const float lumaUpRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, 1)).rgb);
+	const float lumaUpLeft = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(-1, 1)).rgb);
+	const float lumaDownRight = rgb2luma(textureOffset(p_colorMap, p_texCoord, ivec2(1, -1)).rgb);
 
 	// Combine the four edges lumas (using intermediary variables for future computations with the same values).
-	float lumaDownUp = lumaDown + lumaUp;
-	float lumaLeftRight = lumaLeft + lumaRight;
+	const float lumaDownUp = lumaDown + lumaUp;
+	const float lumaLeftRight = lumaLeft + lumaRight;
 
 	// Same for corners
-	float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
-	float lumaDownCorners = lumaDownLeft + lumaDownRight;
-	float lumaRightCorners = lumaDownRight + lumaUpRight;
-	float lumaUpCorners = lumaUpRight + lumaUpLeft;
+	const float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
+	const float lumaDownCorners = lumaDownLeft + lumaDownRight;
+	const float lumaRightCorners = lumaDownRight + lumaUpRight;
+	const float lumaUpCorners = lumaUpRight + lumaUpLeft;
 
 	// Compute an estimation of the gradient along the horizontal and vertical axis.
-	float edgeHorizontal =  abs(-2.0 * lumaLeft + lumaLeftCorners)  + abs(-2.0 * lumaCenter + lumaDownUp ) * 2.0    + abs(-2.0 * lumaRight + lumaRightCorners);
-	float edgeVertical =    abs(-2.0 * lumaUp + lumaUpCorners)      + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0  + abs(-2.0 * lumaDown + lumaDownCorners);
+	const float edgeHorizontal =  abs(-2.0 * lumaLeft + lumaLeftCorners)  + abs(-2.0 * lumaCenter + lumaDownUp ) * 2.0    + abs(-2.0 * lumaRight + lumaRightCorners);
+	const float edgeVertical =    abs(-2.0 * lumaUp + lumaUpCorners)      + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0  + abs(-2.0 * lumaDown + lumaDownCorners);
 
 	// Is the local edge horizontal or vertical ?
-	bool isHorizontal = (edgeHorizontal >= edgeVertical);
+	const bool isHorizontal = (edgeHorizontal >= edgeVertical);
 	
 	// Select the two neighboring texels lumas in the opposite direction to the local edge.
-	float luma1 = isHorizontal ? lumaDown : lumaLeft;
-	float luma2 = isHorizontal ? lumaUp : lumaRight;
+	const float luma1 = isHorizontal ? lumaDown : lumaLeft;
+	const float luma2 = isHorizontal ? lumaUp : lumaRight;
+	
 	// Compute gradients in this direction.
-	float gradient1 = luma1 - lumaCenter;
-	float gradient2 = luma2 - lumaCenter;
+	const float gradient1 = luma1 - lumaCenter;
+	const float gradient2 = luma2 - lumaCenter;
 
 	// Which direction is the steepest ?
-	bool is1Steepest = abs(gradient1) >= abs(gradient2);
+	const bool is1Steepest = abs(gradient1) >= abs(gradient2);
 
 	// Gradient in the corresponding direction, normalized.
-	float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
+	const float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
 	
 	// Choose the step size (one pixel) according to the edge direction.
 	float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x;
@@ -116,7 +121,8 @@ vec3 fxaa(const sampler2D p_colorMap, const vec2 p_texCoord)
 	}
 	
 	// Compute offset (for each iteration step) in the right direction.
-	vec2 offset = isHorizontal ? vec2(inverseScreenSize.x, 0.0) : vec2(0.0, inverseScreenSize.y);
+	const vec2 offset = isHorizontal ? vec2(inverseScreenSize.x, 0.0) : vec2(0.0, inverseScreenSize.y);
+	
 	// Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster.
 	vec2 uv1 = currentUv - offset;
 	vec2 uv2 = currentUv + offset;
@@ -145,7 +151,6 @@ vec3 fxaa(const sampler2D p_colorMap, const vec2 p_texCoord)
 	// If both sides have not been reached, continue to explore.
 	if(!reachedBoth)
 	{
-
 		for(int i = 2; i < FXAA_ITERATIONS; i++){
 			// If needed, read luma in 1st direction, compute delta.
 			if(!reached1)
@@ -183,37 +188,39 @@ vec3 fxaa(const sampler2D p_colorMap, const vec2 p_texCoord)
 	}
 	
 	// Compute the distances to each extremity of the edge.
-	float distance1 = isHorizontal ? (p_texCoord.x - uv1.x) : (p_texCoord.y - uv1.y);
-	float distance2 = isHorizontal ? (uv2.x - p_texCoord.x) : (uv2.y - p_texCoord.y);
+	const float distance1 = isHorizontal ? (p_texCoord.x - uv1.x) : (p_texCoord.y - uv1.y);
+	const float distance2 = isHorizontal ? (uv2.x - p_texCoord.x) : (uv2.y - p_texCoord.y);
 
 	// In which direction is the extremity of the edge closer ?
-	bool isDirection1 = distance1 < distance2;
-	float distanceFinal = min(distance1, distance2);
+	const bool isDirection1 = distance1 < distance2;
+	const float distanceFinal = min(distance1, distance2);
 
 	// Length of the edge.
-	float edgeThickness = (distance1 + distance2);
+	const float edgeThickness = (distance1 + distance2);
 
 	// UV offset: read in the direction of the closest side of the edge.
-	float pixelOffset = -distanceFinal / edgeThickness + 0.5;
+	const float pixelOffset = -distanceFinal / edgeThickness + 0.5;
 	
 	// Is the luma at center smaller than the local average ?
-	bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
+	const bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
 
 	// If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation).
 	// (in the direction of the closer side of the edge.)
-	bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller;
+	const bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller;
 
 	// If the luma variation is incorrect, do not offset.
 	float finalOffset = correctVariation ? pixelOffset : 0.0;
 	
 	// Sub-pixel shifting
 	// Full weighted average of the luma over the 3x3 neighborhood.
-	float lumaAverage = (1.0/12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
+	const float lumaAverage = (1.0/12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
+	
 	// Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood.
-	float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
-	float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
+	const float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
+	const float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
+	
 	// Compute a sub-pixel offset based on this delta.
-	float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * FXAA_SUBPIXEL_QUALITY;
+	const float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * FXAA_SUBPIXEL_QUALITY;
 
 	// Pick the biggest of the two offsets.
 	finalOffset = max(finalOffset, subPixelOffsetFinal);
@@ -231,20 +238,19 @@ vec3 fxaa(const sampler2D p_colorMap, const vec2 p_texCoord)
 
 	// Read the color at the new UV coordinates, and use it.
 	return texture(p_colorMap, finalUv).rgb;
-	//fragColor = finalColor;
 }
 
 void main(void)
 {	
 	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
+	const vec2 texCoord = calcTexCoord();
 	
 #if FXAA
 	// Perform Fast Approximate Anti-Aliasing
-	vec3 fragmentColor = fxaa(inputColorMap, texCoord);
+	const vec3 fragmentColor = fxaa(inputColorMap, texCoord);
 #else
 	// Get the color data from the final color map
-	vec3 fragmentColor = texture(inputColorMap, texCoord).xyz;
+	const vec3 fragmentColor = texture(inputColorMap, texCoord).xyz;
 #endif
 	
 	// Write the color to the framebuffer

+ 5 - 1
Praxis3D/Data/Shaders/finalPass.vert

@@ -1,9 +1,13 @@
+/*
+	Final pass shader, vertex (finalPass.vert)
+	Performs FXAA anti-aliasing. Also used to copy color data to the default framebuffer to show on screen.
+*/
 #version 430 core
 
 void main(void) 
 {	
 	// Determine texture coordinates
-	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	const vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
 	
 	// Calculate the position, so that the triangle fills the whole screen
 	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);

+ 19 - 7
Praxis3D/Data/Shaders/geometryPass.frag

@@ -1,3 +1,15 @@
+/*
+	Geometry pass shader, fragment (geometryPass.frag)
+	Geometry stage of the deferred rendering, draws scene objects to fill the geometry buffers:
+		position data,
+		albedo (diffuse) color,
+		normal data,
+		emissive color,
+		combined material data (RMHA - roughness, metalness, height, ambien-occlusion)
+	Reconstructs the XYZ normal from a compressed normal texture holding XY.
+	Performs Parallax Occlusion Mapping, if defined.
+	Performs alpha discard, if defined.
+*/
 #version 430 core
 
 #define NUM_OF_MATERIAL_TYPES 4
@@ -185,14 +197,14 @@ vec3 getTBNNormalFromMap(vec2 p_texCoord, vec3 p_worldPos)
 
 vec3 barycentricToModelPosition(vec2 p_texCoords, vec3 p_vertex0, vec3 p_vertex1, vec3 p_vertex2) 
 {
-    vec3 barycentric = vec3(1.0 - p_texCoords.x - p_texCoords.y, p_texCoords.x, p_texCoords.y);
+    const vec3 barycentric = vec3(1.0 - p_texCoords.x - p_texCoords.y, p_texCoords.x, p_texCoords.y);
     return barycentric.x * p_vertex0 + barycentric.y * p_vertex1 + barycentric.z * p_vertex2;
 }
 
 vec2 calcSimpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
 {
-	float height =  getHeight(p_texCoords) * heightScale;    
-    vec2 p = p_viewDir.xy * height;
+	const float height =  getHeight(p_texCoords) * heightScale;    
+    const vec2 p = p_viewDir.xy * height;
     return p_texCoords - p;
 }
 
@@ -485,14 +497,14 @@ void main(void)
 	// G - metalness
 	// B - height
 	// A - ambient occlusion
-	vec4 combinedTextureColor = sampleTexture(combinedTexture, newCoords) * m_materialData[MATERIAL_TYPE_COMBINED].m_color;
+	const vec4 combinedTextureColor = sampleTexture(combinedTexture, newCoords) * m_materialData[MATERIAL_TYPE_COMBINED].m_color;
 	
 	// Get the emissive color with the new coordinates
 	vec4 emissiveColor = sampleTexture(emissiveTexture, newCoords) * m_materialData[MATERIAL_TYPE_EMISSIVE].m_color;
 	
 	// Get the normal map values
 	//vec3 normalColor = sampleTexture(normalTexture, newCoords).rgb * m_materialData[MATERIAL_TYPE_NORMAL].m_color.rgb;
-	vec3 normalColor = getNormalFromMap(newCoords) * m_materialData[MATERIAL_TYPE_NORMAL].m_color.rgb;
+	const vec3 normalColor = getNormalFromMap(newCoords) * m_materialData[MATERIAL_TYPE_NORMAL].m_color.rgb;
 	
 #else
 	
@@ -501,13 +513,13 @@ void main(void)
 	// G - metalness
 	// B - height
 	// A - ambient occlusion
-	vec4 combinedTextureColor = sampleTexture(combinedTexture, texCoord[MATERIAL_TYPE_COMBINED]) * m_materialData[MATERIAL_TYPE_COMBINED].m_color;
+	const vec4 combinedTextureColor = sampleTexture(combinedTexture, texCoord[MATERIAL_TYPE_COMBINED]) * m_materialData[MATERIAL_TYPE_COMBINED].m_color;
 	
 	// Get the emissive color with the new coordinates
 	vec4 emissiveColor = sampleTexture(emissiveTexture, texCoord[MATERIAL_TYPE_EMISSIVE]) * m_materialData[MATERIAL_TYPE_EMISSIVE].m_color;
 	
 	// Get the normal map values
-	vec3 normalColor = getNormalFromMap(texCoord[MATERIAL_TYPE_NORMAL]) * m_materialData[MATERIAL_TYPE_NORMAL].m_color.rgb;
+	const vec3 normalColor = getNormalFromMap(texCoord[MATERIAL_TYPE_NORMAL]) * m_materialData[MATERIAL_TYPE_NORMAL].m_color.rgb;
 
 #endif
 	

+ 16 - 4
Praxis3D/Data/Shaders/geometryPass.vert

@@ -1,3 +1,15 @@
+/*
+	Geometry pass shader, vertex (geometryPass.vert)
+	Geometry stage of the deferred rendering, draws scene objects to fill the geometry buffers:
+		position data,
+		albedo (diffuse) color,
+		normal data,
+		emissive color,
+		combined material data (RMHA - roughness, metalness, height, ambien-occlusion)
+	Reconstructs the XYZ normal from a compressed normal texture holding XY.
+	Performs Parallax Occlusion Mapping, if defined.
+	Performs alpha discard, if defined.
+*/
 #version 430 core
 
 #define NUM_OF_MATERIAL_TYPES 4
@@ -71,16 +83,16 @@ void main(void)
 	}
 		
 	// Compute TBN matrix components
-	vec3 T = normalize(normalMatrix * vertexTangent);
-    vec3 B = normalize(normalMatrix * vertexBitangent);
-    vec3 N = normalize(normalMatrix * vertexNormal);
+	const vec3 T = normalize(normalMatrix * vertexTangent);
+    const vec3 B = normalize(normalMatrix * vertexBitangent);
+    const vec3 N = normalize(normalMatrix * vertexNormal);
 	
 	// Compute TBN matrix
     TBN = (mat3(T, B, N));
 	
 #if PARALLAX_MAPPING
 	// Calculate TBN needed for tangent space (used for parallax mapping)
-	mat3 tangentTBN = transpose(TBN);
+	const mat3 tangentTBN = transpose(TBN);
 	
 	// Calculate variables needed for parallax mapping
     tangentCameraPos = tangentTBN * cameraPosVec;

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

@@ -1,5 +1,15 @@
+/*
+	Light pass shader, fragment (lightPass.frag)
+	Calculates lighting by using Cook-Torrance BRDF:
+		Microfacet distribution (based on roughness) using GGX distribution function
+		Geometry attenuation using Smith with Schlick's approximation
+		Fresnel effect using Schlick's approximation
+	Performs Cascaded Shadow Mapping, using PCF filtering with Poisson Disk sampling and automatic bias based on slope
+*/
 #version 430 core
 
+#define SHADOW_MAPPING 0
+
 #define AVG_INTENDED_BRIGHTNESS 0.5
 #define MIN_INTENDED_BRIGHTNESS 0.001
 #define MAX_INTENDED_BRIGHTNESS 100.0
@@ -8,21 +18,40 @@
 #define MAX_NUM_SPOT_LIGHTS 10
 #define PI 3.1415926535
 
+#define NUM_OF_CASCADES 1
+#define NUM_OF_PCF_SAMPLES 16
+
+const float g_pcfSampleWeight = 1.0 / NUM_OF_PCF_SAMPLES;
+
 const vec3 g_sunNoonColor = vec3(1.0, 1.0, 1.0);
 const vec3 g_sunSetColor = vec3(1.0, 0.6, 0.2);
 
 const float g_sunNoonIntensityMod = 1.0;
 const float g_sunSetIntensityMod = 0.0;
 
-layout(std430, binding = 0) buffer HDRBuffer
-{
-	float screenBrightness;
-};
+// Poisson Disk PCF sampling
+const vec2 poissonDisk[16] = vec2[]
+( 
+   vec2( -0.94201624, -0.39906216 ), 
+   vec2( 0.94558609, -0.76890725 ), 
+   vec2( -0.094184101, -0.92938870 ), 
+   vec2( 0.34495938, 0.29387760 ), 
+   vec2( -0.91588581, 0.45771432 ), 
+   vec2( -0.81544232, -0.87912464 ), 
+   vec2( -0.38277543, 0.27676845 ), 
+   vec2( 0.97484398, 0.75648379 ), 
+   vec2( 0.44323325, -0.97511554 ), 
+   vec2( 0.53742981, -0.47373420 ), 
+   vec2( -0.26496911, -0.41893023 ), 
+   vec2( 0.79197514, 0.19090188 ), 
+   vec2( -0.24188840, 0.99706507 ), 
+   vec2( -0.81409955, 0.91437590 ), 
+   vec2( 0.19984126, 0.78641367 ), 
+   vec2( 0.14383161, -0.14100790 ) 
+);
 
 layout(location = 0) out vec4 colorBuffer;
 
-in float avgBrightness;
-
 struct DirectionalLight
 {
     vec3 m_color;
@@ -54,25 +83,41 @@ struct SpotLight
     float m_cutoffAngle;
 };
 
+#if SHADOW_MAPPING
+struct CascadedShadowMapDataSet
+{
+	mat4 m_lightSpaceMatrix;
+	
+	float m_cascadeplanedistance;
+	float m_maxBias;
+	float m_poissonSampleScale;
+	float m_penumbraScale;
+};
+#endif
+
 uniform sampler2D positionMap;
 uniform sampler2D diffuseMap;
 uniform sampler2D normalMap;
 uniform sampler2D emissiveMap;
 uniform sampler2D matPropertiesMap;
-uniform sampler2DArray cascadedShadowMap;
-
-//uniform samplerCube staticEnvMap;
+#if SHADOW_MAPPING
+uniform sampler2DArray csmDepthMap;
+uniform vec2 csmPenumbraScaleRange;
+uniform float csmBiasScale;
+#endif
 
+uniform mat4 lightMatrixTest;
 uniform mat4 modelViewMat;
 uniform mat4 viewMat;
 uniform vec3 cameraPosVec;
+uniform vec2 projPlaneRange; // x - zFar, y - zNear
 uniform ivec2 screenSize;
 uniform float gamma;
+uniform vec3 ambientLightIntensity;	// x - directional, y - point, z - spot
 
 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.
@@ -86,9 +131,42 @@ layout (std140) uniform SpotLights
 	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
 };
 
-vec3 worldPos;
-vec3 normal;
-vec3 fragmentToEye;
+#if SHADOW_MAPPING
+layout (std140) uniform CSMDataSetBuffer
+{
+	CascadedShadowMapDataSet m_csmDataSet[NUM_OF_CASCADES];
+};
+#endif
+
+layout (std430, binding = 0) buffer HDRBuffer
+{
+	float screenBrightness;
+};
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+// Returns a random number based on a vec3 and an int.
+float calcRandom(vec3 p_seed, int p_variable)
+{
+	vec4 seed = vec4(p_seed, p_variable);
+	float dotProduct = dot(seed, vec4(12.9898, 78.233, 45.164, 94.673));
+	return fract(sin(dotProduct) * 43758.5453);
+}
+
+// Linear interpolation in [0 1] range
+float scaleLinear(float p_value, vec2 p_valueDomain) 
+{
+	return (p_value - p_valueDomain.x) / (p_valueDomain.y - p_valueDomain.x);
+}
+
+// Linear interpolation in the given range
+float scaleLinear(float p_value, vec2 p_valueDomain, vec2 p_valueRange) 
+{
+	return mix(p_valueRange.x, p_valueRange.y, scaleLinear(p_value, p_valueDomain));
+}
 
 float getBrightestColor(vec3 p_color)
 {
@@ -134,12 +212,11 @@ float GeometrySchlickGGX(float p_NdotV, float p_roughness)
     float roughness = (p_roughness + 1.0);
     float k = (roughness * roughness) / 8.0;
 	
-    float nominator   = p_NdotV;
-    float denominator = p_NdotV * (1.0 - k) + k;
-	
-	return nominator / denominator;
+    //float nominator   = p_NdotV;
+    //float denominator = p_NdotV * (1.0 - k) + k;
+	//return nominator / denominator;
 	
-    //return p_NdotV / (p_NdotV * (1.0 - k) + k);
+    return p_NdotV / (p_NdotV * (1.0 - k) + k);
 }
 
 // Calculates geometric attenuation (or visibility term - self shadowing of microfacets)
@@ -160,7 +237,18 @@ 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, float p_ambientOcclusion)
+vec3 calcLightColor(const vec3 p_albedoColor, 
+					const vec3 p_normal, 
+					const vec3 p_fragToEye, 
+					const vec3 p_lightColor, 
+					const vec3 p_lightDirection, 
+					const float p_lightDistance, 
+					const vec3 p_F0, 
+					const float p_roughness, 
+					const float p_metalic, 
+					const float p_ambientOcclusion, 
+					const float p_ambientLightIntensity, 
+					const float p_shadowFactor)
 {	
 	/*/ Get specular and diffuse lighting
 	vec3 specularColor = LightingFuncGGX_REF(p_normal, p_fragToEye, p_lightDirection, p_roughnessSqrt, p_F0);
@@ -189,21 +277,92 @@ 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;
 	
 	// Add light color
+#if SHADOW_MAPPING
+	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL * p_shadowFactor;
+#else
 	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+#endif
 	
 	// Add ambient light
-	lightColor += radiance * p_ambientOcclusion * ambientLightIntensity * (kD * p_albedoColor);
+	lightColor += radiance * p_ambientOcclusion * p_ambientLightIntensity * (p_albedoColor) * (1.0 - p_metalic);
 	
 	return lightColor;
 }
 
-vec2 calcTexCoord(void)
+#if SHADOW_MAPPING
+float calcCascadedShadow(vec3 p_worldPos, vec3 p_normal, vec3 p_lightDirection)
 {
-    return gl_FragCoord.xy / screenSize;
+    vec4 fragPosViewSpace = viewMat * vec4(p_worldPos, 1.0);
+    float depthValue = abs(fragPosViewSpace.z);
+
+    // Calculate cascade layer
+    int layer = NUM_OF_CASCADES;
+    for (int i = 0; i < NUM_OF_CASCADES; ++i)
+    {
+        if (depthValue < m_csmDataSet[i].m_cascadeplanedistance)
+        {
+            layer = i;
+            break;
+        }
+    }
+
+	// Get fragment position in light space
+    vec4 fragPosLightSpace = m_csmDataSet[layer].m_lightSpaceMatrix * vec4(p_worldPos, 1.0);
+	
+    // Perform perspective divide
+    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
+	
+    // Transform to [0,1] range
+    projCoords = projCoords * 0.5 + 0.5;
+
+    // Get depth of current fragment from light's perspective
+    float currentDepth = projCoords.z;
+
+    // Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
+    if (currentDepth > 1.0)
+    {
+        return 0.0;
+    }
+	
+	// Compute slope
+    float slope = dot(p_normal, p_lightDirection);
+
+    // Compute depth difference
+    float depthDifference = abs(p_worldPos.z - currentDepth);
+
+    // Calculate bias	
+    float bias2 = max(m_csmDataSet[layer].m_maxBias * slope, csmBiasScale * depthDifference);
+	
+    // Calculate bias based on slope
+	float bias = csmBiasScale * tan(acos(dot(p_normal, p_lightDirection)));
+	//bias *= m_csmDataSet[layer].m_maxBias;
+	bias = clamp(bias, 0.0, m_csmDataSet[layer].m_maxBias);
+	
+	// Calculate the poisson sampling scale
+	float samplingScale = scaleLinear(depthValue, vec2(projPlaneRange.y, projPlaneRange.x), csmPenumbraScaleRange) * m_csmDataSet[layer].m_penumbraScale;
+	
+	// Start the fragment as fully lit
+    float shadow = 1.0;
+	
+    // Perform PCF
+	for(int i = 0 ; i < NUM_OF_PCF_SAMPLES; i++)
+	{		
+		// A random sample, based on the pixel's position in world space.
+		// The position is rounded to the millimeter to avoid too much aliasing
+		int index = int(16.0 * calcRandom(floor(p_worldPos.xyz * 1000.0), i)) % 16;
+		
+		// Get the fragments depth from the depth map
+		float pcfDepth = texture(csmDepthMap, vec3(projCoords.xy + poissonDisk[index] / m_csmDataSet[layer].m_poissonSampleScale * samplingScale, layer)).r;
+		
+		// Check if the fragment is in the shadow (while applying bias)
+		shadow -= (currentDepth - bias) > pcfDepth ? g_pcfSampleWeight : 0.0;
+	}
+	
+    return shadow;
 }
+#endif
 
 void main(void) 
 {
@@ -211,8 +370,8 @@ void main(void)
 	vec2 texCoord = calcTexCoord();
 	// Get the emissive texture color and convert it to linear space
 	vec3 emissiveColor = pow(texture(emissiveMap, texCoord).xyz, vec3(gamma));
-	// Get diffuse color (full-bright) from diffuse buffer and convert it to linear space
-	vec3 diffuseColor = pow(texture(diffuseMap, texCoord).xyz, vec3(gamma));
+	// Get diffuse color (full-bright) from diffuse buffer
+	vec3 diffuseColor = texture(diffuseMap, texCoord).xyz;
 	// Get pixel's position in world space
 	vec3 worldPos = texture(positionMap, texCoord).xyz;
 	// Get normal (in world space) and normalize it to minimize floating point approximation errors
@@ -220,7 +379,7 @@ void main(void)
 	// Get material properties
 	vec4 matProperties = texture(matPropertiesMap, texCoord).xyzw;
 	// Calculate view direction (fragment to eye vector)
-	fragmentToEye = normalize(cameraPosVec - worldPos);
+	vec3 fragmentToEye = normalize(cameraPosVec - worldPos);
 	
 	// Extract roughness, metalness and ambient occlusion values
 	float roughnessSqrt = matProperties.x;
@@ -234,7 +393,7 @@ void main(void)
 	vec3 dirLightDirection =  normalize(directionalLight.m_direction);
 	
 	// Get dot product between the up vector (perpendicular to the ground) and directional light direction
-	float dirLightFactor = dot(dirLightDirection, vec3(0.0, 1.0, 0.0)) + 0.001;
+	float dirLightFactor = dot(dirLightDirection, vec3(0.0, 1.0, 0.0)) + 0.03;
 	
 	// Initialize final color variable of this fragment
 	vec3 finalLightColor = vec3(0.0);
@@ -242,13 +401,17 @@ void main(void)
 	// Calculate directional light only if it is pointing from above the horizon
 	if(dirLightFactor > 0.0)
 	{
-		float dirLightFactorSqrt = sqrt(dirLightFactor);
+		float dirLightFactorSqrt = dirLightFactor;//sqrt(dirLightFactor);
 		vec3 dirLightColor = mix(g_sunSetColor, g_sunNoonColor, dirLightFactorSqrt);
 		float dirLightIntensity = directionalLight.m_intensity * mix(g_sunSetIntensityMod, g_sunNoonIntensityMod, dirLightFactorSqrt);
 	
-		// Add ambient lighting
-		//finalLightColor += diffuseColor * dirLightColor * dirLightIntensity * ambientLightIntensity * ambientOcclusion * (1.0 - metalic * metalic);
-	
+		// Perform cascaded shadow mapping
+#if SHADOW_MAPPING
+		float shadowFactor = calcCascadedShadow(worldPos, normal, dirLightDirection);
+#else
+		float shadowFactor = 1.0;
+#endif
+		
 		// Add directional lighting
 		finalLightColor += calcLightColor(
 			diffuseColor, 
@@ -260,7 +423,9 @@ void main(void)
 			f0, 
 			roughnessSqrt, 
 			metalic,
-			ambientOcclusion) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
+			ambientOcclusion,
+			ambientLightIntensity.x,
+			shadowFactor) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
 	}
 		
 	for(int i = 0; i < numPointLights; i++)
@@ -281,7 +446,9 @@ void main(void)
 			f0, 
 			roughnessSqrt, 
 			metalic, 
-			ambientOcclusion) * pointLights[i].m_intensity);
+			ambientOcclusion,
+			ambientLightIntensity.y,
+			1.0) * pointLights[i].m_intensity);
 	}
 	
 	for(int i = 0; i < numSpotLights; i++)
@@ -311,7 +478,9 @@ void main(void)
 				f0, 
 				roughnessSqrt, 
 				metalic, 
-				ambientOcclusion) * spotLights[i].m_intensity);
+				ambientOcclusion,
+				ambientLightIntensity.z,
+				1.0) * spotLights[i].m_intensity);
 			
 			// Light restriction from cone
 			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
@@ -319,10 +488,6 @@ void main(void)
 			finalLightColor += lightColor * coneAttenuation;
 		}
 	}
-	
-	float pcfDepth = texture(cascadedShadowMap, vec3(texCoord, 0)).r;
-	
+			
 	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
-	//colorBuffer = vec4(pcfDepth, pcfDepth, pcfDepth, 1.0);
-	//colorBuffer = vec4(matProperties.z, matProperties.z, matProperties.z, 1.0);
 }

+ 8 - 12
Praxis3D/Data/Shaders/lightPass.vert

@@ -1,19 +1,15 @@
+/*
+	Light pass shader, fragment (lightPass.frag)
+	Calculates lighting by using Cook-Torrance BRDF:
+		Microfacet distribution (based on roughness) using GGX distribution function
+		Geometry attenuation using Smith with Schlick's approximation
+		Fresnel effect using Schlick's approximation
+	Performs Cascaded Shadow Mapping, using PCF filtering with Poisson Disk sampling and automatic bias based on slope
+*/
 #version 430 core
 
-#define ENABLE_HDR
-
-layout(std430, binding = 0) buffer HDRBuffer
-{
-	float screenBrightness;
-};
-
-out float avgBrightness;
-
 void main(void) 
 {
-	// Send average screen brightness to fragment shader for HDR Mapping
-	avgBrightness = screenBrightness;
-
 	// Determine texture coordinates
 	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
 	

+ 0 - 477
Praxis3D/Data/Shaders/lightPass_CSM.frag

@@ -1,477 +0,0 @@
-#version 430 core
-
-#define SHADOW_MAPPING 0
-
-#define AVG_INTENDED_BRIGHTNESS 0.5
-#define MIN_INTENDED_BRIGHTNESS 0.001
-#define MAX_INTENDED_BRIGHTNESS 100.0
-
-#define MAX_NUM_POINT_LIGHTS 20
-#define MAX_NUM_SPOT_LIGHTS 10
-#define PI 3.1415926535
-
-#define NUM_OF_CASCADES 1
-#define NUM_OF_PCF_SAMPLES 16
-
-const float g_pcfSampleWeight = 1.0 / NUM_OF_PCF_SAMPLES;
-
-const vec3 g_sunNoonColor = vec3(1.0, 1.0, 1.0);
-const vec3 g_sunSetColor = vec3(1.0, 0.6, 0.2);
-
-const float g_sunNoonIntensityMod = 1.0;
-const float g_sunSetIntensityMod = 0.0;
-
-// Poisson Disk PCF sampling
-const vec2 poissonDisk[16] = vec2[]
-( 
-   vec2( -0.94201624, -0.39906216 ), 
-   vec2( 0.94558609, -0.76890725 ), 
-   vec2( -0.094184101, -0.92938870 ), 
-   vec2( 0.34495938, 0.29387760 ), 
-   vec2( -0.91588581, 0.45771432 ), 
-   vec2( -0.81544232, -0.87912464 ), 
-   vec2( -0.38277543, 0.27676845 ), 
-   vec2( 0.97484398, 0.75648379 ), 
-   vec2( 0.44323325, -0.97511554 ), 
-   vec2( 0.53742981, -0.47373420 ), 
-   vec2( -0.26496911, -0.41893023 ), 
-   vec2( 0.79197514, 0.19090188 ), 
-   vec2( -0.24188840, 0.99706507 ), 
-   vec2( -0.81409955, 0.91437590 ), 
-   vec2( 0.19984126, 0.78641367 ), 
-   vec2( 0.14383161, -0.14100790 ) 
-);
-
-layout(location = 0) out vec4 colorBuffer;
-
-in float avgBrightness;
-
-struct DirectionalLight
-{
-    vec3 m_color;
-    vec3 m_direction;
-    float m_intensity;
-};
-
-struct PointLight
-{
-    vec3 m_color;
-    vec3 m_position;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-    float m_intensity;
-};
-struct SpotLight
-{
-    vec3 m_color;
-    vec3 m_position;
-    vec3 m_direction;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-	
-    float m_intensity;
-    float m_cutoffAngle;
-};
-
-#if SHADOW_MAPPING
-struct CascadedShadowMapDataSet
-{
-	mat4 m_lightSpaceMatrix;
-	
-	float m_cascadeplanedistance;
-	float m_maxBias;
-	float m_poissonSampleScale;
-	float m_penumbraScale;
-};
-#endif
-
-uniform sampler2D positionMap;
-uniform sampler2D diffuseMap;
-uniform sampler2D normalMap;
-uniform sampler2D emissiveMap;
-uniform sampler2D matPropertiesMap;
-#if SHADOW_MAPPING
-uniform sampler2DArray csmDepthMap;
-uniform vec2 csmPenumbraScaleRange;
-uniform float csmBiasScale;
-#endif
-
-uniform mat4 lightMatrixTest;
-uniform mat4 modelViewMat;
-uniform mat4 viewMat;
-uniform vec3 cameraPosVec;
-uniform vec2 projPlaneRange; // x - zFar, y - zNear
-uniform ivec2 screenSize;
-uniform float gamma;
-uniform vec3 ambientLightIntensity;	// x - directional, y - point, z - spot
-
-uniform int numPointLights;
-uniform int numSpotLights;
-
-uniform DirectionalLight directionalLight;
-
-// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
-// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
-layout (std140) uniform PointLights
-{
-	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
-};
-layout (std140) uniform SpotLights
-{
-	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
-};
-
-#if SHADOW_MAPPING
-layout (std140) uniform CSMDataSetBuffer
-{
-	CascadedShadowMapDataSet m_csmDataSet[NUM_OF_CASCADES];
-};
-#endif
-
-layout (std430, binding = 0) buffer HDRBuffer
-{
-	float screenBrightness;
-};
-
-vec2 calcTexCoord(void)
-{
-    return gl_FragCoord.xy / screenSize;
-}
-
-// Returns a random number based on a vec3 and an int.
-float calcRandom(vec3 p_seed, int p_variable)
-{
-	vec4 seed = vec4(p_seed, p_variable);
-	float dotProduct = dot(seed, vec4(12.9898, 78.233, 45.164, 94.673));
-	return fract(sin(dotProduct) * 43758.5453);
-}
-
-// Linear interpolation in [0 1] range
-float scaleLinear(float p_value, vec2 p_valueDomain) 
-{
-	return (p_value - p_valueDomain.x) / (p_valueDomain.y - p_valueDomain.x);
-}
-
-// Linear interpolation in the given range
-float scaleLinear(float p_value, vec2 p_valueDomain, vec2 p_valueRange) 
-{
-	return mix(p_valueRange.x, p_valueRange.y, scaleLinear(p_value, p_valueDomain));
-}
-
-float getBrightestColor(vec3 p_color)
-{
-	return max(p_color.x, max(p_color.y, p_color.z));
-}
-
-// Calculates a brightness value from a color
-float calcBrightness(vec3 p_color)
-{
-	return dot(p_color, vec3(0.2126, 0.7152, 0.0722));
-}
-
-// Adjusts an RGB color based on the average brightness (exposure)
-// by making overall brightness match the average intended brightness (AVG_INTENDED_BRIGHTNESS)
-vec3 brightnessMapping(vec3 p_color, float p_exposure)
-{
-	return p_color * clamp(AVG_INTENDED_BRIGHTNESS / p_exposure, MIN_INTENDED_BRIGHTNESS, MAX_INTENDED_BRIGHTNESS);
-}
-
-float saturate(float p_value)
-{
-	return clamp(p_value, 0.0f, 1.0f);
-}
-
-// Calculates microfacet distribution (based on roughness)
-// Using GGX normal distribution function
-float DistributionGGX(vec3 p_normal, vec3 p_halfVector, float p_roughness)
-{
-	float roughnessSquaredSquared = p_roughness * p_roughness * p_roughness * p_roughness;
-    float NdotH = max(dot(p_normal, p_halfVector), 0.0);
-    float NdotH2 = NdotH * NdotH;
-	
-    float denominator = (NdotH2 * (roughnessSquaredSquared - 1.0) + 1.0);
-    denominator = PI * denominator * denominator;
-	
-    return roughnessSquaredSquared / denominator;
-}
-
-
-// Geometry attenuation (Smith with Schlick's approximation)
-float GeometrySchlickGGX(float p_NdotV, float p_roughness)
-{
-    float roughness = (p_roughness + 1.0);
-    float k = (roughness * roughness) / 8.0;
-	
-    //float nominator   = p_NdotV;
-    //float denominator = p_NdotV * (1.0 - k) + k;
-	//return nominator / denominator;
-	
-    return p_NdotV / (p_NdotV * (1.0 - k) + k);
-}
-
-// Calculates geometric attenuation (or visibility term - self shadowing of microfacets)
-float GeometrySmith(vec3 p_normal, vec3 p_fragToEye, vec3 L, float p_roughness)
-{
-    float NdotV = max(dot(p_normal, p_fragToEye), 0.0);
-    float NdotL = max(dot(p_normal, L), 0.0);
-    float ggx2  = GeometrySchlickGGX(NdotV, p_roughness);
-    float ggx1  = GeometrySchlickGGX(NdotL, p_roughness);
-	
-    return ggx1 * ggx2;
-}
-
-// Calculates fresnel effect using Schlick's approximation
-vec3 fresnelSchlick(float p_cosTheta, vec3 p_F0)
-{
-	return p_F0 + (1.0 - p_F0) * pow(clamp(1.0 - p_cosTheta, 0.0, 1.0), 5.0);
-    //return p_F0 + (1.0 - p_F0) * pow(1.0 - p_cosTheta, 5.0);
-} 
-
-vec3 calcLightColor(const vec3 p_albedoColor, 
-					const vec3 p_normal, 
-					const vec3 p_fragToEye, 
-					const vec3 p_lightColor, 
-					const vec3 p_lightDirection, 
-					const float p_lightDistance, 
-					const vec3 p_F0, 
-					const float p_roughness, 
-					const float p_metalic, 
-					const float p_ambientOcclusion, 
-					const float p_ambientLightIntensity, 
-					const float p_shadowFactor)
-{	
-	/*/ Get specular and diffuse lighting
-	vec3 specularColor = LightingFuncGGX_REF(p_normal, p_fragToEye, p_lightDirection, p_roughnessSqrt, p_F0);
-    vec3 diffuseColor = vec3(clamp(dot(p_normal, p_lightDirection), 0.0, 1.0));
-	
-	// Add specular and diffuse together, and multiply it by the color of the light
-	return (specularColor + diffuseColor * p_diffuseAmount) * p_lightColor;*/
-	
-	// Calculate per-light radiance
-	vec3 halfVector = normalize(p_fragToEye + p_lightDirection);
-	float attenuation = 1.0 / (p_lightDistance * p_lightDistance);
-	vec3 radiance = p_lightColor * attenuation;
-	
-	// Calculate Cook-Torrance BRDF
-	float NDF = DistributionGGX(p_normal, halfVector, p_roughness);
-	float G = GeometrySmith(p_normal, p_fragToEye, p_lightDirection, p_roughness);
-	vec3 F = fresnelSchlick(clamp(dot(halfVector, p_fragToEye), 0.0, 1.0), p_F0);
-	
-	vec3 kS = F;
-	vec3 kD = vec3(1.0) - kS;
-	kD *= 1.0 - p_metalic;
-	
-	vec3 numerator = NDF * G * F;
-	float denominator = 4.0 * max(dot(p_normal, p_fragToEye), 0.0) * max(dot(p_normal, p_lightDirection), 0.0);
-	vec3 specular = numerator / max(denominator, 0.001);
-	
-	// Combine diffuse, specular, radiance with albedo color and return it
-	float NdotL = max(dot(p_normal, p_lightDirection), 0.0);
-	
-	// Add light color
-#if SHADOW_MAPPING
-	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL * p_shadowFactor;
-#else
-	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL;
-#endif
-	
-	// Add ambient light
-	lightColor += radiance * p_ambientOcclusion * p_ambientLightIntensity * (p_albedoColor) * (1.0 - p_metalic);
-	
-	return lightColor;
-}
-
-#if SHADOW_MAPPING
-float calcCascadedShadow(vec3 p_worldPos, vec3 p_normal, vec3 p_lightDirection)
-{
-    vec4 fragPosViewSpace = viewMat * vec4(p_worldPos, 1.0);
-    float depthValue = abs(fragPosViewSpace.z);
-
-    // Calculate cascade layer
-    int layer = NUM_OF_CASCADES;
-    for (int i = 0; i < NUM_OF_CASCADES; ++i)
-    {
-        if (depthValue < m_csmDataSet[i].m_cascadeplanedistance)
-        {
-            layer = i;
-            break;
-        }
-    }
-
-	// Get fragment position in light space
-    vec4 fragPosLightSpace = m_csmDataSet[layer].m_lightSpaceMatrix * vec4(p_worldPos, 1.0);
-	
-    // Perform perspective divide
-    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
-	
-    // Transform to [0,1] range
-    projCoords = projCoords * 0.5 + 0.5;
-
-    // Get depth of current fragment from light's perspective
-    float currentDepth = projCoords.z;
-
-    // Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
-    if (currentDepth > 1.0)
-    {
-        return 0.0;
-    }
-	
-    // Calculate bias based on slope
-	float bias = csmBiasScale * tan(acos(dot(p_normal, p_lightDirection)));
-	bias = clamp(bias, 0.0, m_csmDataSet[layer].m_maxBias);
-	
-	// Calculate the poisson sampling scale
-	float samplingScale = scaleLinear(depthValue, vec2(projPlaneRange.y, projPlaneRange.x), csmPenumbraScaleRange) * m_csmDataSet[layer].m_penumbraScale;
-	
-	// Start the fragment as fully lit
-    float shadow = 1.0;
-	
-    // Perform PCF
-	for(int i = 0 ; i < NUM_OF_PCF_SAMPLES; i++)
-	{		
-		// A random sample, based on the pixel's position in world space.
-		// The position is rounded to the millimeter to avoid too much aliasing
-		int index = int(16.0 * calcRandom(floor(p_worldPos.xyz * 1000.0), i)) % 16;
-		
-		// Get the fragments depth from the depth map
-		float pcfDepth = texture(csmDepthMap, vec3(projCoords.xy + poissonDisk[index] / m_csmDataSet[layer].m_poissonSampleScale * samplingScale, layer)).r;
-		
-		// Check if the fragment is in the shadow (while applying bias)
-		shadow -= (currentDepth - bias) > pcfDepth ? g_pcfSampleWeight : 0.0;
-	}
-	
-    return shadow;
-}
-#endif
-
-void main(void) 
-{
-	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
-	// Get the emissive texture color and convert it to linear space
-	vec3 emissiveColor = pow(texture(emissiveMap, texCoord).xyz, vec3(gamma));
-	// Get diffuse color (full-bright) from diffuse buffer
-	vec3 diffuseColor = texture(diffuseMap, texCoord).xyz;
-	// Get pixel's position in world space
-	vec3 worldPos = texture(positionMap, texCoord).xyz;
-	// Get normal (in world space) and normalize it to minimize floating point approximation errors
-	vec3 normal = normalize(texture(normalMap, texCoord).xyz);
-	// Get material properties
-	vec4 matProperties = texture(matPropertiesMap, texCoord).xyzw;
-	// Calculate view direction (fragment to eye vector)
-	vec3 fragmentToEye = normalize(cameraPosVec - worldPos);
-	
-	// Extract roughness, metalness and ambient occlusion values
-	float roughnessSqrt = matProperties.x;
-	float metalic = matProperties.y;
-	float ambientOcclusion = matProperties.z;
-
-	// Calculate F0, with minimum IOR as 0.04
-	vec3 f0 = mix(vec3(0.04), diffuseColor, metalic);
-	
-	// Normalize direction light direction vector
-	vec3 dirLightDirection =  normalize(directionalLight.m_direction);
-	
-	// Get dot product between the up vector (perpendicular to the ground) and directional light direction
-	float dirLightFactor = dot(dirLightDirection, vec3(0.0, 1.0, 0.0)) + 0.03;
-	
-	// Initialize final color variable of this fragment
-	vec3 finalLightColor = vec3(0.0);
-	
-	// Calculate directional light only if it is pointing from above the horizon
-	if(dirLightFactor > 0.0)
-	{
-		float dirLightFactorSqrt = dirLightFactor;//sqrt(dirLightFactor);
-		vec3 dirLightColor = mix(g_sunSetColor, g_sunNoonColor, dirLightFactorSqrt);
-		float dirLightIntensity = directionalLight.m_intensity * mix(g_sunSetIntensityMod, g_sunNoonIntensityMod, dirLightFactorSqrt);
-	
-		// Perform cascaded shadow mapping
-#if SHADOW_MAPPING
-		float shadowFactor = calcCascadedShadow(worldPos, normal, dirLightDirection);
-#else
-		float shadowFactor = 1.0;
-#endif
-		
-		// Add directional lighting
-		finalLightColor += calcLightColor(
-			diffuseColor, 
-			normal, 
-			fragmentToEye, 
-			dirLightColor, 
-			dirLightDirection, 
-			1.0, 
-			f0, 
-			roughnessSqrt, 
-			metalic,
-			ambientOcclusion,
-			ambientLightIntensity.x,
-			shadowFactor) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
-	}
-		
-	for(int i = 0; i < numPointLights; i++)
-	{		
-		// Get light direction, extract length from it and normalize for usage as direction vector
-		vec3 lightDirection =  pointLights[i].m_position - worldPos;
-		float lightDistance = length(lightDirection);
-		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, 
-			ambientOcclusion,
-			ambientLightIntensity.y,
-			1.0) * pointLights[i].m_intensity);
-	}
-	
-	for(int i = 0; i < numSpotLights; i++)
-	{			
-		// Calculate direction from position of light to current pixel
-		vec3 lightToFragment = normalize(worldPos - spotLights[i].m_position);
-		
-		// Get dot product of light direction and direction of light to pixel, and use it as a factor for light strength
-		float spotLightFactor = dot(lightToFragment, spotLights[i].m_direction);
-		
-		// Early bail if pixel is outside of the cone of spot light
-		if(spotLightFactor > spotLights[i].m_cutoffAngle)
-		{
-			// Get light direction, extract length from it and normalize for usage as direction vector
-			vec3 lightDirection =  spotLights[i].m_position - worldPos;
-			float lightDistance = length(lightDirection);
-			lightDirection = normalize(lightDirection);
-			
-			// Light color multiplied by intensity
-			vec3 lightColor = (calcLightColor(
-				diffuseColor, 
-				normal, 
-				fragmentToEye, 
-				spotLights[i].m_color, 
-				lightDirection, 
-				lightDistance, 
-				f0, 
-				roughnessSqrt, 
-				metalic, 
-				ambientOcclusion,
-				ambientLightIntensity.z,
-				1.0) * spotLights[i].m_intensity);
-			
-			// Light restriction from cone
-			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
-			
-			finalLightColor += lightColor * coneAttenuation;
-		}
-	}
-			
-	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
-}

+ 4 - 0
Praxis3D/Data/Shaders/luminanceAverage.comp

@@ -1,3 +1,7 @@
+/*
+	Luminance average shader, compute (luminanceAverage.comp)
+	Calculates the average luminance value of the scene, by counting all the incremented values in each bin of the histogram buffer.
+*/
 #version 460
 
 #define GROUP_SIZE 256

+ 4 - 0
Praxis3D/Data/Shaders/luminanceHistogram.comp

@@ -1,3 +1,7 @@
+/*
+	Luminance histogram shader, compute (luminanceHistogram.comp)
+	Calculates the luminance histogram of the scene by converting color to luminance and incrementing the corresponding bin in the histogram buffer.
+*/
 #version 460
 
 #define GROUP_SIZE 256

+ 30 - 0
Praxis3D/Data/Shaders/passthrough.frag

@@ -0,0 +1,30 @@
+/*
+	Passthrough shader, fragment (passthrough.frag)
+	Simply copies color from the input texture to the output texture.
+*/
+#version 430 core
+
+#define TONEMAPPING_METHOD 0
+
+out vec4 outputColor;
+
+uniform ivec2 screenSize;
+
+uniform sampler2D inputColorMap;
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+void main(void)
+{	
+	// Calculate screen-space texture coordinates, for buffer access
+	const vec2 texCoord = calcTexCoord();
+
+	// Get the color of the current fragment
+	const vec3 fragmentColor = texture2D(inputColorMap, texCoord).xyz;
+	
+	// Write the color to the framebuffer
+	outputColor = vec4(fragmentColor, 1.0);
+}

+ 6 - 14
Praxis3D/Data/Shaders/lightPass_CSM.vert → Praxis3D/Data/Shaders/passthrough.vert

@@ -1,22 +1,14 @@
+/*
+	Passthrough shader, vertex (passthrough.vert)
+	Simply copies color from the input texture to the output texture.
+*/
 #version 430 core
 
-#define ENABLE_HDR
-
-layout(std430, binding = 0) buffer HDRBuffer
-{
-	float screenBrightness;
-};
-
-out float avgBrightness;
-
 void main(void) 
-{
-	// Send average screen brightness to fragment shader for HDR Mapping
-	avgBrightness = screenBrightness;
-
+{	
 	// Determine texture coordinates
 	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
 	
 	// Calculate the position, so that the triangle fills the whole screen
-	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
 }

+ 90 - 99
Praxis3D/Data/Shaders/tonemapping.frag

@@ -1,5 +1,11 @@
+/*
+	Tonemapping pass shader, fragment (tonemapping.frag)
+	Performs one of several defined tonemapping methods and gamma-correction
+*/
 #version 430 core
 
+#define TONEMAPPING_METHOD 0
+
 out vec4 outputColor;
 
 uniform ivec2 screenSize;
@@ -55,7 +61,7 @@ vec3 convertXYZ2Yxy(vec3 p_xyz)
 {
 	// Reference:
 	// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html
-	float inv = 1.0 / dot(p_xyz, vec3(1.0, 1.0, 1.0));
+	const float inv = 1.0 / dot(p_xyz, vec3(1.0, 1.0, 1.0));
 	return vec3(p_xyz.y, p_xyz.x * inv, p_xyz.y * inv);
 }
 
@@ -85,9 +91,9 @@ vec3 convertYxy2RGB(vec3 p_Yxy)
 
 vec3 gammaCorrectionAccurate(vec3 p_color)
 {
-	vec3 lo  = p_color * 12.92;
-	vec3 hi  = pow(abs(p_color), splatVec3(1.0 / 2.4) ) * 1.055 - 0.055;
-	vec3 rgb = mix(hi, lo, vec3(lessThanEqual(p_color, splatVec3(0.0031308))));
+	const vec3 lo  = p_color * 12.92;
+	const vec3 hi  = pow(abs(p_color), splatVec3(1.0 / 2.4) ) * 1.055 - 0.055;
+	const vec3 rgb = mix(hi, lo, vec3(lessThanEqual(p_color, splatVec3(0.0031308))));
 	return rgb;
 }
 
@@ -98,9 +104,9 @@ vec3 gammaCorrection(vec3 p_color, float p_gamma)
 
 vec3 compensateByLuminance(vec3 p_color, float p_luminance)
 {
-	float KeyValue = 1.03 - (2.0 / (log10(p_luminance + 1.0) + 2.0));
-    float ExposureValue = log2(KeyValue / p_luminance);// + _ManualBias;
-    return p_color * exp2(ExposureValue);
+	const float keyValue = 1.03 - (2.0 / (log10(p_luminance + 1.0) + 2.0));
+    const float exposureValue = log2(keyValue / p_luminance);// + _ManualBias;
+    return p_color * exp2(exposureValue);
 }
 
 // Simple reinhard tone mapping
@@ -123,12 +129,12 @@ vec3 tonemap_filmic(vec3 p_color)
 {
 	// http://www.gdcvault.com/play/1012459/Uncharted_2__HDR_Lighting
 	// http://filmicgames.com/archives/75 - the coefficients are from here
-	float A = 0.15; // Shoulder Strength
-	float B = 0.50; // Linear Strength
-	float C = 0.10; // Linear Angle
-	float D = 0.20; // Toe Strength
-	float E = 0.02; // Toe Numerator
-	float F = 0.30; // Toe Denominator
+	const float A = 0.15; // Shoulder Strength
+	const float B = 0.50; // Linear Strength
+	const float C = 0.10; // Linear Angle
+	const float D = 0.20; // Toe Strength
+	const float E = 0.02; // Toe Numerator
+	const float F = 0.30; // Toe Denominator
 	
 	return ((p_color * (A * p_color + C * B) + D * E) / (p_color * (A * p_color + B) + D * F)) - E / F; // E/F = Toe Angle
 }
@@ -138,16 +144,18 @@ vec3 tonemap_filmic(vec3 p_color)
 // It produces bright and vibrant images with high contrast.
 vec3 tonemap_Uncharted2(vec3 p_color)
 {
-	float A = 0.15;
-	float B = 0.50;
-	float C = 0.10;
-	float D = 0.20;
-	float E = 0.02;
-	float F = 0.30;
-	float W = 11.2;
+	const float A = 0.15;
+	const float B = 0.50;
+	const float C = 0.10;
+	const float D = 0.20;
+	const float E = 0.02;
+	const float F = 0.30;
+	const float W = 11.2;
 	p_color = ((p_color * (A * p_color + C * B) + D * E) / (p_color * (A * p_color + B) + D * F)) - E / F;
-	float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
+	
+	const float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
 	p_color /= white;
+	
 	return p_color;
 }
 
@@ -203,21 +211,21 @@ float calculateUchimura(float p_color, float p_maxBrightness, float p_contrast,
 	// Uchimura 2017, "HDR theory and practice"
 	// Math: https://www.desmos.com/calculator/gslcdxvipg
 	// Source: https://www.slideshare.net/nikuque/hdr-theory-and-practicce-jp
-	float l0 = ((p_maxBrightness - p_start) * p_length) / p_contrast;
-	float L0 = p_start - p_start / p_contrast;
-	float L1 = p_start + (1.0 - p_start) / p_contrast;
-	float S0 = p_start + l0;
-	float S1 = p_start + p_contrast * l0;
-	float C2 = (p_contrast * p_maxBrightness) / (p_maxBrightness - S1);
-	float CP = -C2 / p_maxBrightness;
-
-	float w0 = 1.0 - smoothstep(0.0, p_start, p_color);
-	float w2 = step(p_start + l0, p_color);
-	float w1 = 1.0 - w0 - w2;
-
-	float T = p_start * pow(p_color / p_start, p_black) + p_pedestal;
-	float S = p_maxBrightness - (p_maxBrightness - S1) * exp(CP * (p_color - S0));
-	float L = p_start + p_contrast * (p_color - p_start);
+	const float l0 = ((p_maxBrightness - p_start) * p_length) / p_contrast;
+	const float L0 = p_start - p_start / p_contrast;
+	const float L1 = p_start + (1.0 - p_start) / p_contrast;
+	const float S0 = p_start + l0;
+	const float S1 = p_start + p_contrast * l0;
+	const float C2 = (p_contrast * p_maxBrightness) / (p_maxBrightness - S1);
+	const float CP = -C2 / p_maxBrightness;
+
+	const float w0 = 1.0 - smoothstep(0.0, p_start, p_color);
+	const float w2 = step(p_start + l0, p_color);
+	const float w1 = 1.0 - w0 - w2;
+
+	const float T = p_start * pow(p_color / p_start, p_black) + p_pedestal;
+	const float S = p_maxBrightness - (p_maxBrightness - S1) * exp(CP * (p_color - S0));
+	const float L = p_start + p_contrast * (p_color - p_start);
 
 	return T * w0 + L * w1 + S * w2;
 }
@@ -245,81 +253,64 @@ float tonemap_Uchimura(float p_color)
 void main(void)
 {	
 	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
+	const vec2 texCoord = calcTexCoord();
 
 	// Get the color of the current fragment
 	vec3 fragmentColor = texture2D(inputColorMap, texCoord).xyz;
-
-	// Get the average scene luminance
-	float luminance = texture2D(averageLuminanceTexture, vec2(0.0, 0.0)).r * luminanceMultiplier;
 	
-	// Perform exposure compensation by converting the color from RGB to Yxy color space and adjusting the luminosity component
-	vec3 colorYxy = convertRGB2Yxy(fragmentColor);
-	colorYxy.x /= (9.6 * luminance + 0.0001);
-	fragmentColor = convertYxy2RGB(colorYxy);
-	
-	// Perform tonemapping, choosing the method based on a uniform
-	switch(tonemapMethod) 
-	{
-		// No tonemapping
-		case 0:
-		
-		break;
+	// No tonemapping
+#if TONEMAPPING_METHOD == 0
+#endif 
 		
-		// Simple reinhard tonemapping
-		case 1:
-			fragmentColor = tonemap_reinhard(fragmentColor);
-			fragmentColor.x = tonemap_ACES(fragmentColor.x);
-			fragmentColor.y = tonemap_ACES(fragmentColor.y);
-			fragmentColor.z = tonemap_ACES(fragmentColor.z);
-		break;
-			
-		// Reinhard with white point tonemapping
-		case 2:	
-			float whitePoint = 3.0f;
-			whitePoint = whitePoint * whitePoint;
+	// Simple reinhard tonemapping
+#if TONEMAPPING_METHOD == 1
+	fragmentColor = tonemap_reinhard(fragmentColor);
+#endif 
 			
-			fragmentColor = tonemap_reinhardWhitePoint(fragmentColor, whitePoint);
-		break;
+	// Reinhard with white point tonemapping
+#if TONEMAPPING_METHOD == 2
+	float whitePoint = 3.0f;
+	whitePoint = whitePoint * whitePoint;
+	fragmentColor = tonemap_reinhardWhitePoint(fragmentColor, whitePoint);
+#endif 
 		
-		// Filmic tonemapping
-		case 3:
-			fragmentColor = tonemap_filmic(fragmentColor);
-		break;
+	// Filmic tonemapping
+#if TONEMAPPING_METHOD == 3
+	fragmentColor = tonemap_filmic(fragmentColor);
+#endif 
 		
-		// Uncharted 2 tonemapping
-		case 4:
-			fragmentColor = tonemap_Uncharted2(fragmentColor);
-		break;
+	// Uncharted 2 tonemapping
+#if TONEMAPPING_METHOD == 4
+	fragmentColor = tonemap_Uncharted2(fragmentColor);
+#endif 
 		
-		// Unreal 3 tonemapping
-		case 5:
-			fragmentColor.x = tonemap_Unreal(fragmentColor.x);
-			fragmentColor.y = tonemap_Unreal(fragmentColor.y);
-			fragmentColor.z = tonemap_Unreal(fragmentColor.z);
-		break;
+	// Unreal 3 tonemapping
+#if TONEMAPPING_METHOD == 5
+	fragmentColor.x = tonemap_Unreal(fragmentColor.x);
+	fragmentColor.y = tonemap_Unreal(fragmentColor.y);
+	fragmentColor.z = tonemap_Unreal(fragmentColor.z);
+#endif 
 		
-		// ACES tonemapping
-		case 6:
-			fragmentColor.x = tonemap_ACES(fragmentColor.x);
-			fragmentColor.y = tonemap_ACES(fragmentColor.y);
-			fragmentColor.z = tonemap_ACES(fragmentColor.z);
-		break;
+	// ACES tonemapping
+#if TONEMAPPING_METHOD == 6
+	fragmentColor.x = tonemap_ACES(fragmentColor.x);
+	fragmentColor.y = tonemap_ACES(fragmentColor.y);
+	fragmentColor.z = tonemap_ACES(fragmentColor.z);
+#endif 
 		
-		// Lottes tonemapping
-		case 7:
-			fragmentColor.x = tonemap_Lottes(fragmentColor.x);
-			fragmentColor.y = tonemap_Lottes(fragmentColor.y);
-			fragmentColor.z = tonemap_Lottes(fragmentColor.z);
-		break;
+	// Lottes tonemapping
+#if TONEMAPPING_METHOD == 7
+	fragmentColor.x = tonemap_Lottes(fragmentColor.x);
+	fragmentColor.y = tonemap_Lottes(fragmentColor.y);
+	fragmentColor.z = tonemap_Lottes(fragmentColor.z);
+#endif 
 		
-		// Uchimura tonemapping
-		case 8:
-			fragmentColor.x = tonemap_Uchimura(fragmentColor.x);
-			fragmentColor.y = tonemap_Uchimura(fragmentColor.y);
-			fragmentColor.z = tonemap_Uchimura(fragmentColor.z);
-		break;
-	}
+	// Uchimura tonemapping
+#if TONEMAPPING_METHOD == 8
+	fragmentColor.x = tonemap_Uchimura(fragmentColor.x);
+	fragmentColor.y = tonemap_Uchimura(fragmentColor.y);
+	fragmentColor.z = tonemap_Uchimura(fragmentColor.z);
+#endif 
 	
 	// Perform gamma correction
 	fragmentColor = gammaCorrectionAccurate(fragmentColor);

+ 4 - 0
Praxis3D/Data/Shaders/tonemapping.vert

@@ -1,3 +1,7 @@
+/*
+	Tonemapping pass shader, vertex (tonemapping.vert)
+	Performs one of several defined tonemapping methods and gamma-correction
+*/
 #version 430 core
 
 void main(void) 

+ 9 - 6
Praxis3D/Data/config.ini

@@ -1,7 +1,8 @@
-window_position_x 0
-window_position_y 0
-window_size_windowed_x 1920
-window_size_windowed_y 1080
+window_position_x 15
+window_position_y 10
+window_position_centered 1
+window_size_windowed_x 1280
+window_size_windowed_y 768
 window_size_fullscreen_x 1920
 window_size_fullscreen_y 1080
 fullscreen 0
@@ -30,11 +31,13 @@ lens_flare_ghost_count 6
 mouse_jaw 0.05
 mouse_pitch 0.05
 tonemap_method 8
+objects_loaded_per_frame 10
+multisample_samples 1
 generate_mipmaps 1
 texture_downsample 1
-texture_downsample_max_resolution 2048
+texture_downsample_max_resolution 4096
 texture_downsample_scale 1
 texture_compression 1
 texture_normal_compression 1
 borderless 0
-maximized 1
+maximized 0

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

@@ -27,6 +27,9 @@
 		"Universal_scene_extend_null"				: "Invalid scene was passed while trying to extend the Universal Scene",
 		"Universal_scene_extend_duplicate"	: "Duplicate scene was passed while trying to extend the Universal Scene",
 		"Window_handle_missing"							: "Failed to get window handle",
+		"Lua_init_func_failed"							: "Initialization function inside a LUA script failed to run",
+		"Lua_load_script_failed"						: "Failed to load LUA script file",
+		"Lua_update_func_failed"						: "Update function inside a LUA script failed to run",
 		"AssimpScene_failed"								: "Assimp scene has failed to load",
 		"ObjectPool_full"										: "Object pool overflow",
 		"Collision_invalid"									: "Invalid collision type",

+ 3 - 1
Praxis3D/Praxis3D.vcxproj

@@ -99,7 +99,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>true</SDLCheck>
       <LanguageStandard>stdcpp20</LanguageStandard>
-      <PreprocessorDefinitions>NOMINMAX;_RELEASE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;CUSTOM_IMGUIFILEDIALOG_CONFIG="..\\..\\..\\Praxis3D\Source\EngineDefinitions.h";GLM_FORCE_CTOR_INIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NOMINMAX;NDEBUG;_RELEASE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;CUSTOM_IMGUIFILEDIALOG_CONFIG="..\\..\\..\\Praxis3D\Source\EngineDefinitions.h";GLM_FORCE_CTOR_INIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>false</ConformanceMode>
       <AdditionalOptions>/bigobj /MP8 %(AdditionalOptions)</AdditionalOptions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -176,6 +176,7 @@
     <ClCompile Include="Source\GUIHandler.cpp" />
     <ClCompile Include="Source\GUIHandlerLocator.cpp" />
     <ClCompile Include="Source\GUIScene.cpp" />
+    <ClCompile Include="Source\GUISystem.cpp" />
     <ClCompile Include="Source\GUITask.cpp" />
     <ClCompile Include="Source\Input.cpp" />
     <ClCompile Include="Source\KeyCommand.cpp" />
@@ -377,6 +378,7 @@
     <ClInclude Include="Source\TaskManagerLocator.h" />
     <ClInclude Include="Source\TaskScheduler.h" />
     <ClInclude Include="Source\TextureLoader.h" />
+    <ClInclude Include="Source\TonemappingPass.h" />
     <ClInclude Include="Source\UniformData.h" />
     <ClInclude Include="Source\ObjectRegister.h" />
     <ClInclude Include="Source\Universal.h" />

+ 6 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -465,6 +465,9 @@
     <ClCompile Include="Source\AboutWindow.cpp">
       <Filter>GUI\Objects\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\GUISystem.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Source\ErrorCodes.h">
@@ -965,6 +968,9 @@
     <ClInclude Include="Source\AboutWindow.h">
       <Filter>GUI\Objects\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\TonemappingPass.h">
+      <Filter>Renderer\Render Passes\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 36 - 27
Praxis3D/Source/AudioScene.cpp

@@ -456,6 +456,11 @@ void AudioScene::update(const float p_deltaTime)
 			}
 		}
 
+		// Get the number of active impact event audio instances
+		int impactAudioInstanceCounts[ObjectMaterialType::NumberOfMaterialTypes];
+		for(int i = 0; i < ObjectMaterialType::NumberOfMaterialTypes; i++)
+			m_impactEvents[i]->getInstanceCount(&impactAudioInstanceCounts[i]);
+
 		//	 ___________________________
 		//	|							|
 		//	|  COLLISION EVENTS UPDATE	|
@@ -477,35 +482,39 @@ void AudioScene::update(const float p_deltaTime)
 
 				auto &materialComponent = collisionEventMaterialSpatialView.get<ObjectMaterialComponent>(entity);
 
-				// Go over each collision of the entity
-				for(size_t i = 0, size = collisionComponent.m_numOfDynamicCollisions[frontIndex]; i < size; i++)
+				// Limit the number of concurrent impact event audio instances
+				if(impactAudioInstanceCounts[materialComponent.getObjectMaterialType()] <= Config::audioVar().max_impact_audio_instances)
 				{
-					//if(collisionComponent.m_dynamicCollisions[frontIndex][i].m_firstObjInCollisionPair)
+					// Go over each collision of the entity
+					for(size_t i = 0, size = collisionComponent.m_numOfDynamicCollisions[frontIndex]; i < size; i++)
 					{
-						// Get the transform matrix
-						glm::mat4 transformMatrix;
-						collisionComponent.m_dynamicCollisions[frontIndex][i].m_worldTransform.getOpenGLMatrix(glm::value_ptr(transformMatrix));
-						const glm::mat3 translateMatrix = glm::mat3(transformMatrix) * parentTranslateMatrix;
-
-						// Get 3D attributes
-						FMOD_3D_ATTRIBUTES spatialAttributes;
-						spatialAttributes.position = Math::toFmodVector(transformMatrix[3] + parentPosition);
-						spatialAttributes.velocity = Math::toFmodVector(collisionComponent.m_dynamicCollisions[frontIndex][i].m_velocity);
-						spatialAttributes.forward = Math::toFmodVector(glm::vec3(0.0f, 0.0f, -1.0f) * translateMatrix);
-						spatialAttributes.up = Math::toFmodVector(glm::vec3(0.0f, 1.0f, 0.0f) * translateMatrix);
-						const float volume = glm::clamp(collisionComponent.m_dynamicCollisions[frontIndex][i].m_appliedImpulse / Config::audioVar().impact_impulse_volume_divider, Config::audioVar().impact_min_volume_threshold, Config::audioVar().impact_max_volume_threshold);
-
-						// Create an event (sound) instance
-						FMOD::Studio::EventInstance *eventInstance;
-						m_impactEvents[materialComponent.getObjectMaterialType()]->createInstance(&eventInstance);
-
-						// Set sound parameters and play the sound
-						eventInstance->setParameterByName("Impulse", collisionComponent.m_dynamicCollisions[frontIndex][i].m_appliedImpulse / Config::audioVar().impact_impulse_param_divider);
-						eventInstance->setVolume(volume);
-						eventInstance->set3DAttributes(&spatialAttributes);
-						eventInstance->start();
-						eventInstance->setPaused(false);
-						eventInstance->release();
+						//if(collisionComponent.m_dynamicCollisions[frontIndex][i].m_firstObjInCollisionPair)
+						{
+							// Get the transform matrix
+							glm::mat4 transformMatrix;
+							collisionComponent.m_dynamicCollisions[frontIndex][i].m_worldTransform.getOpenGLMatrix(glm::value_ptr(transformMatrix));
+							const glm::mat3 translateMatrix = glm::mat3(transformMatrix) * parentTranslateMatrix;
+
+							// Get 3D attributes
+							FMOD_3D_ATTRIBUTES spatialAttributes;
+							spatialAttributes.position = Math::toFmodVector(transformMatrix[3] + parentPosition);
+							spatialAttributes.velocity = Math::toFmodVector(collisionComponent.m_dynamicCollisions[frontIndex][i].m_velocity);
+							spatialAttributes.forward = Math::toFmodVector(glm::vec3(0.0f, 0.0f, -1.0f) * translateMatrix);
+							spatialAttributes.up = Math::toFmodVector(glm::vec3(0.0f, 1.0f, 0.0f) * translateMatrix);
+							const float volume = glm::clamp(collisionComponent.m_dynamicCollisions[frontIndex][i].m_appliedImpulse / Config::audioVar().impact_impulse_volume_divider, Config::audioVar().impact_min_volume_threshold, Config::audioVar().impact_max_volume_threshold);
+
+							// Create an event (sound) instance
+							FMOD::Studio::EventInstance *eventInstance;
+							m_impactEvents[materialComponent.getObjectMaterialType()]->createInstance(&eventInstance);
+
+							// Set sound parameters and play the sound
+							eventInstance->setParameterByName("Impulse", collisionComponent.m_dynamicCollisions[frontIndex][i].m_appliedImpulse / Config::audioVar().impact_impulse_param_divider);
+							eventInstance->setVolume(volume);
+							eventInstance->set3DAttributes(&spatialAttributes);
+							eventInstance->start();
+							eventInstance->setPaused(false);
+							eventInstance->release();
+						}
 					}
 				}
 			}

+ 8 - 1
Praxis3D/Source/BloomPass.h

@@ -84,11 +84,15 @@ public:
 			glm::uvec2 mipmapSize = glm::uvec2(imageWidth / 2, imageHeight / 2);
 			unsigned int mipmapLevels = calculateMipmapLevels(imageWidth, imageHeight, (unsigned int)Config::graphicsVar().bloom_mipmap_limit, (unsigned int)Config::graphicsVar().bloom_downscale_limit);
 
-			m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture);
+			//m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture);
+			m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GBufferTextureType::GBufferInputTexture);
 
 			// Bloom downscaling
 			for(unsigned int i = 0; i < mipmapLevels - 1; i++)
 			{
+				if(i > 0)
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture);
+
 				// Assign the texel size and mipmap level so it can be sent to the shader
 				m_renderer.m_frameData.m_texelSize = 1.0f / glm::vec2(mipmapSize);
 				m_renderer.m_frameData.m_mipLevel = i;
@@ -135,6 +139,9 @@ public:
 				m_renderer.passComputeDispatchCommandsToBackend();
 			}
 		}
+
+		if(p_renderPassData.getColorInputMap() != GBufferTextureType::GBufferFinal)
+			p_renderPassData.swapColorInputOutputMaps();
 	}
 
 private:

+ 1 - 3
Praxis3D/Source/CSMFramebuffer.h

@@ -107,10 +107,8 @@ public:
 		glViewport(0, 0, m_bufferWidth, m_bufferHeight);
 		glDepthMask(GL_TRUE);
 		glEnable(GL_DEPTH_TEST);		// Enable depth testing, as this is much like a regular forward render pass
+		glDepthFunc(GL_LEQUAL);
 		glClear(GL_DEPTH_BUFFER_BIT);	// Make sure to clear the depth buffer for the new frame
-		glDisable(GL_CULL_FACE);
-		//glDepthFunc(GL_LEQUAL);
-		//glCullFace(GL_FRONT);			// peter panning
 	}
 
 	// Buffer binding functions

+ 13 - 0
Praxis3D/Source/CommonDefinitions.h

@@ -65,6 +65,12 @@ enum CommandType : unsigned int
 	CommandType_Bind,
 	CommandType_Load
 };
+enum DrawCommandTextureBinding
+{
+	DrawCommandTextureBinding_None,
+	DrawCommandTextureBinding_DiffuseOnly,
+	DrawCommandTextureBinding_All
+};
 enum MemoryBarrierType : unsigned int
 {
 	MemoryBarrierType_All,
@@ -84,6 +90,7 @@ enum MemoryBarrierType : unsigned int
 	Code(RenderPassType_LenseFlare,) \
 	Code(RenderPassType_LenseFlareComposite,) \
 	Code(RenderPassType_Luminance,) \
+	Code(RenderPassType_Tonemapping,) \
 	Code(RenderPassType_Final,) \
 	Code(RenderPassType_GUI,) \
 	Code(RenderPassType_AmbientOcclusion,) \
@@ -370,6 +377,12 @@ enum GuiFontType : unsigned int
 	GuiFontType_AboutWindow,
 	GuiFontType_NumOfTypes
 };
+enum GuiColorPallet : int
+{
+	GuiColorPallet_Default = 0,
+	GuiColorPallet_Green,
+	GuiColorPallet_NumOfPallets
+};
 
 #define OBJ_MATERIAL_ID(Code) \
 	Code(Concrete, = 0) \

+ 16 - 6
Praxis3D/Source/Config.cpp

@@ -63,7 +63,8 @@ void Config::init()
 	AddVariablePredef(m_audioVar, volume_ambient);
 	AddVariablePredef(m_audioVar, volume_master);
 	AddVariablePredef(m_audioVar, volume_music);
-	AddVariablePredef(m_audioVar, volume_sfx); 
+	AddVariablePredef(m_audioVar, volume_sfx);
+	AddVariablePredef(m_audioVar, max_impact_audio_instances);
 	AddVariablePredef(m_audioVar, num_audio_channels);
 	AddVariablePredef(m_audioVar, bus_name_ambient);
 	AddVariablePredef(m_audioVar, bus_name_master);
@@ -96,9 +97,11 @@ void Config::init()
 	AddVariablePredef(m_engineVar, gl_context_major_version);
 	AddVariablePredef(m_engineVar, gl_context_minor_version);
 	AddVariablePredef(m_engineVar, loaders_num_of_unload_per_frame);
+	AddVariablePredef(m_engineVar, log_max_num_of_logs);
 	AddVariablePredef(m_engineVar, object_directory_init_pool_size);
 	AddVariablePredef(m_engineVar, smoothing_tick_samples);
 	AddVariablePredef(m_engineVar, task_scheduler_clock_frequency);
+	AddVariablePredef(m_engineVar, log_store_logs);
 
 	// Frame-buffer variables
 	AddVariablePredef(m_framebfrVar, gl_position_buffer_internal_format);
@@ -220,6 +223,7 @@ void Config::init()
 	AddVariablePredef(m_GUIVar, gui_docking_enabled);
 	AddVariablePredef(m_GUIVar, gui_render);
 	AddVariablePredef(m_GUIVar, gui_dark_style);
+	AddVariablePredef(m_GUIVar, gui_color_pallet);
 	AddVariablePredef(m_GUIVar, gui_sequence_array_reserve_size);
 	AddVariablePredef(m_GUIVar, about_window_font_size);
 	AddVariablePredef(m_GUIVar, editor_asset_selection_button_size_multiplier);
@@ -386,9 +390,11 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, blur_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, blur_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, csm_bias_scale);
-	AddVariablePredef(m_rendererVar, csm_pass_frag_shader);
-	AddVariablePredef(m_rendererVar, csm_pass_geom_shader);
-	AddVariablePredef(m_rendererVar, csm_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_layered_frag_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_layered_geom_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_layered_vert_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_single_frag_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_single_vert_shader);
 	AddVariablePredef(m_rendererVar, hbao_blur_horizontal_frag_shader);
 	AddVariablePredef(m_rendererVar, hbao_blur_horizontal_vert_shader);
 	AddVariablePredef(m_rendererVar, hbao_blur_vertical_frag_shader);
@@ -399,8 +405,6 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, lense_flare_comp_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_pass_frag_shader);
-	AddVariablePredef(m_rendererVar, light_pass_csm_vert_shader);
-	AddVariablePredef(m_rendererVar, light_pass_csm_frag_shader);
 	AddVariablePredef(m_rendererVar, light_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, light_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, final_pass_vert_shader);
@@ -434,6 +438,8 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, parallax_mapping_max_steps);
 	AddVariablePredef(m_rendererVar, csm_num_of_pcf_samples);
 	AddVariablePredef(m_rendererVar, csm_resolution);
+	AddVariablePredef(m_rendererVar, csm_face_culling);
+	AddVariablePredef(m_rendererVar, csm_front_face_culling);
 	AddVariablePredef(m_rendererVar, depth_test_func);
 	AddVariablePredef(m_rendererVar, face_culling_mode);
 	AddVariablePredef(m_rendererVar, fxaa_iterations);
@@ -458,6 +464,7 @@ void Config::init()
 	AddVariablePredef(m_scriptVar, updateFunctionName);
 	AddVariablePredef(m_scriptVar, createObjectFunctionName);
 	AddVariablePredef(m_scriptVar, userTypeTableName);
+	AddVariablePredef(m_scriptVar, luaUpdateErrorsEveryFrame);
 
 	// Shader variables
 	AddVariablePredef(m_shaderVar, atmScatProjMatUniform);
@@ -583,6 +590,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, define_shadowMapping);
 	AddVariablePredef(m_shaderVar, define_stochasticSampling);
 	AddVariablePredef(m_shaderVar, define_stochasticSamplingSeamFix);
+	AddVariablePredef(m_shaderVar, define_tonemappingMethod);
 
 	// Texture variables
 	AddVariablePredef(m_textureVar, default_texture);
@@ -633,6 +641,8 @@ void Config::init()
 	AddVariablePredef(m_windowVar, mouse_release_on_lost_focus);
 	AddVariablePredef(m_windowVar, resizable);
 	AddVariablePredef(m_windowVar, vertical_sync);
+	AddVariablePredef(m_windowVar, window_in_focus);
+	AddVariablePredef(m_windowVar, window_position_centered);
 
 	for(int i = Properties::Null; i < Properties::NumberOfPropertyIDs; i++)
 	{

+ 47 - 20
Praxis3D/Source/Config.h

@@ -36,6 +36,7 @@ enum DataType : uint32_t
 	DataType_AmbientOcclusionData,		// AmbientOcclusionData
 	DataType_GUIPassFunctors,			// FunctorSequence
 	DataType_MiscSceneData,				// MiscSceneData
+	DataType_RenderingPasses,			// RenderingPasses
 	DataType_RenderToTexture,			// bool
 	DataType_RenderToTextureResolution, // glm::ivec2
 	DataType_ShadowMappingData,			// ShadowMappingData
@@ -350,15 +351,19 @@ namespace Properties
 	Code(ChangeController,) \
 	Code(Components,) \
 	Code(Default,) \
+	Code(Enabled,) \
 	Code(File,) \
 	Code(Filename,) \
 	Code(Index,) \
 	Code(Keybindings,) \
 	Code(LoadInBackground,) \
+	Code(Major,) \
+	Code(Minor,) \
 	Code(Name,) \
 	Code(None,) \
 	Code(Objects,) \
 	Code(ObjectPoolSize,) \
+	Code(Patch,) \
 	Code(Scene,) \
 	Code(Source,) \
 	Code(System,) \
@@ -368,6 +373,7 @@ namespace Properties
 	Code(UniversalScene,) \
 	Code(Value,) \
 	Code(Variables,) \
+	Code(Version,) \
 	/* Audio */ \
 	Code(Ambient,) \
 	Code(Audio,) \
@@ -398,6 +404,7 @@ namespace Properties
 	Code(AmbientIntensity, ) \
 	Code(AmbientOcclusion, ) \
 	Code(Attenuation,) \
+	Code(Back,) \
 	Code(Bias,) \
 	Code(BiasScale,) \
 	Code(BiasMax,) \
@@ -425,9 +432,12 @@ namespace Properties
 	Code(EmissiveIntensity,) \
 	Code(EnvironmentMapDynamic,) \
 	Code(EnvironmentMapObject,) \
+	Code(FaceCullingDraw,) \
+	Code(FaceCullingShadow,) \
 	Code(FOV,) \
 	Code(FragmentShader,) \
 	Code(Framing,) \
+	Code(Front,) \
 	Code(GeometryShader,) \
 	Code(Graphics,) \
 	Code(GraphicsObject,) \
@@ -448,9 +458,6 @@ namespace Properties
 	Code(ModelComponent,) \
 	Code(ModelObject,) \
 	Code(ModelPoolSize,) \
-	Code(NegativeX,) \
-	Code(NegativeY,) \
-	Code(NegativeZ,) \
 	Code(Normal,) \
 	Code(ParallaxHeightScale,) \
 	Code(PenumbraScale,) \
@@ -459,9 +466,6 @@ namespace Properties
 	Code(PCF,) \
 	Code(PointLight,) \
 	Code(PointLightPoolSize,) \
-	Code(PositiveX,) \
-	Code(PositiveY,) \
-	Code(PositiveZ,) \
 	Code(PostProcess,) \
 	Code(Renderer,) \
 	Code(Rendering,) \
@@ -490,6 +494,7 @@ namespace Properties
 	Code(TessEvaluationShader,) \
 	Code(TextureTilingFactor,) \
 	Code(TextureScale,) \
+	Code(TonemappingPass,) \
 	Code(Top,) \
 	Code(VertexShader,) \
 	Code(WrapMode,) \
@@ -709,6 +714,7 @@ public:
 			volume_master = 0.5f;
 			volume_music = 1.0f;
 			volume_sfx = 1.0f;
+			max_impact_audio_instances = 10;
 			num_audio_channels = 32;
 			bus_name_ambient = "Ambient";
 			bus_name_master = "";
@@ -732,6 +738,7 @@ public:
 		float volume_master;
 		float volume_music;
 		float volume_sfx;
+		int max_impact_audio_instances;
 		int num_audio_channels;
 		std::string bus_name_ambient;
 		std::string bus_name_master;
@@ -789,11 +796,13 @@ public:
 			gl_context_major_version = 3;
 			gl_context_minor_version = 3;
 			loaders_num_of_unload_per_frame = 1;
+			log_max_num_of_logs = 200;
 			object_directory_init_pool_size = 1000;
 			smoothing_tick_samples = 100;
 			task_scheduler_clock_frequency = 120;
 			running = true;
 			loadingState = true;
+			log_store_logs = true;
 			editorState = false;
 			engineState = EngineStateType::EngineStateType_MainMenu;
 		}
@@ -809,11 +818,13 @@ public:
 		int gl_context_major_version;
 		int gl_context_minor_version;
 		int loaders_num_of_unload_per_frame;
+		int log_max_num_of_logs;
 		int object_directory_init_pool_size;
 		int smoothing_tick_samples;
 		int task_scheduler_clock_frequency;
 		bool running;
 		bool loadingState;
+		bool log_store_logs;
 		bool editorState;
 		EngineStateType engineState;
 	};
@@ -1097,6 +1108,7 @@ public:
 			gui_docking_enabled = true;
 			gui_render = true;
 			gui_dark_style = true;
+			gui_color_pallet = 1;
 			gui_sequence_array_reserve_size = 50;
 			about_window_font_size = 30.0f;
 			editor_asset_selection_button_size_multiplier = 2.0f;
@@ -1142,6 +1154,7 @@ public:
 		bool gui_docking_enabled;
 		bool gui_render;
 		bool gui_dark_style;
+		int gui_color_pallet;
 		int gui_sequence_array_reserve_size;
 		float about_window_font_size;
 		float editor_asset_selection_button_size_multiplier;
@@ -1383,6 +1396,8 @@ public:
 			spot_light_cone = "cone.3ds";
 			stencil_pass_vert_shader = "stencilPass.vert";
 			stencil_pass_frag_shader = "stencilPass.frag";
+			exposure_adaptation_frag_shader = "exposureAdaptation.frag";
+			exposure_adaptation_vert_shader = "exposureAdaptation.vert";
 			geometry_pass_vert_shader = "geometryPass.vert";
 			geometry_pass_frag_shader = "geometryPass.frag";
 			geom_billboard_vert_shader = "geomBillboard.vert";
@@ -1404,9 +1419,11 @@ public:
 			bloom_upscale_comp_shader = "bloomUpscale.comp";
 			blur_pass_vert_shader = "blurPass.vert";
 			blur_pass_frag_shader = "blurPass.frag";
-			csm_pass_frag_shader = "csmPass.frag";
-			csm_pass_geom_shader = "csmPass.geom";
-			csm_pass_vert_shader = "csmPass.vert";
+			csm_pass_layered_frag_shader = "csmPassLayered.frag";
+			csm_pass_layered_geom_shader = "csmPassLayered.geom";
+			csm_pass_layered_vert_shader = "csmPassLayered.vert";
+			csm_pass_single_frag_shader = "csmPassSingle.frag";
+			csm_pass_single_vert_shader = "csmPassSingle.vert";
 			hbao_blur_horizontal_frag_shader = "ambientOcclusionBlurHBAOhorizontal.frag";
 			hbao_blur_horizontal_vert_shader = "ambientOcclusionBlurHBAO.vert";
 			hbao_blur_vertical_frag_shader = "ambientOcclusionBlurHBAOvertical.frag";
@@ -1417,10 +1434,8 @@ public:
 			lense_flare_comp_pass_frag_shader = "lenseFlareCompositePass.frag";
 			lense_flare_pass_vert_shader = "lenseFlarePass.vert";
 			lense_flare_pass_frag_shader = "lenseFlarePass.frag";
-			light_pass_csm_vert_shader = "lightPass_CSM.vert";
-			light_pass_csm_frag_shader = "lightPass_CSM.frag";
-			light_pass_vert_shader = "lightPass_CSM.vert";
-			light_pass_frag_shader = "lightPass_CSM.frag";
+			light_pass_vert_shader = "lightPass.vert";
+			light_pass_frag_shader = "lightPass.frag";
 			final_pass_vert_shader = "finalPass.vert";
 			final_pass_frag_shader = "finalPass.frag";
 			postProcess_pass_vert_shader = "postProcessPass.vert";
@@ -1462,13 +1477,15 @@ public:
 			fxaa_iterations = 12;
 			heightmap_combine_channel = 3;
 			heightmap_combine_texture = 1;
-			max_num_point_lights = 150;
-			max_num_spot_lights = 20;
+			max_num_point_lights = 450;
+			max_num_spot_lights = 50;
 			objects_loaded_per_frame = 1;
 			parallax_mapping_method = 5;
 			render_to_texture_buffer = GBufferTextureType::GBufferEmissive;
 			shader_pool_size = 10;
 			ssao_num_of_samples = 64;
+			csm_face_culling = true;
+			csm_front_face_culling = true;
 			depth_test = true;
 			face_culling = true;
 			fxaa_enabled = true;
@@ -1491,6 +1508,8 @@ public:
 		std::string spot_light_cone;
 		std::string stencil_pass_vert_shader;
 		std::string stencil_pass_frag_shader;
+		std::string exposure_adaptation_frag_shader;
+		std::string exposure_adaptation_vert_shader;
 		std::string geometry_pass_vert_shader;
 		std::string geometry_pass_frag_shader;
 		std::string geom_billboard_vert_shader;
@@ -1512,9 +1531,11 @@ public:
 		std::string bloom_upscale_comp_shader;
 		std::string blur_pass_vert_shader;
 		std::string blur_pass_frag_shader;
-		std::string csm_pass_frag_shader;
-		std::string csm_pass_geom_shader;
-		std::string csm_pass_vert_shader;
+		std::string csm_pass_layered_frag_shader;
+		std::string csm_pass_layered_geom_shader;
+		std::string csm_pass_layered_vert_shader;
+		std::string csm_pass_single_frag_shader;
+		std::string csm_pass_single_vert_shader;
 		std::string hbao_blur_horizontal_frag_shader;
 		std::string hbao_blur_horizontal_vert_shader;
 		std::string hbao_blur_vertical_frag_shader;
@@ -1525,8 +1546,6 @@ public:
 		std::string lense_flare_comp_pass_frag_shader;
 		std::string lense_flare_pass_vert_shader;
 		std::string lense_flare_pass_frag_shader;
-		std::string light_pass_csm_vert_shader;
-		std::string light_pass_csm_frag_shader;
 		std::string light_pass_vert_shader;
 		std::string light_pass_frag_shader;
 		std::string final_pass_vert_shader;
@@ -1577,6 +1596,8 @@ public:
 		int render_to_texture_buffer;
 		int shader_pool_size;
 		int ssao_num_of_samples;
+		bool csm_face_culling;
+		bool csm_front_face_culling;
 		bool depth_test;
 		bool face_culling;
 		bool fxaa_enabled;
@@ -1592,6 +1613,7 @@ public:
 			updateFunctionName = "update";
 			createObjectFunctionName = "create";
 			userTypeTableName = "Types";
+			luaUpdateErrorsEveryFrame = true;
 		}
 
 		std::string defaultScriptFilename;
@@ -1599,6 +1621,7 @@ public:
 		std::string updateFunctionName;
 		std::string createObjectFunctionName;
 		std::string userTypeTableName;
+		bool luaUpdateErrorsEveryFrame;
 	};
 	struct ShaderVariables
 	{
@@ -1745,6 +1768,7 @@ public:
 			define_shadowMapping = "SHADOW_MAPPING";
 			define_stochasticSampling = "STOCHASTIC_SAMPLING";
 			define_stochasticSamplingSeamFix = "STOCHASTIC_SAMPLING_MIPMAP_SEAM_FIX";
+			define_tonemappingMethod = "TONEMAPPING_METHOD";
 		}
 
 		std::string atmScatProjMatUniform;
@@ -1889,6 +1913,7 @@ public:
 		std::string define_shadowMapping;
 		std::string define_stochasticSampling;
 		std::string define_stochasticSamplingSeamFix;
+		std::string define_tonemappingMethod;
 	};
 	struct TextureVariables
 	{
@@ -1978,6 +2003,7 @@ public:
 			resizable = true;
 			vertical_sync = true;
 			window_in_focus = true;
+			window_position_centered = true;
 		}
 
 		std::string name;
@@ -1997,6 +2023,7 @@ public:
 		bool resizable;
 		bool vertical_sync;
 		bool window_in_focus;
+		bool window_position_centered;
 	};
 
 	const inline static AudioVariables		&audioVar()			{ return m_audioVar;		}

+ 183 - 114
Praxis3D/Source/Containers.h

@@ -7,97 +7,146 @@
 #include "Math.h"
 #include "PropertySet.h"
 
-// Stores all spatial data (position, rotation, scale)
-struct SpatialData
-{	
-	SpatialData() 
+// Stores two template objects to be used for double buffering
+template <class T_Object>
+struct DoubleBufferedContainer
+{
+	DoubleBufferedContainer()
 	{
-		clear();
+		m_swapFlag = false;
 	}
-	SpatialData(const glm::vec3 &p_position, const glm::vec3 &p_rotationEuler, const glm::vec3 &p_scale, const glm::quat &p_rotationQuat)
-		: m_position(p_position), m_scale(p_scale), m_rotationEuler(p_rotationEuler), m_rotationQuat(p_rotationQuat) { }
 
-	friend const inline SpatialData operator+(const SpatialData &p_left, const SpatialData &p_right)
-	{
-		return SpatialData(	p_left.m_position + p_right.m_position, 
-							p_left.m_rotationEuler + p_right.m_rotationEuler, 
-							p_left.m_scale + p_right.m_scale, 
-							p_left.m_rotationQuat * p_right.m_rotationQuat);
-	}
+	T_Object &getFront() { return m_buffers[(int)m_swapFlag]; }
+	T_Object &getBack() { return m_buffers[(int)!m_swapFlag]; }
 
-	const inline SpatialData operator+=(const SpatialData &p_data)
-	{
-		return SpatialData(	m_position + p_data.m_position,
-							m_rotationEuler + p_data.m_rotationEuler,
-							m_scale + p_data.m_scale,
-							m_rotationQuat * p_data.m_rotationQuat);
-	}
+	void swapBuffer() { m_swapFlag = !m_swapFlag; }
 
-	// Set all the data to default
-	void clear()
+	bool m_swapFlag;
+
+	T_Object m_buffers[2];
+};
+
+// Contains GUI settings for an editor window
+struct EditorWindowSettings
+{
+	EditorWindowSettings()
 	{
-		m_position = glm::vec3(0.0f);
-		m_rotationEuler = glm::vec3(0.0f);
-		m_scale = glm::vec3(1.0f);
-		m_rotationQuat = glm::quat();
+		m_enabled = true;
 	}
 
-	glm::vec3	m_position,
-				m_rotationEuler,
-				m_scale;
-	glm::quat m_rotationQuat;
+	bool m_enabled;
 };
 
-// Stores all spatial data (position, rotation, scale) plus the transform matrix
-struct SpatialTransformData
+// Stores an engine change type and all associated data needed for that change
+struct EngineChangeData
 {
-	SpatialTransformData() 
+	EngineChangeData(const EngineChangeType p_changeType = EngineChangeType::EngineChangeType_None, const EngineStateType p_engineStateType = EngineStateType::EngineStateType_Default) : m_changeType(p_changeType), m_engineStateType(p_engineStateType) { }
+	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const std::string &p_filename) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_filename(p_filename) { }
+	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const PropertySet &p_sceneProperties) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_sceneProperties(p_sceneProperties) { }
+	~EngineChangeData() { }
+
+	void setChangeType(const EngineChangeType p_changeType) { m_changeType = p_changeType; }
+	void setEngineStateType(const EngineStateType p_engineStateType) { m_engineStateType = p_engineStateType; }
+	void setFilename(const std::string &p_filename) { m_filename = p_filename; }
+
+	EngineChangeType m_changeType;
+	EngineStateType m_engineStateType;
+	std::string m_filename;
+	PropertySet m_sceneProperties;
+};
+
+// Contains Major, Minor and Patch engine version values
+struct EngineVersion
+{
+	EngineVersion() : m_major(-1), m_minor(-1), m_patch(-1) { }
+	EngineVersion(const int p_major, const int p_minor, const int p_patch) :
+		m_major(p_major), m_minor(p_minor), m_patch(p_patch) { }
+
+	void reset()
 	{
-		clear();
+		m_major = -1;
+		m_minor = -1;
+		m_patch = -1;
 	}
-	SpatialTransformData(const SpatialData &p_spatialData, const glm::mat4 &p_transformMat) : m_spatialData(p_spatialData), m_transformMatNoScale(p_transformMat) { }
 
-	friend const inline SpatialTransformData operator+(const SpatialTransformData &p_left, const SpatialTransformData &p_right)
+	inline int getVersionSingleFormat() const noexcept
 	{
-		return SpatialTransformData(p_left.m_spatialData + p_right.m_spatialData, p_left.m_transformMatNoScale * p_right.m_transformMatNoScale);
+		if(valid())
+			return (m_major * 1000000) + (m_minor * 1000) + m_patch;
+
+		return 0;
 	}
-	const inline SpatialTransformData operator+=(const SpatialTransformData &p_data)
-	{ 
-		return SpatialTransformData(m_spatialData + p_data.m_spatialData, m_transformMatNoScale * p_data.m_transformMatNoScale);
+
+	inline bool valid() const noexcept
+	{
+		if(m_major < 0)
+			return false;
+		if(m_minor < 0)
+			return false;
+		if(m_patch < 0)
+			return false;
+		return true;
 	}
 
-	// Set all the data to default
-	void clear()
+	inline explicit operator bool() const noexcept { return valid(); }
+
+	const inline bool operator==(const EngineVersion &p_version) const noexcept
 	{
-		m_spatialData.clear();
-		m_transformMatNoScale = glm::mat4(1.0f);
+		return (getVersionSingleFormat() == p_version.getVersionSingleFormat());
+	}
+	const inline bool operator<(const EngineVersion &p_version) const noexcept
+	{
+		return (getVersionSingleFormat() < p_version.getVersionSingleFormat());
+	}
+	const inline bool operator>(const EngineVersion &p_version) const noexcept
+	{
+		return (getVersionSingleFormat() > p_version.getVersionSingleFormat());
 	}
 
-	SpatialData m_spatialData;
-	glm::mat4 m_transformMatNoScale;
+	int m_major;
+	int m_minor;
+	int m_patch;
 };
 
-// Stores all GUI data
-struct GUIData
+// Stores an entity ID and a component type, used for sending data to create / delete components
+struct EntityAndComponent
 {
-	GUIData()
-	{
-		clear();
-	}	
+	//EntityAndComponent() : m_entityID(NULL_ENTITY_ID), m_componentType(ComponentType::ComponentType_None) { }
+	EntityAndComponent(const EntityID p_entityID, const ComponentType p_componentType) : m_entityID(p_entityID), m_componentType(p_componentType) { }
 
-	// Set all the data to default
-	void clear()
-	{
-		m_functors.clear();
-	}
+	EntityID m_entityID;
+	ComponentType m_componentType;
+};
+
+// Stores settings for face culling
+struct FaceCullingSettings
+{
+	FaceCullingSettings() : m_faceCullingEnabled(true), m_backFaceCulling(true) { }
+	FaceCullingSettings(const bool p_faceCullingEnabled, const bool p_backFaceCulling) : m_faceCullingEnabled(p_faceCullingEnabled), m_backFaceCulling(p_backFaceCulling) { }
+
+	bool m_faceCullingEnabled;
+	bool m_backFaceCulling;
+};
+
+// Stores a vector of std::functions (Functors) and methods to add, clear and get them
+struct FunctorSequence
+{
+	FunctorSequence() { }
+
+	template<typename Functor>
+	inline void addFunctor(Functor p_functor) { m_functors.push_back(p_functor); }
+
+	const Functors &getFunctors() const { return m_functors; }
+
+	void clear() { m_functors.clear(); }
 
 	Functors m_functors;
 };
 
-// Stores all physics data
-struct PhysicsData
+// Stores all GUI data
+struct GUIData
 {
-	PhysicsData()
+	GUIData()
 	{
 		clear();
 	}
@@ -105,8 +154,10 @@ struct PhysicsData
 	// Set all the data to default
 	void clear()
 	{
-
+		m_functors.clear();
 	}
+
+	Functors m_functors;
 };
 
 // Packs a single unsigned 64bit int into two unsigned 32bit ints
@@ -126,79 +177,97 @@ struct Int64Packer
 	unsigned __int32 x, y;
 };
 
-// Contains GUI settings for an editor window
-struct EditorWindowSettings
+// Stores all physics data
+struct PhysicsData
 {
-	EditorWindowSettings()
+	PhysicsData()
 	{
-		m_enabled = true;
+		clear();
 	}
 
-	bool m_enabled;
-};
-
-// Stores a vector of std::functions (Functors) and methods to add, clear and get them
-struct FunctorSequence
-{
-	FunctorSequence() { }
-
-	template<typename Functor>
-	inline void addFunctor(Functor p_functor) { m_functors.push_back(p_functor); }
-
-	const Functors &getFunctors() const { return m_functors; }
-
-	void clear() { m_functors.clear(); }
+	// Set all the data to default
+	void clear()
+	{
 
-	Functors m_functors;
+	}
 };
 
-// Stores two template objects to be used for double buffering
-template <class T_Object>
-struct DoubleBufferedContainer
-{
-	DoubleBufferedContainer() 
+// Stores all spatial data (position, rotation, scale)
+struct SpatialData
+{	
+	SpatialData() 
 	{
-		m_swapFlag = false;
+		clear();
 	}
+	SpatialData(const glm::vec3 &p_position, const glm::vec3 &p_rotationEuler, const glm::vec3 &p_scale, const glm::quat &p_rotationQuat)
+		: m_position(p_position), m_scale(p_scale), m_rotationEuler(p_rotationEuler), m_rotationQuat(p_rotationQuat) { }
 
-	T_Object &getFront() { return m_buffers[(int)m_swapFlag]; }
-	T_Object &getBack() { return m_buffers[(int)!m_swapFlag]; }
+	friend const inline SpatialData operator+(const SpatialData &p_left, const SpatialData &p_right)
+	{
+		return SpatialData(	p_left.m_position + p_right.m_position, 
+							p_left.m_rotationEuler + p_right.m_rotationEuler, 
+							p_left.m_scale + p_right.m_scale, 
+							p_left.m_rotationQuat * p_right.m_rotationQuat);
+	}
 
-	void swapBuffer() { m_swapFlag = !m_swapFlag; }
+	const inline SpatialData operator+=(const SpatialData &p_data)
+	{
+		return SpatialData(	m_position + p_data.m_position,
+							m_rotationEuler + p_data.m_rotationEuler,
+							m_scale + p_data.m_scale,
+							m_rotationQuat * p_data.m_rotationQuat);
+	}
 
-	bool m_swapFlag;
+	// Set all the data to default
+	void clear()
+	{
+		m_position = glm::vec3(0.0f);
+		m_rotationEuler = glm::vec3(0.0f);
+		m_scale = glm::vec3(1.0f);
+		m_rotationQuat = glm::quat();
+	}
 
-	T_Object m_buffers[2];
+	glm::vec3	m_position,
+				m_rotationEuler,
+				m_scale;
+	glm::quat m_rotationQuat;
 };
 
-// Stores an engine change type and all associated data needed for that change
-struct EngineChangeData
+// Stores all spatial data (position, rotation, scale) plus the transform matrix
+struct SpatialTransformData
 {
-	EngineChangeData(const EngineChangeType p_changeType = EngineChangeType::EngineChangeType_None, const EngineStateType p_engineStateType = EngineStateType::EngineStateType_Default) : m_changeType(p_changeType), m_engineStateType(p_engineStateType) { }
-	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const std::string &p_filename) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_filename(p_filename) { }
-	EngineChangeData(const EngineChangeType p_changeType, const EngineStateType p_engineStateType, const PropertySet &p_sceneProperties) : m_changeType(p_changeType), m_engineStateType(p_engineStateType), m_sceneProperties(p_sceneProperties) { }
-	~EngineChangeData() { }
-
-	void setChangeType(const EngineChangeType p_changeType) { m_changeType = p_changeType; }
-	void setEngineStateType(const EngineStateType p_engineStateType) { m_engineStateType = p_engineStateType; }
-	void setFilename(const std::string &p_filename) { m_filename = p_filename; }
+	SpatialTransformData() 
+	{
+		clear();
+	}
+	SpatialTransformData(const SpatialData &p_spatialData, const glm::mat4 &p_transformMat) : m_spatialData(p_spatialData), m_transformMatNoScale(p_transformMat) { }
 
-	EngineChangeType m_changeType;
-	EngineStateType m_engineStateType;
-	std::string m_filename;
-	PropertySet m_sceneProperties;
-};
+	friend const inline SpatialTransformData operator+(const SpatialTransformData &p_left, const SpatialTransformData &p_right)
+	{
+		return SpatialTransformData(p_left.m_spatialData + p_right.m_spatialData, p_left.m_transformMatNoScale * p_right.m_transformMatNoScale);
+	}
+	const inline SpatialTransformData operator+=(const SpatialTransformData &p_data)
+	{ 
+		return SpatialTransformData(m_spatialData + p_data.m_spatialData, m_transformMatNoScale * p_data.m_transformMatNoScale);
+	}
 
-// Stores an entity ID and a component type, used for sending data to create / delete components
-struct EntityAndComponent
-{
-	//EntityAndComponent() : m_entityID(NULL_ENTITY_ID), m_componentType(ComponentType::ComponentType_None) { }
-	EntityAndComponent(const EntityID p_entityID, const ComponentType p_componentType) : m_entityID(p_entityID), m_componentType(p_componentType) { }
+	// Set all the data to default
+	void clear()
+	{
+		m_spatialData.clear();
+		m_transformMatNoScale = glm::mat4(1.0f);
+	}
 
-	EntityID m_entityID;
-	ComponentType m_componentType;
+	SpatialData m_spatialData;
+	glm::mat4 m_transformMatNoScale;
 };
 
+/*	 _______________________________
+	|								|
+	|   Renderer data containers:	|
+	|_______________________________|
+*/
+
 // Stores settings for atmospheric light scattering
 struct AtmosphericScatteringData
 {

+ 10 - 10
Praxis3D/Source/DeferredRenderer.cpp

@@ -94,21 +94,21 @@ ErrorCode DeferredRenderer::init()
 		m_fullscreenTriangle.load();
 
 		// Enable / disable face culling
-		if(Config::rendererVar().face_culling)
-			glEnable(GL_CULL_FACE);
-		else
-			glDisable(GL_CULL_FACE);
+		//if(Config::rendererVar().face_culling)
+		//	glEnable(GL_CULL_FACE);
+		//else
+		//	glDisable(GL_CULL_FACE);
 
 		// Enable / disable depth test
-		if(Config::rendererVar().depth_test)
-			glEnable(GL_DEPTH_TEST);
-		else
-			glDisable(GL_DEPTH_TEST);
+		//if(Config::rendererVar().depth_test)
+		//	glEnable(GL_DEPTH_TEST);
+		//else
+		//	glDisable(GL_DEPTH_TEST);
 
-		glDepthFunc(GL_LESS);
+		//glDepthFunc(GL_LESS);
 
 		// Set face culling mode
-		glCullFace(Config::rendererVar().face_culling_mode);
+		//glCullFace(Config::rendererVar().face_culling_mode);
 
 		// Set depth test function
 		glDepthFunc(Config::rendererVar().depth_test_func);

File diff suppressed because it is too large
+ 2161 - 1907
Praxis3D/Source/EditorWindow.cpp


+ 153 - 2
Praxis3D/Source/EditorWindow.h

@@ -62,8 +62,11 @@ public:
 
 		m_newEntityConstructionInfo = nullptr;
 		m_openNewEntityPopup = false;
+		m_openEntityRightClickOptionsPopup = false;
 		m_duplicateParent = false;
 
+		resetActivateAllComponentFlags();
+
 		m_newSceneSettingsTabFlags = 0;
 
 		m_fontSize = ImGui::GetFontSize();
@@ -82,12 +85,16 @@ public:
 		m_2DTextureScale = true;
 		m_2DTextureFraming = false;
 
+		m_numOfLogs = 0;
+
 		for(unsigned int i = 0; i < ObjectMaterialType::NumberOfMaterialTypes; i++)
 			m_physicalMaterialProperties.push_back(GetString(static_cast<ObjectMaterialType>(i)));
 
 		for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
 			m_renderingPassesTypeText.push_back(GetString(static_cast<RenderPassType>(i)));
 
+		m_entityRightClickOptionsPopup = "EntityRightClickOptionsPopup";
+
 		m_antialiasingTypeText = { "None", "MSAA", "FXAA" };
 		m_ambientOcclusionTypeText = { "None", "SSAO", "HBAO" };
 		m_cascadeDistanceTypeText = { "Units", "Divider" };
@@ -524,6 +531,9 @@ private:
 	enum MainMenuButtonType : unsigned int
 	{
 		MainMenuButtonType_None = 0,
+		MainMenuButtonType_Play,
+		MainMenuButtonType_Pause,
+		MainMenuButtonType_Restart,
 		MainMenuButtonType_New,
 		MainMenuButtonType_Open,
 		MainMenuButtonType_Save,
@@ -799,8 +809,9 @@ private:
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Lighting);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
-			m_renderingPasses.push_back(RenderPassType::RenderPassType_Bloom);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Luminance);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Bloom);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_Tonemapping);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_GUI);
 
@@ -810,6 +821,7 @@ private:
 		// General
 		bool m_loadInBackground;
 		bool m_modified;
+		std::string m_sceneFilename;
 
 		// Audio scene
 		std::vector<std::string> m_audioBanks;
@@ -899,6 +911,7 @@ private:
 	};
 
 	void drawSceneData(SceneData &p_sceneData, const bool p_sendChanges = false);
+	void drawEntityHierarchy(EntityHierarchyEntry *p_rootEntry);
 	void drawEntityHierarchyEntry(EntityHierarchyEntry *p_entityEntry);
 	inline void drawLeftAlignedLabelText(const char *p_labelText, float p_nextWidgetOffset)
 	{
@@ -959,7 +972,27 @@ private:
 
 		return returnBool;
 	}
-	
+	inline void drawExportPrefabFileBrowser()
+	{
+		// Only open the file browser if it's not opened already
+		if(m_currentlyOpenedFileBrowser == FileBrowserActivated::FileBrowserActivated_None)
+		{
+			// Set the file browser activation to Save Scene
+			m_currentlyOpenedFileBrowser = FileBrowserActivated::FileBrowserActivated_SavePrefabFile;
+
+			// Define file browser variables
+			m_fileBrowserDialog.m_definedFilename = m_selectedEntity.m_componentData.m_name + ".prefab";
+			m_fileBrowserDialog.m_filter = ".prefab,.*";
+			m_fileBrowserDialog.m_title = "Save prefab";
+			m_fileBrowserDialog.m_name = "SavePrefabFileDialog";
+			m_fileBrowserDialog.m_rootPath = Config::filepathVar().prefab_path;
+			m_fileBrowserDialog.m_flags = FileBrowserDialog::FileBrowserDialogFlags::FileBrowserDialogFlags_ConfirmOverwrite;
+
+			// Tell the GUI scene to open the file browser
+			m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene, DataType::DataType_FileBrowserDialog, (void *)&m_fileBrowserDialog);
+		}
+	}
+
 	// Hides and locks the mouse to the screen while an item (like a float drag) is active
 	// Returns the mouse to the original position after the mouse button is released
 	void captureMouseWhileItemActive();
@@ -1001,6 +1034,12 @@ private:
 			m_componentConstructionInfoPool.clear();
 		}
 	}
+	inline void resetActivateAllComponentFlags()
+	{
+		m_lightComponentActivateAllSet = false;
+		m_modelComponentActivateAllSet = false;
+		m_rigidBodyComponentActivateAllSet = false;
+	}
 
 	void exportPrefab(const EntityID p_entityID, std::string p_filename);
 	void saveTextFile(TextEditorData &p_textEditorData);
@@ -1012,6 +1051,108 @@ private:
 	void updateComponentList();
 	void updateAssetLists();
 
+	void duplicateEntity(const EntityID p_entityID)
+	{
+		// Construction info for the new entity
+		ComponentsConstructionInfo *newEntityConstructionInfo = new ComponentsConstructionInfo();
+
+		// Get the construction info of the current entity
+		WorldScene *worldScene = static_cast<WorldScene *>(m_systemScene->getSceneLoader()->getSystemScene(Systems::World));
+		worldScene->exportEntity(p_entityID, *newEntityConstructionInfo);
+
+		// Set the new entity name by adding a count at the end and checking if an entity of the same name doesn't exist
+		{
+			int newEntityNameCount = 2;
+			std::string baseEntityName = newEntityConstructionInfo->m_name;
+
+			// If the entity name ends with ' 1', replace the existing number
+			if(baseEntityName.size() > 2 && baseEntityName[baseEntityName.size() - 1] == '1' && baseEntityName[baseEntityName.size() - 2] == ' ')
+			{
+				baseEntityName.pop_back();  // Remove '1'
+				baseEntityName.pop_back();  // Remove ' '
+			}
+			std::string newEntityName = baseEntityName + " " + Utilities::toString(newEntityNameCount);
+
+			// Keep increasing the number at the end until there is no other entity with the same name
+			while(worldScene->getEntity(newEntityName) != NULL_ENTITY_ID)
+			{
+				newEntityNameCount++;
+				newEntityName = baseEntityName + " " + Utilities::toString(newEntityNameCount);
+			}
+			newEntityConstructionInfo->m_name = newEntityName;
+		}
+
+		// Assign a next available entity ID (start the available ID search from the next ID after the parent)
+		{
+			EntityID newEntityID = newEntityConstructionInfo->m_parent + 1;
+			for(decltype(m_entityList.size()) i = 0, size = m_entityList.size(); i < size;)
+			{
+				if(newEntityID == m_entityList[i].m_entityID)
+				{
+					newEntityID++;
+					i = 0;
+				}
+				else
+					i++;
+			}
+			newEntityConstructionInfo->m_id = newEntityID;
+		}
+
+
+		m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::World), DataType::DataType_CreateEntity, (void *)newEntityConstructionInfo, false);
+
+		// Make the new entity be selected next frame
+		m_nextEntityIDToSelect = newEntityConstructionInfo->m_id;
+		m_pendingEntityToSelect = true;
+
+		// Add the new entity construction info to the pool (so it will be deleted the next frame)
+		m_componentConstructionInfoPool.push_back(newEntityConstructionInfo);
+	}
+	void deleteEntityAndChildren(const EntityID p_entityID)
+	{
+		// Get entity children
+		std::vector<EntityID> childrenEntityIDs;
+		if(p_entityID != 0)
+			childrenEntityIDs.push_back(p_entityID);
+		if(auto *entityListEntry = getEntityListEntry(p_entityID); entityListEntry != nullptr)
+			getEntityChildren(childrenEntityIDs, *entityListEntry);
+
+		// Do not allow the deletion of root entity
+		for(decltype(childrenEntityIDs.size()) i = 0, size = childrenEntityIDs.size(); i < size; i++)
+		{
+			// Create a container with the entity ID and the component type, add it to the pool (so it can be deleted next frame) and send a Delete Entity change with the attached container
+			EntityAndComponent *deleteComponentData = new EntityAndComponent(childrenEntityIDs[i], ComponentType::ComponentType_Entity);
+			m_entityAndComponentPool.push_back(deleteComponentData);
+			m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::World), DataType::DataType_DeleteEntity, (void *)deleteComponentData, false);
+		}
+	}
+	EntityListEntry *getEntityListEntry(const EntityID p_entityID)
+	{
+		for(decltype(m_entityList.size()) i = 0, size = m_entityList.size(); i < size; i++)
+		{
+			if(m_entityList[i].m_entityID == p_entityID)
+			{
+				return &m_entityList[i];
+			}
+		}
+
+		return nullptr;
+	}
+	inline void getEntityChildren(std::vector<EntityID> &p_children, const EntityListEntry &p_entityHierarchyEntry)
+	{
+		for(decltype(m_entityList.size()) i = 0, size = m_entityList.size(); i < size; i++)
+		{
+			if(m_entityList[i].m_parentEntityID == p_entityHierarchyEntry.m_entityID)
+			{
+				p_children.push_back(m_entityList[i].m_entityID);
+
+				// Do not process the root node as it will cause an infinite loop
+				if(p_entityHierarchyEntry.m_entityID != 0)
+					getEntityChildren(p_children, m_entityList[i]);
+			}
+		}
+	}
+
 	void generateNewMap(PropertySet &p_newSceneProperties, SceneData &p_sceneData);
 
 	inline std::string getTextureFormatString(const TextureFormat p_textureFormat) const
@@ -1215,6 +1356,9 @@ private:
 	bool m_2DTextureScale;
 	bool m_2DTextureFraming;
 
+	// Console settings
+	std::size_t m_numOfLogs;
+
 	// Assets variables
 	std::vector<std::pair<const Texture2D *, std::string>> m_textureAssets;
 	std::vector<std::pair<Model *, std::string>> m_modelAssets;
@@ -1249,7 +1393,11 @@ private:
 	std::vector<ComponentsConstructionInfo*> m_componentConstructionInfoPool;
 	ComponentsConstructionInfo *m_newEntityConstructionInfo;
 	bool m_openNewEntityPopup;
+	bool m_openEntityRightClickOptionsPopup;
 	bool m_duplicateParent;
+	bool m_lightComponentActivateAllSet;
+	bool m_modelComponentActivateAllSet;
+	bool m_rigidBodyComponentActivateAllSet;
 
 	// New scene settings
 	SceneData m_newSceneData;
@@ -1265,6 +1413,9 @@ private:
 	// Square button size that is the same height as text
 	ImVec2 m_buttonSizedByFont;
 
+	// Names for ImGui widgets
+	const char *m_entityRightClickOptionsPopup;
+
 	// String arrays and other data used for ImGui Combo inputs
 	std::vector<const char *> m_antialiasingTypeText;
 	std::vector<const char *> m_ambientOcclusionTypeText;

+ 6 - 0
Praxis3D/Source/Engine.cpp

@@ -3,6 +3,8 @@
 #include <sstream>
 
 #include <iostream>
+#include <stdlib.h>     /* srand, rand */
+#include <time.h>       /* time */
 
 #include "AudioSystem.h"
 #include "ClockLocator.h"
@@ -43,6 +45,10 @@ Engine::Engine()
 	m_objectChangeController = nullptr;
 	m_currentStateType = Config::engineVar().engineState;
 
+	// Seed the random number generator
+	srand((unsigned int)time(NULL));
+	rand();
+
 	for(unsigned int i = 0; i < EngineStateType::EngineStateType_NumOfTypes; i++)
 		m_engineStates[i] = nullptr;
 

+ 5 - 2
Praxis3D/Source/EngineDefinitions.h

@@ -3,8 +3,8 @@
 // Affects engine only when recompiled, to eliminate run-time checks.
 
 // Enable multithreading
-#define SETTING_MULTITHREADING_ENABLED
-#define SETTING_ATOMIC_VARIABLES_ENABLED
+#define SETTING_MULTITHREADING_ENABLED 1
+#define SETTING_ATOMIC_VARIABLES_ENABLED 1
 
 // Use glBlitFramebuffer to copy the final buffer to the default back-buffer, instead of rendering a full-screen triangle
 //#define SETTING_USE_BLIT_FRAMEBUFFER
@@ -19,5 +19,8 @@
 #define NUM_DYNAMIC_COLLISION_EVENTS 100u
 #define NUM_STATIC_COLLISION_EVENTS 200u
 
+// Shadow mapping settings
+#define CSM_USE_MULTILAYER_DRAW 1
+
 // Loaders settings
 #define SETTING_LOADER_RESERVE_SIZE 200

+ 4 - 0
Praxis3D/Source/ErrorCodes.h

@@ -65,6 +65,10 @@ DECLARE_ENUM(ErrorType, ERROR_TYPES)
 	Code(Universal_scene_extend_null,) \
 	Code(Universal_scene_extend_duplicate,) \
 	Code(Window_handle_missing,) \
+	/* LUA errors */ \
+	Code(Lua_init_func_failed,) \
+	Code(Lua_load_script_failed,) \
+	Code(Lua_update_func_failed,) \
 	/* Model loader errors */ \
 	Code(AssimpScene_failed,) \
 	/* Object pool errors */ \

+ 46 - 37
Praxis3D/Source/ErrorHandler.cpp

@@ -45,6 +45,9 @@ ErrorHandler::ErrorHandler()
 	AssignErrorType(Universal_scene_extend_null, Error);
 	AssignErrorType(Universal_scene_extend_duplicate, Error);
 	AssignErrorType(Window_handle_missing, Error);
+	AssignErrorType(Lua_init_func_failed, Warning);
+	AssignErrorType(Lua_load_script_failed, Warning);
+	AssignErrorType(Lua_update_func_failed, Warning);
 	AssignErrorType(AssimpScene_failed, Error);
 	AssignErrorType(ObjectPool_full, Warning); 
 	AssignErrorType(Collision_invalid, Warning);
@@ -122,6 +125,9 @@ ErrorCode ErrorHandler::init()
 		for(int i = ErrorCode::NumberOfErrorCodes + ErrorSource::Source_NumberOfErrorSources,
 			size = ErrorSource::Source_NumberOfErrorSources + ErrorCode::NumberOfErrorCodes + ErrorType::NumberOfErrorTypes; i < size; i++)
 			m_errorTypeStrings[i - ErrorCode::NumberOfErrorCodes - ErrorSource::Source_NumberOfErrorSources] = it.getValue(i).getString();
+
+		if(Config::engineVar().log_store_logs)
+			m_logData.setMaxLogs(Config::engineVar().log_max_num_of_logs);
 	}
 
 	return ErrorCode::Success;
@@ -152,43 +158,46 @@ void ErrorHandler::log(ErrorType p_errorType, ErrorSource p_errorSource, std::st
 		std::string displayMessage;
 		switch(p_errorType)
 		{
-		case ErrorType::Info:
-		{
-			m_console->displayMessage("\033[1;32m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
-			break;
-		}
-
-		case ErrorType::Warning:
-		{
-			m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
-			break;
-		}
-
-		case ErrorType::Error:
-		{
-			// Remove a 'new line' character if it's present, as it would break the formating
-			if(p_error[p_error.size() - 1] == '\n')
-				p_error.pop_back();
-
-			// TODO make the error question data driven
-			if(!WindowLocator().get().spawnYesNoErrorBox(m_errorTypeStrings[p_errorType] + ": " + m_errorSources[p_errorSource], m_errorSources[p_errorSource] + ": " + p_error + ".\n\nWould you like to continue?"))
-				Config::m_engineVar.running = false;
-
-			//m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] [" + m_errorSources[p_errorSource] + "]: " + p_error + ".\033[0m");
-			m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
-			break;
-		}
-
-		case ErrorType::FatalError:
-		{
-			WindowLocator().get().spawnErrorBox(m_errorTypeStrings[p_errorType] + ": " + m_errorSources[p_errorSource], m_errorSources[p_errorSource] + ": " + p_error + ".");
-			Config::m_engineVar.running = false;
-
-			break;
-		}
-
-		default:
-			break;
+			case ErrorType::Info:
+				{
+					m_logData.addLogMessage(p_errorType, p_errorSource, p_error);
+					m_console->displayMessage("\033[1;32m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
+					break;
+				}
+
+			case ErrorType::Warning:
+				{
+					m_logData.addLogMessage(p_errorType, p_errorSource, p_error);
+					m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
+					break;
+				}
+
+			case ErrorType::Error:
+				{
+					// Remove a 'new line' character if it's present, as it would break the formating
+					if(p_error[p_error.size() - 1] == '\n')
+						p_error.pop_back();
+
+					// TODO make the error question data driven
+					if(!WindowLocator().get().spawnYesNoErrorBox(m_errorTypeStrings[p_errorType] + ": " + m_errorSources[p_errorSource], m_errorSources[p_errorSource] + ": " + p_error + ".\n\nWould you like to continue?"))
+						Config::m_engineVar.running = false;
+
+					m_logData.addLogMessage(p_errorType, p_errorSource, p_error);
+					m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
+					break;
+				}
+
+			case ErrorType::FatalError:
+				{
+					m_logData.addLogMessage(p_errorType, p_errorSource, p_error);
+					WindowLocator().get().spawnErrorBox(m_errorTypeStrings[p_errorType] + ": " + m_errorSources[p_errorSource], m_errorSources[p_errorSource] + ": " + p_error + ".");
+					Config::m_engineVar.running = false;
+
+					break;
+				}
+
+			default:
+				break;
 		}
 	}
 }

+ 66 - 5
Praxis3D/Source/ErrorHandler.h

@@ -2,10 +2,54 @@
 
 #include <iostream>
 #include <string>
+#include <tbb/concurrent_vector.h>
 
 #include "Config.h"
 #include "ErrorCodes.h"
 
+struct SingleLog
+{
+	SingleLog(const ErrorType p_logType, const ErrorSource p_logSource, const std::string &p_logMessage) :
+		m_logType(p_logType), m_logSource(p_logSource), m_logMessage(p_logMessage) { }
+
+	ErrorType m_logType;
+	ErrorSource m_logSource;
+	std::string m_logMessage;
+};
+
+// Stores the logging strings
+struct LogData
+{
+	LogData() : m_maxLogs(0) { }
+	LogData(tbb::concurrent_vector<std::string>::size_type p_maxLogs)
+	{
+		setMaxLogs(p_maxLogs);
+	}
+
+	void addLogMessage(const ErrorType p_logType, const ErrorSource p_logSource, const std::string &p_logMessage)
+	{
+		m_logs.emplace_back(p_logType, p_logSource, p_logMessage);
+	}
+
+	void addLogMessage(const SingleLog &p_log)
+	{
+		m_logs.emplace_back(p_log);
+	}
+
+	void setMaxLogs(int p_maxLogs)
+	{
+		setMaxLogs((tbb::concurrent_vector<std::string>::size_type)p_maxLogs);
+	}
+	void setMaxLogs(tbb::concurrent_vector<std::string>::size_type p_maxLogs)
+	{
+		m_maxLogs = p_maxLogs;
+		m_logs.reserve(p_maxLogs);
+	}
+
+	tbb::concurrent_vector<SingleLog> m_logs;
+	tbb::concurrent_vector<std::string>::size_type m_maxLogs;
+};
+
 class ErrorHandlerBase
 {
 public:
@@ -15,11 +59,15 @@ public:
 	virtual ErrorCode init() = 0;
 
 	// Get a pre-define text string of an error code
-	virtual std::string getErrorString(ErrorCode p_error) = 0;
+	const virtual std::string &getErrorString(ErrorCode p_error) = 0;
+	// Get a pre-define text string of an error type
+	const virtual std::string &getErrorTypeString(ErrorType p_error) = 0;
+	// Get a pre-define text string of an error source
+	const virtual std::string &getErrorSourceString(ErrorSource p_error) = 0;
 	// Get a pre-defined error type of an error code
 	virtual ErrorType getErrorType(ErrorCode p_error) = 0;
 
-	// Error logging functions, passes the error to the apropriate "console" class
+	// Error logging functions, passes the error to the appropriate "console" class
 	virtual void log(ErrorCode p_errorCode) = 0;
 	virtual void log(ErrorCode p_errorCode, ErrorSource p_errorSource) = 0;
 	virtual void log(ErrorType p_errorType, ErrorSource p_errorSource, std::string p_error) = 0;
@@ -33,6 +81,12 @@ public:
 
 	// Assigns the p_errorCode to the p_returnCode; Returns true if the p_errorCode was successful
 	const inline bool ifSuccessful(ErrorCode p_errorCode, ErrorCode &p_returnCode) const { p_returnCode = p_errorCode; return p_errorCode == ErrorCode::Success; }
+
+	// Returns log data containing all logged strings
+	const inline LogData &getLogData() const { return m_logData; }
+
+protected:
+	LogData m_logData;
 };
 
 class ErrorHandler : public ErrorHandlerBase
@@ -42,7 +96,10 @@ public:
 	~ErrorHandler();
 
 	ErrorCode init();
-	inline std::string getErrorString(ErrorCode p_error) { return (p_error < ErrorCode::NumberOfErrorCodes) ? m_errorData[p_error].m_errorString : "Invalid error code."; }
+
+	const inline std::string &getErrorString(ErrorCode p_error) { return (p_error < ErrorCode::NumberOfErrorCodes) ? m_errorData[p_error].m_errorString : m_errorData[ErrorCode::Undefined].m_errorString; }
+	const inline std::string &getErrorTypeString(ErrorType p_errorType) { return (p_errorType < ErrorType::NumberOfErrorTypes) ? m_errorTypeStrings[p_errorType] : m_errorTypeStrings[ErrorType::Info]; }
+	const inline std::string &getErrorSourceString(ErrorSource p_errorSource) { return (p_errorSource < ErrorSource::Source_NumberOfErrorSources) ? m_errorSources[p_errorSource] : m_errorSources[ErrorSource::Source_Unknown]; }
 	inline ErrorType getErrorType(ErrorCode p_error) { return (p_error < ErrorCode::NumberOfErrorCodes) ? m_errorData[p_error].m_errorType : ErrorType::Info; }
 
 	void log(ErrorCode p_errorCode);
@@ -144,7 +201,9 @@ public:
 
 	ErrorCode init() { return ErrorCode::Success; }
 
-	std::string getErrorString(ErrorCode p_error) { return ""; }
+	const std::string &getErrorString(ErrorCode p_error) { return m_emptyString; }
+	const std::string &getErrorTypeString(ErrorType p_error) { return m_emptyString; }
+	const std::string &getErrorSourceString(ErrorSource p_error) { return m_emptyString;}
 	ErrorType getErrorType(ErrorCode p_error) { return ErrorType::Warning; }
 
 	void log(ErrorCode p_errorCode) { printf("Error: %i.\n", p_errorCode); }
@@ -154,5 +213,7 @@ public:
 	void log(const ErrorCode p_errorCode, const std::string &p_objectName, const ErrorSource p_errorSource) { printf("Error: %s: %i.\n", p_objectName.c_str(), p_errorCode); }
 
 	bool ifSuccessful(ErrorCode p_errorCode, ErrorCode &p_returnCode) { p_returnCode = p_errorCode; return p_errorCode == ErrorCode::Success; }
-	//ErrorCode cacheError(ErrorCode p_errorCode, std::string p_error) { return ErrorCode::Undefined; }
+
+private:
+	std::string m_emptyString;
 };

+ 10 - 8
Praxis3D/Source/FinalPass.h

@@ -6,7 +6,9 @@
 class FinalPass : public RenderPass
 {
 public:
-	FinalPass(RendererFrontend &p_renderer) : RenderPass(p_renderer, RenderPassType::RenderPassType_Final) 
+	FinalPass(RendererFrontend &p_renderer) : 
+		RenderPass(p_renderer, RenderPassType::RenderPassType_Final),
+		m_shaderFinalPass(nullptr)
 	{
 		updateFxaaSettings();
 	}
@@ -82,7 +84,7 @@ public:
 			m_renderer.passLoadCommandsToBackend();
 		}
 
-		glDepthFunc(GL_LEQUAL);
+		//glDepthFunc(GL_LEQUAL);
 		glDisable(GL_DEPTH_TEST);
 		
 		// Bind final texture for reading so it can be accessed in the shaders
@@ -115,27 +117,27 @@ private:
 		if(shaderError == ErrorCode::Success)
 		{
 			// Enable or disable the FXAA
-			if(ErrorCode shaderVariableError = m_shaderFinalPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa, m_fxaaEnabled ? 1 : 0); shaderVariableError != ErrorCode::Success)
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa, m_fxaaEnabled ? 1 : 0); shaderVariableError != ErrorCode::Success)
 				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_fxaa, ErrorSource::Source_FinalPass);
 
 			// Set the minimum FXAA edge threshold
-			if(ErrorCode shaderVariableError = m_shaderFinalPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_edge_threshold_min, m_fxaaEdgeThresholdMin); shaderVariableError != ErrorCode::Success)
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_edge_threshold_min, m_fxaaEdgeThresholdMin); shaderVariableError != ErrorCode::Success)
 				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_fxaa_edge_threshold_min, ErrorSource::Source_FinalPass);
 
 			// Set the maximum FXAA edge threshold
-			if(ErrorCode shaderVariableError = m_shaderFinalPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_edge_threshold_max, m_fxaaEdgeThresholdMax); shaderVariableError != ErrorCode::Success)
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_edge_threshold_max, m_fxaaEdgeThresholdMax); shaderVariableError != ErrorCode::Success)
 				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_fxaa_edge_threshold_max, ErrorSource::Source_FinalPass);
 
 			// Set the number of FXAA iterations
-			if(ErrorCode shaderVariableError = m_shaderFinalPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_iterations, m_fxaaIterations); shaderVariableError != ErrorCode::Success)
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_iterations, m_fxaaIterations); shaderVariableError != ErrorCode::Success)
 				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_fxaa_iterations, ErrorSource::Source_FinalPass);
 
 			// Set the FXAA sub-pixel quality
-			if(ErrorCode shaderVariableError = m_shaderFinalPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_subpixel_quality, m_fxaaSubpixelQuality); shaderVariableError != ErrorCode::Success)
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_fxaa_subpixel_quality, m_fxaaSubpixelQuality); shaderVariableError != ErrorCode::Success)
 				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_fxaa_subpixel_quality, ErrorSource::Source_FinalPass);
 
 			// Queue the shader to be loaded to GPU
-			m_renderer.queueForLoading(*m_shaderFinalPass);
+			m_renderer.queueForLoading(*p_shader);
 		}
 
 		return shaderError;

+ 74 - 0
Praxis3D/Source/GUISystem.cpp

@@ -0,0 +1,74 @@
+#include "GUISystem.h"
+
+void GUISystem::setColorPallet(const GuiColorPallet p_colorPallet)
+{
+	ImGuiStyle *style = &ImGui::GetStyle();
+	ImVec4 *colors = style->Colors;
+
+	switch(p_colorPallet)
+	{
+		case GuiColorPallet_Default:
+		default:
+			// Leave the default ImGui colors
+			break;
+		case GuiColorPallet_Green:
+			{
+				colors[ImGuiCol_Text]					= ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
+				colors[ImGuiCol_TextDisabled]			= ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
+				colors[ImGuiCol_WindowBg]				= ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
+				colors[ImGuiCol_ChildBg]				= ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
+				colors[ImGuiCol_PopupBg]				= ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
+				colors[ImGuiCol_Border]					= ImVec4(0.05f, 0.05f, 0.05f, 0.50f);
+				colors[ImGuiCol_BorderShadow]			= ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
+				colors[ImGuiCol_FrameBg]				= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_FrameBgHovered]			= ImVec4(0.42f, 0.55f, 0.21f, 0.80f);
+				colors[ImGuiCol_FrameBgActive]			= ImVec4(0.42f, 0.55f, 0.21f, 1.00f);
+				colors[ImGuiCol_TitleBg]				= ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
+				colors[ImGuiCol_TitleBgActive]			= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_TitleBgCollapsed]		= ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
+				colors[ImGuiCol_MenuBarBg]				= ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
+				colors[ImGuiCol_ScrollbarBg]			= ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
+				colors[ImGuiCol_ScrollbarGrab]			= ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
+				colors[ImGuiCol_ScrollbarGrabHovered]	= ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
+				colors[ImGuiCol_ScrollbarGrabActive]	= ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
+				colors[ImGuiCol_CheckMark]				= ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
+				colors[ImGuiCol_SliderGrab]				= ImVec4(1.00f, 1.00f, 1.00f, 0.71f);
+				colors[ImGuiCol_SliderGrabActive]		= ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
+				colors[ImGuiCol_Button]					= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_ButtonHovered]			= ImVec4(0.42f, 0.55f, 0.21f, 0.80f);
+				colors[ImGuiCol_ButtonActive]			= ImVec4(0.42f, 0.55f, 0.21f, 1.00f);
+				colors[ImGuiCol_Header]					= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_HeaderHovered]			= ImVec4(0.42f, 0.55f, 0.21f, 0.80f);
+				colors[ImGuiCol_HeaderActive]			= ImVec4(0.42f, 0.55f, 0.21f, 1.00f);
+				colors[ImGuiCol_Separator]				= ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
+				colors[ImGuiCol_SeparatorHovered]		= ImVec4(0.42f, 0.55f, 0.21f, 0.80f);
+				colors[ImGuiCol_SeparatorActive]		= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_ResizeGrip]				= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_ResizeGripHovered]		= ImVec4(0.42f, 0.55f, 0.21f, 0.80f);
+				colors[ImGuiCol_ResizeGripActive]		= ImVec4(0.42f, 0.55f, 0.21f, 1.00f);
+				colors[ImGuiCol_Tab]					= ImVec4(0.16f, 0.16f, 0.16f, 1.00f);
+				colors[ImGuiCol_TabHovered]				= ImVec4(0.42f, 0.55f, 0.21f, 1.00f);
+				colors[ImGuiCol_TabActive]				= ImVec4(0.24f, 0.31f, 0.12f, 1.00f);
+				colors[ImGuiCol_TabUnfocused]			= ImVec4(0.07f, 0.10f, 0.15f, 0.97f);
+				colors[ImGuiCol_TabUnfocusedActive]		= ImVec4(0.17f, 0.27f, 0.10f, 0.78f);
+				colors[ImGuiCol_DockingPreview]			= ImVec4(0.26f, 0.59f, 0.98f, 0.70f);
+				colors[ImGuiCol_DockingEmptyBg]			= ImVec4(0.20f, 0.20f, 0.20f, 0.00f);
+				colors[ImGuiCol_PlotLines]				= ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
+				colors[ImGuiCol_PlotLinesHovered]		= ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
+				colors[ImGuiCol_PlotHistogram]			= ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
+				colors[ImGuiCol_PlotHistogramHovered]	= ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
+				colors[ImGuiCol_TableHeaderBg]			= ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
+				colors[ImGuiCol_TableBorderStrong]		= ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
+				colors[ImGuiCol_TableBorderLight]		= ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
+				colors[ImGuiCol_TableRowBg]				= ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
+				colors[ImGuiCol_TableRowBgAlt]			= ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
+				colors[ImGuiCol_TextSelectedBg]			= ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
+				colors[ImGuiCol_DragDropTarget]			= ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
+				colors[ImGuiCol_NavHighlight]			= ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
+				colors[ImGuiCol_NavWindowingHighlight]	= ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+				colors[ImGuiCol_NavWindowingDimBg]		= ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
+				colors[ImGuiCol_ModalWindowDimBg]		= ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
+			}
+			break;
+	}
+}

+ 12 - 0
Praxis3D/Source/GUISystem.h

@@ -14,6 +14,15 @@ public:
 
 		m_systemName = GetString(Systems::GUI);
 		m_fonts.resize(GuiFontType::GuiFontType_NumOfTypes, nullptr);
+
+		// Set the color pallet
+		if(Config::GUIVar().gui_color_pallet >= 0 && Config::GUIVar().gui_color_pallet < GuiColorPallet::GuiColorPallet_NumOfPallets)
+			m_currentColorPallet = static_cast<GuiColorPallet>(Config::GUIVar().gui_color_pallet);
+		else
+			m_currentColorPallet = GuiColorPallet::GuiColorPallet_Default;
+
+		// Set the GUI colors
+		setColorPallet(m_currentColorPallet);
 	}
 	~GUISystem()
 	{
@@ -127,6 +136,9 @@ protected:
 		}
 	}
 
+	void setColorPallet(const GuiColorPallet p_colorPallet);
+
 	GUIScene *m_GUIScenes[EngineStateType::EngineStateType_NumOfTypes];
 	std::vector<ImFont *> m_fonts;
+	GuiColorPallet m_currentColorPallet;
 };

+ 51 - 12
Praxis3D/Source/GeometryPass.h

@@ -139,15 +139,15 @@ public:
 		//glDisable(GL_CULL_FACE);
 
 		// Enable / disable face culling
-		if(Config::rendererVar().face_culling)
-		{
-			glEnable(GL_CULL_FACE);
+		//if(Config::rendererVar().face_culling)
+		//{
+		//	glEnable(GL_CULL_FACE);
 
-			// Set face culling mode
-			glCullFace(Config::rendererVar().face_culling_mode);
-		}
-		else
-			glDisable(GL_CULL_FACE);
+		//	// Set face culling mode
+		//	glCullFace(Config::rendererVar().face_culling_mode);
+		//}
+		//else
+		//	glDisable(GL_CULL_FACE);
 
 
 		//glDepthMask(GL_TRUE);
@@ -156,6 +156,8 @@ public:
 		// Set input and output color maps for this frame
 		p_renderPassData.setColorInputMap(GBufferTextureType::GBufferIntermediate);
 		p_renderPassData.setColorOutputMap(GBufferTextureType::GBufferFinal);
+		//p_renderPassData.setColorInputMap(GBufferTextureType::GBufferFinal);
+		//p_renderPassData.setColorOutputMap(GBufferTextureType::GBufferIntermediate);
 
 		// Bind the geometry framebuffer to be used
 		m_renderer.m_backend.getGeometryBuffer()->bindFramebufferForWriting(GeometryBuffer::GBufferFramebufferType::FramebufferGeometry);
@@ -208,12 +210,30 @@ public:
 									if(modelData[modelIndex].m_meshes[meshIndex].m_heightScale > 0.0f)
 									{
 										// TEXTURE REPETITION and PARALLAX MAPPING enabled
-										m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), m_shaderGeometryStochasticParallaxMap->getShaderHandle(), m_shaderGeometryStochasticParallaxMap->getUniformUpdater(), modelMatrix, modelViewProjMatrix);
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											m_shaderGeometryStochasticParallaxMap->getShaderHandle(), 
+											m_shaderGeometryStochasticParallaxMap->getUniformUpdater(), 
+											DrawCommandTextureBinding::DrawCommandTextureBinding_All,
+											modelData[modelIndex].m_drawFaceCulling,
+											modelMatrix, 
+											modelViewProjMatrix);
 									}
 									else
 									{
 										// TEXTURE REPETITION enabled
-										m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), m_shaderGeometryStochastic->getShaderHandle(), m_shaderGeometryStochastic->getUniformUpdater(), modelMatrix, modelViewProjMatrix);
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											m_shaderGeometryStochastic->getShaderHandle(), 
+											m_shaderGeometryStochastic->getUniformUpdater(), 
+											DrawCommandTextureBinding::DrawCommandTextureBinding_All,
+											modelData[modelIndex].m_drawFaceCulling,
+											modelMatrix, 
+											modelViewProjMatrix);
 									}
 								}
 								else
@@ -221,12 +241,30 @@ public:
 									if(modelData[modelIndex].m_meshes[meshIndex].m_heightScale > 0.0f)
 									{
 										// PARALLAX MAPPING enabled
-										m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), m_shaderGeometryParallaxMap->getShaderHandle(), m_shaderGeometryParallaxMap->getUniformUpdater(), modelMatrix, modelViewProjMatrix);
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											m_shaderGeometryParallaxMap->getShaderHandle(),
+											m_shaderGeometryParallaxMap->getUniformUpdater(), 
+											DrawCommandTextureBinding::DrawCommandTextureBinding_All,
+											modelData[modelIndex].m_drawFaceCulling,
+											modelMatrix, 
+											modelViewProjMatrix);
 									}
 									else
 									{
 										// Regular geometry pass
-										m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), geomShaderHandle, geomUniformUpdater, modelMatrix, modelViewProjMatrix);
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											geomShaderHandle, 
+											geomUniformUpdater, 
+											DrawCommandTextureBinding::DrawCommandTextureBinding_All,
+											modelData[modelIndex].m_drawFaceCulling,
+											modelMatrix, 
+											modelViewProjMatrix);
 									}
 								}
 							}
@@ -252,6 +290,7 @@ public:
 							m_renderer.queueForDrawing(modelData[i],
 								shader.getShaderData()->m_shader.getShaderHandle(),
 								shader.getShaderData()->m_shader.getUniformUpdater(),
+								DrawCommandTextureBinding::DrawCommandTextureBinding_All,
 								spatialData.getSpatialDataChangeManager().getWorldTransformWithScale(),
 								m_renderer.m_viewProjMatrix);
 						}

+ 28 - 0
Praxis3D/Source/GraphicsDataSets.h

@@ -12,6 +12,13 @@ struct MaterialParameters
 {
 	MaterialParameters() : m_color(1.0f), m_scale(1.0f), m_framing(0.0f) { }
 
+	inline bool operator==(const MaterialParameters &p_matOaram)
+	{
+		return	m_color == p_matOaram.m_color &&
+				m_scale == p_matOaram.m_scale &&
+				m_framing == p_matOaram.m_framing;
+	}
+
 	glm::vec4 m_color;
 	glm::vec2 m_scale;
 	glm::vec2 m_framing;
@@ -21,6 +28,15 @@ struct MaterialData
 {
 	MaterialData() { }
 
+	inline bool operator==(const MaterialData &p_matData)
+	{
+		for(unsigned int i = 0; i < MaterialType::MaterialType_NumOfTypes; i++)
+			if(m_parameters[i] != p_matData.m_parameters[i])
+				return false;
+
+		return true;
+	}
+
 	MaterialParameters m_parameters[MaterialType::MaterialType_NumOfTypes];
 };
 
@@ -102,11 +118,23 @@ struct MeshData
 struct ModelData
 {
 	ModelData(ModelLoader::ModelHandle p_model) : m_model(p_model) { }
+	ModelData(
+		ModelLoader::ModelHandle p_model,
+		const FaceCullingSettings p_drawFaceCulling,
+		const FaceCullingSettings p_shadowFaceCulling) :
+		m_drawFaceCulling(p_drawFaceCulling),
+		m_shadowFaceCulling(p_shadowFaceCulling),
+		m_model(p_model) { }
 
 	// Handle to a model
 	ModelLoader::ModelHandle m_model;
+
 	// An array of meshes
 	std::vector<MeshData> m_meshes;
+
+	// Face culling settings
+	FaceCullingSettings m_drawFaceCulling;
+	FaceCullingSettings m_shadowFaceCulling;
 };
 
 // Contains model data and spatial data; needed by the renderer to draw a model using default shaders

+ 7 - 1
Praxis3D/Source/LuaScript.cpp

@@ -226,7 +226,9 @@ void LuaScript::setFunctions()
 	m_luaState.set_function("getEntityID", [this](const std::string &p_filename) -> EntityID { return static_cast<WorldScene *>(m_scriptScene->getSceneLoader()->getSystemScene(Systems::World))->getEntity(p_filename); });
 	m_luaState.set_function("isEntityIDValid", [](const EntityID p_entityID) -> bool { return p_entityID != NULL_ENTITY_ID; });
 	m_luaState.set_function("createEntity", [this](const ComponentsConstructionInfo &p_constructionInfo) -> EntityID { return static_cast<WorldScene *>(m_scriptScene->getSceneLoader()->getSystemScene(Systems::World))->createEntity(p_constructionInfo); });
-	m_luaState.set_function("importPrefab", [this](ComponentsConstructionInfo &p_constructionInfo, const std::string &p_filename) -> bool { return m_scriptScene->getSceneLoader()->importPrefab(p_constructionInfo, p_filename) == ErrorCode::Success; });
+	m_luaState.set_function("importPrefab", sol::overload(
+		[this](ComponentsConstructionInfo &p_constructionInfo, const std::string &p_filename) -> bool { return m_scriptScene->getSceneLoader()->importPrefab(p_constructionInfo, p_filename, false) == ErrorCode::Success; },
+		[this](ComponentsConstructionInfo &p_constructionInfo, const std::string &p_filename, const bool p_forceReload) -> bool { return m_scriptScene->getSceneLoader()->importPrefab(p_constructionInfo, p_filename, p_forceReload) == ErrorCode::Success; }));
 
 	// Entity component functions
 	m_luaState.set_function("getSoundComponent", [this](const EntityID p_entityID) -> SoundComponent *{ return static_cast<WorldScene *>(m_scriptScene->getSceneLoader()->getSystemScene(Systems::World))->getEntityRegistry().try_get<SoundComponent>(p_entityID); });
@@ -357,6 +359,9 @@ void LuaScript::setFunctions()
 	m_luaState.set_function("toDegreesVec3", sol::resolve<glm::vec3(const glm::vec3 &)>(&glm::degrees));
 	m_luaState.set_function("toDegreesVec4", sol::resolve<glm::vec4(const glm::vec4 &)>(&glm::degrees));
 
+	// Misc
+	m_luaState.set_function("time", [=]() -> int { return (int)time(NULL); });
+
 	// Physics functions
 	m_luaState.set_function("getPhysicsSimulationRunning", [this]() -> const bool { return static_cast<PhysicsScene *>(m_scriptScene->getSceneLoader()->getSystemScene(Systems::Physics))->getSimulationRunning(); });
 }
@@ -669,6 +674,7 @@ void LuaScript::setUsertypes()
 
 	// Component construction info
 	m_luaState.new_usertype<ComponentsConstructionInfo>("ComponentsConstructionInfo",
+		sol::constructors<ComponentsConstructionInfo()>(),
 		sol::meta_function::garbage_collect, sol::destructor(&ComponentsConstructionInfo::deleteConstructionInfo),
 		"m_name", &ComponentsConstructionInfo::m_name,
 		"m_id", &ComponentsConstructionInfo::m_id,

+ 76 - 21
Praxis3D/Source/LuaScript.h

@@ -113,6 +113,7 @@ public:
 		m_keyCommands.reserve(10);
 		m_conditionals.reserve(10);
 		m_currentChanges = Systems::Changes::None;
+		resetErrorFlag();
 	}
 	LuaScript(SystemScene *p_scriptScene, SystemObject *p_luaComponent, SpatialDataManager &p_spatialData, GUIDataManager &p_GUIData, std::string &p_scriptFilename) : 
 		m_scriptScene(p_scriptScene), m_luaComponent(p_luaComponent), m_spatialData(p_spatialData), m_GUIData(p_GUIData), m_luaScriptFilename(p_scriptFilename)
@@ -120,44 +121,87 @@ public:
 		m_keyCommands.reserve(10);
 		m_conditionals.reserve(10);
 		m_currentChanges = Systems::Changes::None;
+		resetErrorFlag();
 	}
 	~LuaScript();
 
 	ErrorCode init()
 	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		resetErrorFlag();
+
 		// Initialize Lua state
 		m_luaState.open_libraries(sol::lib::base);
 
 		// Load script file
-		m_luaState.script_file(Config::filepathVar().script_path + m_luaScriptFilename);
+		if(auto loadScriptError = m_luaState.script_file(Config::filepathVar().script_path + m_luaScriptFilename); loadScriptError.valid())
+		{
+			// Set enum definitions and function call-backs, and define C++ user-types in Lua
+			setDefinitions();
+			setFunctions();
+			setUsertypes();
+			setLuaVariables();
+
+			// Get function references that are inside the Lua script
+			m_luaInit = m_luaState[Config::scriptVar().iniFunctionName];
+			m_luaUpdate = m_luaState[Config::scriptVar().updateFunctionName];
+
+			// Initialize the Lua script
+			if(auto initError = m_luaInit(); !initError.valid())
+			{
+				sol::error error = initError;
+				std::string errorString = error.what();
+
+				ErrHandlerLoc().get().log(ErrorCode::Lua_init_func_failed, ErrorSource::Source_LuaScript, errorString);
 
-		// Set enum definitions and function call-backs, and define C++ user-types in Lua
-		setDefinitions();
-		setFunctions();
-		setUsertypes();
-		setLuaVariables();
+				returnError = ErrorCode::Lua_init_func_failed;
+			}
+		}
+		else
+		{
+			sol::error error = loadScriptError;
+			std::string errorString = error.what();
 
-		// Get function references that are inside the Lua script
-		m_luaInit = m_luaState[Config::scriptVar().iniFunctionName];
-		m_luaUpdate = m_luaState[Config::scriptVar().updateFunctionName];
+			ErrHandlerLoc().get().log(ErrorCode::Lua_load_script_failed, ErrorSource::Source_LuaScript, errorString);
 
-		// Initialize the Lua script
-		m_luaInit();
+			returnError = ErrorCode::Lua_load_script_failed;
+		}
 
-		return ErrorCode::Success;
+		return returnError;
 	}
 
 	inline void update(const float p_deltaTime)
 	{
-		// Clear changes from the last update
-		clearChanges();
-		m_queuedChanges.clear();
+		if(!m_updateFuncGeneratedError)
+		{
+			// Clear changes from the last update
+			clearChanges();
+			m_queuedChanges.clear();
 
-		// Clear all the GUI functors from the last update
-		m_GUIData.clearFunctors();
+			// Clear all the GUI functors from the last update
+			m_GUIData.clearFunctors();
 
-		// Call update function in the lua script
-		m_luaUpdate(p_deltaTime);
+			// Call update function in the lua script
+			if(!Config::scriptVar().luaUpdateErrorsEveryFrame && m_updateFuncRanWithNoErrors)
+			{
+				m_luaUpdate(p_deltaTime);
+			}
+			else
+			{
+				// Check for errors if the update function is called for the first time
+				if(auto updateError = m_luaUpdate(p_deltaTime); !updateError.valid())
+				{
+					sol::error error = updateError;
+					std::string errorString = error.what();
+
+					ErrHandlerLoc().get().log(ErrorCode::Lua_update_func_failed, ErrorSource::Source_LuaScript, errorString);
+					m_updateFuncGeneratedError = true;
+				}
+				else
+					m_updateFuncRanWithNoErrors = true;
+			}
+		}
 	}
 
 	// Clears the currently registered changes. They are also cleared at the beginning of each update call
@@ -258,13 +302,24 @@ private:
 		m_currentChanges += p_packer.get();
 	}
 
+	// Errors flags should be reset after reinitializing
+	void resetErrorFlag()
+	{
+		m_updateFuncGeneratedError = false;
+		m_updateFuncRanWithNoErrors = false;
+	}
+
+	// Used to stop running after an error is generated
+	bool m_updateFuncGeneratedError;
+	bool m_updateFuncRanWithNoErrors;
+
 	// Lua state for the loaded script
 	sol::state m_luaState;
 	std::string m_luaScriptFilename;
 
 	// Function binds that call functions inside the lua script
-	sol::function m_luaInit;
-	sol::function m_luaUpdate;
+	sol::protected_function m_luaInit;
+	sol::protected_function m_luaUpdate;
 
 	// Tables for variable definitions
 	sol::table m_userTypesTable;

+ 49 - 15
Praxis3D/Source/LuminancePass.h

@@ -8,10 +8,11 @@ public:
 	LuminancePass(RendererFrontend &p_renderer) :
 		RenderPass(p_renderer, RenderPassType::RenderPassType_Luminance),
 		m_histogramBuffer(BufferType_ElementArray, BufferBindTarget_ShaderStorage, BufferUsageHint_DynamicDraw),
-		m_luminanceAverageTexture(Loaders::texture2D().create("LuminanceTexture", 1, 1, TextureFormat_Red, TextureDataFormat_R16F, TextureDataType_Float)),
+		m_luminanceAverageTexture(Loaders::texture2D().create("LuminanceAverageTexture", 1, 1, TextureFormat_Red, TextureDataFormat_R16F, TextureDataType_Float)),
 		m_luminanceHistogramShader(nullptr),
 		m_luminanceAverageShader(nullptr),
-		m_tonemappingShader(nullptr) { }
+		m_exposureAdaptationShader(nullptr),
+		m_passthroughShader(nullptr) { }
 
 	~LuminancePass() { }
 
@@ -29,20 +30,29 @@ public:
 		PropertySet luminanceAverageProperty(Properties::Shaders);
 		luminanceAverageProperty.addProperty(Properties::ComputeShader, Config::rendererVar().luminance_average_comp_shader);
 
-		// Create a property-set used to load tonemapping shader
-		PropertySet tonemappingProperty(Properties::Shaders);
-		tonemappingProperty.addProperty(Properties::VertexShader, Config::rendererVar().tonemapping_vert_shader);
-		tonemappingProperty.addProperty(Properties::FragmentShader, Config::rendererVar().tonemapping_frag_shader);
+		// Create a property-set used to load exposure adaptation shader
+		PropertySet exposureAdaptationProperty(Properties::Shaders);
+		exposureAdaptationProperty.addProperty(Properties::Name, std::string("exposureAdaptation"));
+		exposureAdaptationProperty.addProperty(Properties::FragmentShader, Config::rendererVar().exposure_adaptation_frag_shader);
+		exposureAdaptationProperty.addProperty(Properties::VertexShader, Config::rendererVar().exposure_adaptation_vert_shader);
+
+		// Create a property-set used to load pass-through shader
+		PropertySet passthroughProperty(Properties::Shaders);
+		passthroughProperty.addProperty(Properties::Name, std::string("luminancePassthrough"));
+		passthroughProperty.addProperty(Properties::FragmentShader, std::string("passthrough.frag"));
+		passthroughProperty.addProperty(Properties::VertexShader, std::string("passthrough.vert"));
 
 		// Create shaders
 		m_luminanceHistogramShader = Loaders::shader().load(luminanceHistogramProperty);
 		m_luminanceAverageShader = Loaders::shader().load(luminanceAverageProperty);
-		m_tonemappingShader = Loaders::shader().load(tonemappingProperty);
+		m_exposureAdaptationShader = Loaders::shader().load(exposureAdaptationProperty);
+		m_passthroughShader = Loaders::shader().load(passthroughProperty);
 
 		// Load shaders to memory
 		ErrorCode luminanceHistogramError = m_luminanceHistogramShader->loadToMemory();
 		ErrorCode luminanceAverageError = m_luminanceAverageShader->loadToMemory();
-		ErrorCode tonemappingError = m_tonemappingShader->loadToMemory();
+		ErrorCode tonemappingError = m_exposureAdaptationShader->loadToMemory();
+		ErrorCode passthroughError = m_passthroughShader->loadToMemory();
 
 		// Queue the shaders to be loaded to GPU
 		if(luminanceHistogramError == ErrorCode::Success)
@@ -56,10 +66,15 @@ public:
 			returnError = luminanceAverageError;
 
 		if(tonemappingError == ErrorCode::Success)
-			m_renderer.queueForLoading(*m_tonemappingShader);
+			m_renderer.queueForLoading(*m_exposureAdaptationShader);
 		else
 			returnError = tonemappingError;
 
+		if(passthroughError == ErrorCode::Success)
+			m_renderer.queueForLoading(*m_passthroughShader);
+		else
+			returnError = passthroughError;
+
 		// Check for errors and log either a successful or a failed initialization
 		if(returnError == ErrorCode::Success)
 		{
@@ -95,38 +110,57 @@ public:
 			unsigned int groupsX = static_cast<uint32_t>(glm::ceil(imageWidth / 16.0f));
 			unsigned int groupsY = static_cast<uint32_t>(glm::ceil(imageHeight / 16.0f));
 
+			/*
+				Calculate luminance histogram
+			*/
 			m_renderer.m_backend.getGeometryBuffer()->bindBufferToImageUnitForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture, 0);
 
 			m_renderer.queueForDrawing(m_luminanceHistogramShader->getShaderHandle(), m_luminanceHistogramShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix, groupsX, groupsY, 1, MemoryBarrierType::MemoryBarrierType_ShaderStorageBarrier);
 			m_renderer.passComputeDispatchCommandsToBackend();
 
+			/*
+				Calculate average luminance
+			*/
 			glBindImageTexture(0, m_luminanceAverageTexture.getHandle(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_R16F);
 
 			m_renderer.queueForDrawing(m_luminanceAverageShader->getShaderHandle(), m_luminanceAverageShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix, 1, 1, 1, MemoryBarrierType::MemoryBarrierType_ShaderStorageBarrier);
 			m_renderer.passComputeDispatchCommandsToBackend();
 
+			/*
+				Adjust exposure based on average luminance
+			*/
 			glDisable(GL_DEPTH_TEST);
 
 			glActiveTexture(GL_TEXTURE0 + LuminanceTextureType::LensFlareTextureType_AverageLuminance);
 			glBindTexture(GL_TEXTURE_2D, m_luminanceAverageTexture.getHandle());
 
 			m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GBufferTextureType::GBufferInputTexture);
-			m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GBufferTextureType::GBufferInputTexture);
-
 			m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(p_renderPassData.getColorOutputMap());
 
-			// Queue and render a full screen quad using a final pass shader
-			m_renderer.queueForDrawing(m_tonemappingShader->getShaderHandle(), m_tonemappingShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+			// Queue and render a full screen quad using a exposure adaptation pass shader
+			m_renderer.queueForDrawing(m_exposureAdaptationShader->getShaderHandle(), m_exposureAdaptationShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
 			m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+			/*
+				Copy color from input color buffer to final buffer
+			*/
+			m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorOutputMap(), GBufferTextureType::GBufferInputTexture);
+			m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferFinal);
+
+			// Queue and render a full screen quad using a pass-through pass shader
+			m_renderer.queueForDrawing(m_passthroughShader->getShaderHandle(), m_passthroughShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+			m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+			//p_renderPassData.swapColorInputOutputMaps();
 		}
 
-		p_renderPassData.swapColorInputOutputMaps();
 	}
 
 private:
 	ShaderLoader::ShaderProgram *m_luminanceHistogramShader;
 	ShaderLoader::ShaderProgram *m_luminanceAverageShader;
-	ShaderLoader::ShaderProgram *m_tonemappingShader;
+	ShaderLoader::ShaderProgram *m_exposureAdaptationShader;
+	ShaderLoader::ShaderProgram *m_passthroughShader;
 
 	RendererFrontend::ShaderBuffer m_histogramBuffer;
 

+ 27 - 65
Praxis3D/Source/ModelComponent.h

@@ -28,17 +28,17 @@ public:
 		}
 
 		SingleMeshData(
-			const std::vector<std::string> &p_meshMaterials, 
+			const std::vector<std::string> &p_meshMaterials,
 			const std::vector<glm::vec2> &p_meshMaterialsScales,
 			const std::vector<glm::vec2> &p_meshMaterialFraming,
 			const std::vector<glm::vec4> &p_meshMaterialsColor,
-			const std::string &p_meshName, 
-			const float p_alphaThreshold, 
-			const float p_emissiveIntensity, 
-			const float p_heightScale, 
+			const std::string &p_meshName,
+			const float p_alphaThreshold,
+			const float p_emissiveIntensity,
+			const float p_heightScale,
 			const float p_stochasticSamplingScale,
-			const bool p_active, 
-			const bool p_present, 
+			const bool p_active,
+			const bool p_present,
 			const bool p_stochasticSampling,
 			const TextureWrapType p_textureWrapMode) :
 			m_meshMaterials(p_meshMaterials),
@@ -71,78 +71,30 @@ public:
 	};
 	struct MeshProperties
 	{
-		MeshProperties()
-		{
-			//m_numOfMeshes = 0;
-		}
+		MeshProperties() { }
+		MeshProperties(
+			const FaceCullingSettings p_drawFaceCulling, 
+			const FaceCullingSettings p_shadowFaceCulling) : 
+			m_drawFaceCulling(p_drawFaceCulling), 
+			m_shadowFaceCulling(p_shadowFaceCulling) { }
 
 		void resize(const std::size_t p_size)
 		{
 			if(p_size > m_meshData.size())
 				m_meshData.resize(p_size);
-			//if(p_size > m_numOfMeshes)
-			//{
-			//	m_numOfMeshes = p_size;
-
-			//	// Resize mesh materials
-			//	if(p_size > m_meshMaterials.size())
-			//	{
-			//		std::vector<std::string> emptyStrings(MaterialType::MaterialType_NumOfTypes);
-			//		m_meshMaterials.resize(p_size, emptyStrings);
-			//	}
-
-			//	// Resize mesh materials scale and initialize each element to 1.0f
-			//	const auto oldSize = m_meshMaterialsScale.size();
-			//	if(p_size > oldSize)
-			//	{
-			//		std::vector<glm::vec2> emptyVec2(MaterialType::MaterialType_NumOfTypes, glm::vec2(1.0f, 1.0f));
-			//		m_meshMaterialsScale.resize(p_size, emptyVec2);
-			//	}
-
-			//	// Resize mesh material alpha threshold and initialize each element to 0.0f
-			//	if(p_size > m_alphaThreshold.size())
-			//		m_alphaThreshold.resize(p_size, Config::graphicsVar().alpha_threshold);
-
-			//	// Resize mesh material emissive intensity initialize each element to 0.0f
-			//	if(p_size > m_emissiveIntensity.size())
-			//		m_emissiveIntensity.resize(p_size, Config::graphicsVar().emissive_multiplier);
-
-			//	// Resize mesh material height scale and initialize each element to 1.0f
-			//	if(p_size > m_heightScale.size())
-			//		m_heightScale.resize(p_size, Config::graphicsVar().height_scale);
-
-			//	// Resize the "mesh is active" array and initialize each element to true
-			//	if(p_size > m_active.size())
-			//		m_active.resize(p_size, true);
-
-			//	// Resize the "mesh is present" array and initialize each element to false
-			//	if(p_size > m_present.size())
-			//		m_present.resize(p_size, false);
-
-			//	// Resize the texture wrap mode array and initialize each element to Repeat
-			//	if(p_size > m_textureWrapMode.size())
-			//		m_textureWrapMode.resize(p_size, TextureWrapType::TextureWrapType_Repeat);
-			//}
 		}
 
 		void clear()
 		{
-			//m_numOfMeshes = 0;
 			m_modelName.clear();
 			m_meshData.clear();
-			//m_meshMaterials.clear();
-			//m_meshMaterialsScale.clear();
-			//m_alphaThreshold.clear();
-			//m_emissiveIntensity.clear();
-			//m_heightScale.clear();
-			//m_active.clear();
-			//m_present.clear();
-			//m_textureWrapMode.clear();
 		}
 
 		std::string m_modelName;
-
 		std::vector<SingleMeshData> m_meshData;
+
+		FaceCullingSettings m_drawFaceCulling;
+		FaceCullingSettings m_shadowFaceCulling;
 	};
 	struct ModelsProperties
 	{
@@ -195,7 +147,13 @@ public:
 				// Add a new model data entry, and get a reference to it
 				m_modelData.emplace_back(Loaders::model().load(m_modelsProperties->m_models[modelIndex].m_modelName, false));
 
-				if(!m_modelData.back().m_model.isLoadedToMemory())
+				auto &newModel = m_modelData.back();
+
+				// Add face culling settings
+				newModel.m_drawFaceCulling = m_modelsProperties->m_models[modelIndex].m_drawFaceCulling;
+				newModel.m_shadowFaceCulling = m_modelsProperties->m_models[modelIndex].m_shadowFaceCulling;
+
+				if(!newModel.m_model.isLoadedToMemory())
 					m_modelsNeedLoading = true;
 			}
 		}
@@ -498,6 +456,10 @@ public:
 			// Add model filename to the models properties
 			newModelEntry.m_modelName = m_modelData[modelIndex].m_model.getFilename();
 
+			// Add face culling settings
+			newModelEntry.m_drawFaceCulling = m_modelData[modelIndex].m_drawFaceCulling;
+			newModelEntry.m_shadowFaceCulling = m_modelData[modelIndex].m_shadowFaceCulling;
+
 			// Loop over each mesh
 			for(decltype(m_modelData[modelIndex].m_meshes.size()) meshSize = m_modelData[modelIndex].m_meshes.size(), meshIndex = 0; meshIndex < meshSize; meshIndex++)
 			{

+ 19 - 5
Praxis3D/Source/PhysicsScene.cpp

@@ -15,6 +15,7 @@ PhysicsScene::PhysicsScene(SystemBase *p_system, SceneLoader *p_sceneLoader) : S
 	m_collisionBroadphase = nullptr;
 	m_dynamicsWorld = nullptr;
 	m_simulationRunning = true;
+	resetErrors();
 }
 
 PhysicsScene::~PhysicsScene()
@@ -71,7 +72,7 @@ ErrorCode PhysicsScene::init()
 	// collision configuration contains default setup for memory , collision setup . Advanced users can create their own configuration .
 	m_collisionConfiguration = new btDefaultCollisionConfiguration();
 
-	// use the default collision dispatcher . For parallel processing you can use a diffent dispatcher(see Extras / BulletMultiThreaded)
+	// use the default collision dispatcher . For parallel processing you can use a different dispatcher (see Extras / BulletMultiThreaded)
 	m_collisionDispatcher = new btCollisionDispatcher(m_collisionConfiguration);
 
 	// btDbvtBroadphase is a good general purpose broadphase . You can also try out btAxis3Sweep .
@@ -128,6 +129,9 @@ void PhysicsScene::exportSetup(PropertySet &p_propertySet)
 
 void PhysicsScene::update(const float p_deltaTime)
 {
+	// Reset errors for this frame
+	resetErrors();
+
 	// Get double buffering index
 	const auto dbIndex = ClockLocator::get().getDoubleBufferingIndexBack();
 
@@ -218,8 +222,13 @@ void PhysicsScene::internalTickCallback(btDynamicsWorld *p_world, btScalar p_tim
 					}
 					else
 					{
-						// Log an error if the dynamic collision event count exceeds the maximum number of supported dynamic events
-						ErrHandlerLoc::get().log(ErrorCode::Collision_max_dynamic_events, ErrorSource::Source_Physics);
+						if(!m_maxDynamicCollisionsErrorIssued)
+						{
+							m_maxDynamicCollisionsErrorIssued = true;
+
+							// Log an error if the dynamic collision event count exceeds the maximum number of supported dynamic events
+							ErrHandlerLoc::get().log(ErrorCode::Collision_max_dynamic_events, ErrorSource::Source_Physics);
+						}
 					}
 
 					// Process the dynamic collision event on the second object
@@ -236,8 +245,13 @@ void PhysicsScene::internalTickCallback(btDynamicsWorld *p_world, btScalar p_tim
 					}
 					else
 					{
-						// Log an error if the dynamic collision event count exceeds the maximum number of supported dynamic events
-						ErrHandlerLoc::get().log(ErrorCode::Collision_max_dynamic_events, ErrorSource::Source_Physics);
+						if(!m_maxStaticCollisionsErrorIssued)
+						{
+							m_maxStaticCollisionsErrorIssued = true;
+
+							// Log an error if the dynamic collision event count exceeds the maximum number of supported dynamic events
+							ErrHandlerLoc::get().log(ErrorCode::Collision_max_dynamic_events, ErrorSource::Source_Physics);
+						}
 					}
 				}
 				else

+ 8 - 0
Praxis3D/Source/PhysicsScene.h

@@ -179,6 +179,12 @@ private:
 		return false;
 	}
 
+	inline void resetErrors()
+	{
+		m_maxDynamicCollisionsErrorIssued = false;
+		m_maxStaticCollisionsErrorIssued = false;
+	}
+
 	PhysicsTask *m_physicsTask;
 
 	// Collision configuration
@@ -196,4 +202,6 @@ private:
 	static PhysicsScene *s_currentPhysicsScene;
 
 	bool m_simulationRunning;
+	bool m_maxDynamicCollisionsErrorIssued;
+	bool m_maxStaticCollisionsErrorIssued;
 };

+ 1 - 1
Praxis3D/Source/PropertySet.h

@@ -512,7 +512,7 @@ public:
 	const inline bool operator==(const Property &p_property) const noexcept { return (m_propertyID == p_property.m_propertyID); }
 	const inline bool operator<(const Property &p_property) const noexcept { return (m_propertyID < p_property.m_propertyID); }
 
-	// Cope assignment operator
+	// Copy assignment operator
 	inline Property &operator=(const Property &p_property) noexcept
 	{
 		m_propertyID = p_property.m_propertyID;

+ 87 - 31
Praxis3D/Source/RendererBackend.cpp

@@ -39,11 +39,17 @@ ErrorCode RendererBackend::init(const UniformFrameData &p_frameData)
 		m_fullscreenTriangle.load();
 
 		// Enable / disable face culling
-		if(Config::rendererVar().face_culling)
+		if(m_rendererState.m_lastFaceCullingSettings.m_faceCullingEnabled)
 			glEnable(GL_CULL_FACE);
 		else
 			glDisable(GL_CULL_FACE);
 
+		// Set face culling face type
+		if(m_rendererState.m_lastFaceCullingSettings.m_backFaceCulling)
+			glCullFace(GL_BACK);
+		else
+			glCullFace(GL_FRONT);
+
 		// Enable / disable depth test
 		if(Config::rendererVar().depth_test)
 			glEnable(GL_DEPTH_TEST);
@@ -142,6 +148,11 @@ void RendererBackend::processDrawing(const DrawCommands &p_drawCommands, const U
 
 	for(decltype(p_drawCommands.size()) i = 0, size = p_drawCommands.size(); i < size; i++)
 	{
+		// Set face culling settings
+		setFaceCullEnable(p_drawCommands[i].second.m_faceCullingSettings.m_faceCullingEnabled);
+		if(p_drawCommands[i].second.m_faceCullingSettings.m_faceCullingEnabled)
+			setBackFaceCull(p_drawCommands[i].second.m_faceCullingSettings.m_backFaceCulling);
+
 		// Get uniform data
 		const UniformObjectData &uniformObjectData = p_drawCommands[i].second.m_uniformObjectData;
 
@@ -159,40 +170,86 @@ void RendererBackend::processDrawing(const DrawCommands &p_drawCommands, const U
 		meshUniformUpdate(shaderHandle, uniformUpdater, uniformObjectData, p_frameData);
 
 		// Update material data buffer
-		BufferUpdateCommand materialDataUpdateCommand(
-			m_materialDataBuffer.m_handle, 
-			0, 
-			m_materialDataBuffer.m_size, 
-			(const void *)&p_drawCommands[i].second.m_materialData, 
-			BufferUpdateType::BufferUpdate_Data, 
-			m_materialDataBuffer.m_bufferType, 
-			m_materialDataBuffer.m_bufferUsage);
-
-		processCommand(materialDataUpdateCommand, p_frameData);
+		if(p_drawCommands[i].second.m_textureBindingType != DrawCommandTextureBinding_None)
+		{
+			if(m_rendererState.m_materialData != p_drawCommands[i].second.m_materialData)
+			{
+				m_rendererState.m_materialData = p_drawCommands[i].second.m_materialData;
+
+				BufferUpdateCommand materialDataUpdateCommand(
+					m_materialDataBuffer.m_handle,
+					0,
+					m_materialDataBuffer.m_size,
+					(const void *)&p_drawCommands[i].second.m_materialData,
+					BufferUpdateType::BufferUpdate_Data,
+					m_materialDataBuffer.m_bufferType,
+					m_materialDataBuffer.m_bufferUsage);
+
+				processCommand(materialDataUpdateCommand, p_frameData);
+			}
+		}
 
 		// Bind VAO
 		bindVAO(p_drawCommands[i].second.m_modelHandle);
 
 		// Bind textures
-		glActiveTexture(GL_TEXTURE0 + MaterialType_Diffuse);
-		glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matDiffuse);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
-
-		glActiveTexture(GL_TEXTURE0 + MaterialType_Normal);
-		glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matNormal);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
-
-		glActiveTexture(GL_TEXTURE0 + MaterialType_Combined);
-		glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matCombined);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
-
-		glActiveTexture(GL_TEXTURE0 + MaterialType_Emissive);
-		glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matEmissive);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+		switch(p_drawCommands[i].second.m_textureBindingType)
+		{
+			case DrawCommandTextureBinding_None:
+			default:
+				break;
+			case DrawCommandTextureBinding_DiffuseOnly:
+				{
+					if(m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Diffuse] != p_drawCommands[i].second.m_matDiffuse)
+					{
+						m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Diffuse] = p_drawCommands[i].second.m_matDiffuse;
+						glActiveTexture(GL_TEXTURE0 + MaterialType_Diffuse);
+						glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matDiffuse);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+					}
+				}
+				break;
+			case DrawCommandTextureBinding_All:
+				{
+					if(m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Diffuse] != p_drawCommands[i].second.m_matDiffuse)
+					{
+						m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Diffuse] = p_drawCommands[i].second.m_matDiffuse;
+						glActiveTexture(GL_TEXTURE0 + MaterialType_Diffuse);
+						glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matDiffuse);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+					}
+
+					if(m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Normal] != p_drawCommands[i].second.m_matNormal)
+					{
+						m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Normal] = p_drawCommands[i].second.m_matNormal;
+						glActiveTexture(GL_TEXTURE0 + MaterialType_Normal);
+						glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matNormal);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+					}
+
+					if(m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Emissive] != p_drawCommands[i].second.m_matEmissive)
+					{
+						m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Emissive] = p_drawCommands[i].second.m_matEmissive;
+						glActiveTexture(GL_TEXTURE0 + MaterialType_Emissive);
+						glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matEmissive);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+					}
+
+					if(m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Combined] != p_drawCommands[i].second.m_matCombined)
+					{
+						m_rendererState.m_lastBoundTextures[MaterialType::MaterialType_Combined] = p_drawCommands[i].second.m_matCombined;
+						glActiveTexture(GL_TEXTURE0 + MaterialType_Combined);
+						glBindTexture(GL_TEXTURE_2D, p_drawCommands[i].second.m_matCombined);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_drawCommands[i].second.m_matWrapMode);
+						//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_drawCommands[i].second.m_matWrapMode);
+					}
+				}
+				break;
+		}
 		
 		// Draw the geometry
 		glDrawElementsBaseVertex(GL_TRIANGLES,
@@ -279,4 +336,3 @@ void RendererBackend::processDrawing(const ComputeDispatchCommands &p_computeDis
 		glMemoryBarrier(memoryBarrierBit);
 	}
 }
-

+ 51 - 0
Praxis3D/Source/RendererBackend.h

@@ -62,7 +62,9 @@ public:
 					const unsigned int p_matNormal,
 					const unsigned int p_matEmissive,
 					const unsigned int p_matCombined,
+					const FaceCullingSettings p_faceCulling,
 					const MaterialData p_materialData,
+					const DrawCommandTextureBinding p_textureBindingType,
 					const int p_matWrapMode) :
 
 			m_uniformUpdater(&p_uniformUpdater), 
@@ -76,7 +78,9 @@ public:
 			m_matNormal(p_matNormal),
 			m_matEmissive(p_matEmissive),
 			m_matCombined(p_matCombined),
+			m_faceCullingSettings(p_faceCulling),
 			m_materialData(p_materialData),
+			m_textureBindingType(p_textureBindingType),
 			m_matWrapMode(p_matWrapMode) { }
 
 		ShaderUniformUpdater *m_uniformUpdater;
@@ -94,7 +98,10 @@ public:
 		unsigned int m_matEmissive;
 		unsigned int m_matCombined;
 
+		FaceCullingSettings m_faceCullingSettings;
+
 		MaterialData m_materialData;
+		DrawCommandTextureBinding m_textureBindingType;
 
 		int m_matWrapMode;
 	};
@@ -482,6 +489,14 @@ protected:
 			m_lastFrameUpdate = 0;
 			m_lastModelUpdate = 0;
 			m_lastMeshUpdate = 0;
+
+			resetBoundTextures();
+		}
+
+		inline void resetBoundTextures()
+		{
+			for(unsigned int i = 0; i < MaterialType::MaterialType_NumOfTypes; i++)
+				m_lastBoundTextures[i] = 0;
 		}
 
 		unsigned int m_boundUniformBuffer;
@@ -492,6 +507,12 @@ protected:
 		unsigned int m_lastFrameUpdate;
 		unsigned int m_lastModelUpdate;
 		unsigned int m_lastMeshUpdate;
+
+		unsigned int m_lastBoundTextures[MaterialType::MaterialType_NumOfTypes];
+
+		FaceCullingSettings m_lastFaceCullingSettings;
+
+		MaterialData m_materialData;
 	};
 	
 	// Holds buffer parameters
@@ -525,6 +546,9 @@ protected:
 			// Bind the shader
 			glUseProgram(p_shaderHandle);
 			m_rendererState.m_boundShader = p_shaderHandle;
+
+			// Reset the bound textures because the shader changed
+			m_rendererState.resetBoundTextures();
 		}
 	}
 	inline void bindVAO(const unsigned int p_VAO)
@@ -537,6 +561,33 @@ protected:
 			m_rendererState.m_boundVAO = p_VAO;
 		}
 	}
+	inline void setFaceCullEnable(const bool p_enable)
+	{
+		if(m_rendererState.m_lastFaceCullingSettings.m_faceCullingEnabled != p_enable)
+		{
+			m_rendererState.m_lastFaceCullingSettings.m_faceCullingEnabled = p_enable;
+
+			// Enable / disable face culling
+			if(p_enable)
+				glEnable(GL_CULL_FACE);
+			else
+				glDisable(GL_CULL_FACE);
+		}
+	}
+	inline void setBackFaceCull(const bool p_cullBackFace)
+	{
+		if(m_rendererState.m_lastFaceCullingSettings.m_faceCullingEnabled && 
+			m_rendererState.m_lastFaceCullingSettings.m_backFaceCulling != p_cullBackFace)
+		{
+			m_rendererState.m_lastFaceCullingSettings.m_backFaceCulling = p_cullBackFace;
+
+			// Set face culling face type
+			if(p_cullBackFace)
+				glCullFace(GL_BACK);
+			else
+				glCullFace(GL_FRONT);
+		}
+	}
 	inline void resetVAO() 
 	{
 		// Set the currently bound VAO back to 0, so the VAO will need to be bound again

+ 59 - 53
Praxis3D/Source/RendererFrontend.cpp

@@ -18,6 +18,7 @@
 #include "RendererFrontend.h"
 #include "ShadowMappingPass.h"
 #include "SkyPass.h"
+#include "TonemappingPass.h"
 
 RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
 {
@@ -122,77 +123,82 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 	{
 		switch(p_renderingPasses[i])
 		{
-			case RenderPassType_Geometry:
-				if(m_allRenderPasses[RenderPassType_Geometry] == nullptr)
-					m_allRenderPasses[RenderPassType_Geometry] = new GeometryPass(*this);
+			case RenderPassType::RenderPassType_Geometry:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Geometry] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Geometry] = new GeometryPass(*this);
 				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Geometry]);
 				break;
-			case RenderPassType_ShadowMapping:
-				if(m_allRenderPasses[RenderPassType_ShadowMapping] == nullptr)
-					m_allRenderPasses[RenderPassType_ShadowMapping] = new ShadowMappingPass(*this);
+			case RenderPassType::RenderPassType_ShadowMapping:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_ShadowMapping] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_ShadowMapping] = new ShadowMappingPass(*this);
 				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_ShadowMapping]);
 				shadowMappingPassSet = true;
 				break;
-			case RenderPassType_Lighting:
-				if(m_allRenderPasses[RenderPassType_Lighting] == nullptr)
-					m_allRenderPasses[RenderPassType_Lighting] = new LightingPass(*this);
+			case RenderPassType::RenderPassType_Lighting:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Lighting] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Lighting] = new LightingPass(*this);
 				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Lighting]);
 				break;
-			case RenderPassType_AtmScattering:
-				if(m_allRenderPasses[RenderPassType_AtmScattering] == nullptr)
-					m_allRenderPasses[RenderPassType_AtmScattering] = new AtmScatteringPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_AtmScattering]);
+			case RenderPassType::RenderPassType_AtmScattering:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_AtmScattering] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_AtmScattering] = new AtmScatteringPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_AtmScattering]);
 				break;
-			case RenderPassType_HdrMapping:
-				if(m_allRenderPasses[RenderPassType_HdrMapping] == nullptr)
-					m_allRenderPasses[RenderPassType_HdrMapping] = new HdrMappingPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_HdrMapping]);
+			case RenderPassType::RenderPassType_HdrMapping:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_HdrMapping] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_HdrMapping] = new HdrMappingPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_HdrMapping]);
 				break;
-			case RenderPassType_Blur:
-				if(m_allRenderPasses[RenderPassType_Blur] == nullptr)
-					m_allRenderPasses[RenderPassType_Blur] = new BlurPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Blur]);
+			case RenderPassType::RenderPassType_Blur:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Blur] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Blur] = new BlurPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_Blur]);
 				break;
-			case RenderPassType_Bloom:
-				if(m_allRenderPasses[RenderPassType_Bloom] == nullptr)
-					m_allRenderPasses[RenderPassType_Bloom] = new BloomPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Bloom]);
+			case RenderPassType::RenderPassType_Bloom:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Bloom] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Bloom] = new BloomPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_Bloom]);
 				break;
-			case RenderPassType_BloomComposite:
-				if(m_allRenderPasses[RenderPassType_BloomComposite] == nullptr)
-					m_allRenderPasses[RenderPassType_BloomComposite] = new BloomCompositePass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_BloomComposite]);
+			case RenderPassType::RenderPassType_BloomComposite:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_BloomComposite] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_BloomComposite] = new BloomCompositePass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_BloomComposite]);
 				break;
-			case RenderPassType_LenseFlare:
-				if(m_allRenderPasses[RenderPassType_LenseFlare] == nullptr)
-					m_allRenderPasses[RenderPassType_LenseFlare] = new LenseFlarePass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_LenseFlare]);
+			case RenderPassType::RenderPassType_LenseFlare:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_LenseFlare] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_LenseFlare] = new LenseFlarePass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_LenseFlare]);
 				break;
-			case RenderPassType_LenseFlareComposite:
-				if(m_allRenderPasses[RenderPassType_LenseFlareComposite] == nullptr)
-					m_allRenderPasses[RenderPassType_LenseFlareComposite] = new LenseFlareCompositePass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_LenseFlareComposite]);
+			case RenderPassType::RenderPassType_LenseFlareComposite:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_LenseFlareComposite] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_LenseFlareComposite] = new LenseFlareCompositePass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_LenseFlareComposite]);
 				break;
-			case RenderPassType_Luminance:
-				if(m_allRenderPasses[RenderPassType_Luminance] == nullptr)
-					m_allRenderPasses[RenderPassType_Luminance] = new LuminancePass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Luminance]);
+			case RenderPassType::RenderPassType_Luminance:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Luminance] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Luminance] = new LuminancePass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_Luminance]);
 				break;
-			case RenderPassType_Final:
-				if(m_allRenderPasses[RenderPassType_Final] == nullptr)
-					m_allRenderPasses[RenderPassType_Final] = new FinalPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Final]);
+			case RenderPassType::RenderPassType_Final:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Final] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Final] = new FinalPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_Final]);
 				break;
-			case RenderPassType_GUI:
-				if(m_allRenderPasses[RenderPassType_GUI] == nullptr)
-					m_allRenderPasses[RenderPassType_GUI] = new GUIPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_GUI]);
+			case RenderPassType::RenderPassType_GUI:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_GUI] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_GUI] = new GUIPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_GUI]);
 				guiRenderPassSet = true;
 				break;
-			case RenderPassType_AmbientOcclusion:
-				if(m_allRenderPasses[RenderPassType_AmbientOcclusion] == nullptr)
-					m_allRenderPasses[RenderPassType_AmbientOcclusion] = new AmbientOcclusionPass(*this);
-				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_AmbientOcclusion]);
+			case RenderPassType::RenderPassType_AmbientOcclusion:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_AmbientOcclusion] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_AmbientOcclusion] = new AmbientOcclusionPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_AmbientOcclusion]);
+				break;
+			case RenderPassType::RenderPassType_Tonemapping:
+				if(m_allRenderPasses[RenderPassType::RenderPassType_Tonemapping] == nullptr)
+					m_allRenderPasses[RenderPassType::RenderPassType_Tonemapping] = new TonemappingPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType::RenderPassType_Tonemapping]);
 				break;
 		}
 	}

+ 6 - 3
Praxis3D/Source/RendererFrontend.h

@@ -27,6 +27,7 @@ class RendererFrontend
 	friend class ReflectionPass;
 	friend class ShadowMappingPass;
 	friend class SkyPass;
+	friend class TonemappingPass;
 public:
 	// A handle for a uniform or shader storage buffer
 	struct ShaderBuffer
@@ -80,7 +81,7 @@ public:
 	const inline UniformFrameData &getFrameData() const { return m_frameData; }
 	
 protected:
-	inline void queueForDrawing(const Model::Mesh &p_mesh, const MeshData &p_meshData, const uint32_t p_modelHandle, const uint32_t p_shaderHandle, ShaderUniformUpdater &p_uniformUpdater, const glm::mat4 &p_modelMatrix, const glm::mat4 &p_modelViewProjMatrix)
+	inline void queueForDrawing(const Model::Mesh &p_mesh, const MeshData &p_meshData, const uint32_t p_modelHandle, const uint32_t p_shaderHandle, ShaderUniformUpdater &p_uniformUpdater, const DrawCommandTextureBinding p_textureBindingType, const FaceCullingSettings p_faceCulling, const glm::mat4 &p_modelMatrix, const glm::mat4 &p_modelViewProjMatrix)
 	{
 		// Calculate the sort key, by combining lower 16 bits of shader handle and model handle
 		// sortkey = 64 bits total
@@ -113,11 +114,13 @@ protected:
 				p_meshData.m_materials[MaterialType::MaterialType_Normal].getHandle(),
 				p_meshData.m_materials[MaterialType::MaterialType_Emissive].getHandle(),
 				p_meshData.m_materials[MaterialType::MaterialType_Combined].getHandle(),
+				p_faceCulling,
 				p_meshData.m_materialData,
+				p_textureBindingType,
 				p_meshData.m_textureWrapMode)
 		);
 	}
-	inline void queueForDrawing(const ModelData &p_modelData, const unsigned int p_shaderHandle, ShaderUniformUpdater &p_uniformUpdater, const glm::mat4 &p_modelMatrix, const glm::mat4 &p_viewProjMatrix)
+	inline void queueForDrawing(const ModelData &p_modelData, const unsigned int p_shaderHandle, ShaderUniformUpdater &p_uniformUpdater, const DrawCommandTextureBinding p_textureBindingType, const glm::mat4 &p_modelMatrix, const glm::mat4 &p_viewProjMatrix)
 	{
 		// Get the necessary handles
 		const unsigned int modelHandle = p_modelData.m_model.getHandle();
@@ -130,7 +133,7 @@ protected:
 		{
 			if(p_modelData.m_meshes[meshIndex].m_active)
 			{
-				queueForDrawing(p_modelData.m_model[meshIndex], p_modelData.m_meshes[meshIndex], modelHandle, p_shaderHandle, p_uniformUpdater, p_modelMatrix, modelViewProjMatrix);
+				queueForDrawing(p_modelData.m_model[meshIndex], p_modelData.m_meshes[meshIndex], modelHandle, p_shaderHandle, p_uniformUpdater, p_textureBindingType, p_modelData.m_drawFaceCulling, p_modelMatrix, modelViewProjMatrix);
 			}
 		}
 	}

+ 20 - 0
Praxis3D/Source/RendererScene.cpp

@@ -267,6 +267,9 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 					case Properties::ShadowMappingPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_ShadowMapping);
 						break;
+					case Properties::TonemappingPass:
+						m_renderingPasses.push_back(RenderPassType::RenderPassType_Tonemapping);
+						break;
 				}
 			}
 		}
@@ -1432,6 +1435,23 @@ void RendererScene::receiveData(const DataType p_dataType, void *p_data, const b
 			}
 			break;
 
+		case DataType::DataType_RenderingPasses:
+			{
+				RenderingPasses *renderingPasses = static_cast<RenderingPasses*>(p_data);
+				if(!renderingPasses->empty())
+				{
+					m_renderingPasses = *renderingPasses;
+
+					// Set rendering passes
+					static_cast<RendererSystem *>(m_system)->setRenderingPasses(m_renderingPasses);
+				}
+
+				// Delete the received data if it has been marked for deletion (ownership transfered upon receiving)
+				if(p_deleteAfterReceiving)
+					delete renderingPasses;
+			}
+			break;
+
 		case DataType::DataType_RenderToTexture:
 			{
 				m_renderToTexture = static_cast<bool>(p_data);

+ 3 - 0
Praxis3D/Source/RendererScene.h

@@ -285,6 +285,9 @@ public:
 				case RenderPassType::RenderPassType_ShadowMapping:
 					renderPassTypeProperty = Properties::ShadowMappingPass;
 					break;
+				case RenderPassType::RenderPassType_Tonemapping:
+					renderPassTypeProperty = Properties::TonemappingPass;
+					break;
 			}
 
 			// Add the rendering pass array entry and rendering pass type

+ 105 - 14
Praxis3D/Source/SceneLoader.cpp

@@ -1,10 +1,13 @@
 
+#include <mutex>          // std::mutex
+#include <thread>         // std::thread
 #include <utility>
 #include <vector>
 
 #include "ComponentConstructorInfo.h"
 #include "PropertyLoader.h"
 #include "SceneLoader.h"
+#include "Version.h"
 
 SceneLoader::SceneLoader(const EngineStateType p_engineStateType) : m_engineStateType(p_engineStateType)
 {
@@ -30,6 +33,20 @@ ErrorCode SceneLoader::loadFromProperties(const PropertySet &p_sceneProperties)
 	// Get systems property set
 	auto &systemProperties = p_sceneProperties.getPropertySetByID(Properties::Systems);
 
+	// Get the scene engine version
+	m_sceneEngineVersion.reset();
+	if(const auto &versionPropertySet = p_sceneProperties.getPropertySetByID(Properties::Version); versionPropertySet)
+	{
+		if(const auto &majorVersionProperty = versionPropertySet.getPropertyByID(Properties::Major); majorVersionProperty)
+			m_sceneEngineVersion.m_major = majorVersionProperty.getInt();
+
+		if(const auto &minorVersionProperty = versionPropertySet.getPropertyByID(Properties::Major); minorVersionProperty)
+			m_sceneEngineVersion.m_minor = minorVersionProperty.getInt();
+
+		if(const auto &patchVersionProperty = versionPropertySet.getPropertyByID(Properties::Major); patchVersionProperty)
+			m_sceneEngineVersion.m_patch = patchVersionProperty.getInt();
+	}
+
 	// Iterate over all systems scenes
 	for(int sysIndex = 0; sysIndex < Systems::NumberOfSystems; sysIndex++)
 	{
@@ -134,15 +151,13 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 
 ErrorCode SceneLoader::saveToFile(const std::string p_filename)
 {
-	std::string filename;
-
 	if(!p_filename.empty())
-		filename = Config::filepathVar().map_path + p_filename;
-	else
-		filename = m_filename;
+		m_filename = p_filename;
 
-	if(!filename.empty())
+	if(!m_filename.empty())
 	{
+		std::string filename = Config::filepathVar().map_path + m_filename;
+
 		// Get the world scene required for getting the entity registry
 		WorldScene *worldScene = static_cast<WorldScene *>(m_systemScenes[Systems::World]);
 
@@ -152,6 +167,15 @@ ErrorCode SceneLoader::saveToFile(const std::string p_filename)
 		// Add root property set for the whole file
 		PropertySet rootPropertySet(Properties::Default);
 
+		// Add scene loaded properties
+		rootPropertySet.addProperty(Properties::LoadInBackground, m_loadInBackground);
+
+		// Add engine version properties
+		auto &versionPropertySet = rootPropertySet.addPropertySet(Properties::Version);
+		versionPropertySet.addProperty(Properties::Major, (int)PRAXIS3D_VERSION_MAJOR);
+		versionPropertySet.addProperty(Properties::Minor, (int)PRAXIS3D_VERSION_MINOR);
+		versionPropertySet.addProperty(Properties::Patch, (int)PRAXIS3D_VERSION_PATCH);
+
 		// Add root property set game objects
 		auto &gameObjects = rootPropertySet.addPropertySet(Properties::GameObject);
 
@@ -179,9 +203,6 @@ ErrorCode SceneLoader::saveToFile(const std::string p_filename)
 			exportToProperties(constructionInfo, gameObjectEntry);
 		}
 
-		// Add scene loaded properties
-		rootPropertySet.addProperty(Properties::LoadInBackground, m_loadInBackground);
-
 		// Add root property set for systems
 		auto &rootSystemsPropertySet = rootPropertySet.addPropertySet(Properties::Systems);
 
@@ -242,6 +263,12 @@ ErrorCode SceneLoader::importPrefab(ComponentsConstructionInfo &p_constructionIn
 	// Check if the given filename isn't empty
 	if(!p_filename.empty())
 	{
+		//m_mtx.lock();
+		// Make sure calls from other threads are locked, while current call is in progress
+		// This is needed as the prefab that is being requested might be currently being imported
+		// Mutex prevents duplicate prefabs being loaded, and same data being changed.
+		SpinWait::Lock lock(m_mutex);
+
 		// Search for the given prefab (it might have been loaded before, already)
 		auto prefabIterator = m_prefabs.find(p_filename);
 
@@ -255,10 +282,6 @@ ErrorCode SceneLoader::importPrefab(ComponentsConstructionInfo &p_constructionIn
 		}
 		else // If the prefab doesn't exist in the map, import it
 		{
-			// Make sure calls from other threads are locked, while current call is in progress
-			// This is needed as the prefab that is being requested might be currently being imported
-			// Mutex prevents duplicate prefabs being loaded, and same data being changed.
-			SpinWait::Lock lock(m_mutex);
 
 			// Search for the prefab again, as it might have been imported from another thread call before mutex lock was released
 			auto prefabIteratorNew = m_prefabs.find(p_filename);
@@ -287,6 +310,8 @@ ErrorCode SceneLoader::importPrefab(ComponentsConstructionInfo &p_constructionIn
 				}
 			}
 		}
+
+		//m_mtx.unlock();
 	}
 	else
 		returnError = ErrorCode::Filename_empty;
@@ -672,6 +697,52 @@ void SceneLoader::importFromProperties(GraphicsComponentsConstructionInfo &p_con
 							// Assign the model filename
 							newModelEntry.m_modelName = modelName;
 
+							// Get face culling settings for drawing
+							if(auto faceCullingProperty = modelsProperty.getPropertySet(iModel).getPropertySetByID(Properties::FaceCullingDraw); faceCullingProperty)
+							{
+								// Get the enable flag, if it is present
+								if(auto enableProperty = faceCullingProperty.getPropertyByID(Properties::Enabled); enableProperty)
+									newModelEntry.m_drawFaceCulling.m_faceCullingEnabled = enableProperty.getBool();
+
+								// Get face culling mode
+								if(auto faceCullingModeProperty = faceCullingProperty.getPropertyByID(Properties::Type); faceCullingModeProperty)
+								{
+									switch(faceCullingModeProperty.getID())
+									{
+										case Properties::Front:
+											newModelEntry.m_drawFaceCulling.m_backFaceCulling = false;
+											break;
+										case Properties::Back:
+										default:
+											newModelEntry.m_drawFaceCulling.m_backFaceCulling = true;
+											break;
+									}
+								}
+							}
+
+							// Get face culling settings for shadow mapping
+							if(auto faceCullingProperty = modelsProperty.getPropertySet(iModel).getPropertySetByID(Properties::FaceCullingShadow); faceCullingProperty)
+							{
+								// Get the enable flag, if it is present
+								if(auto enableProperty = faceCullingProperty.getPropertyByID(Properties::Enabled); enableProperty)
+									newModelEntry.m_shadowFaceCulling.m_faceCullingEnabled = enableProperty.getBool();
+
+								// Get face culling mode
+								if(auto faceCullingModeProperty = faceCullingProperty.getPropertyByID(Properties::Type); faceCullingModeProperty)
+								{
+									switch(faceCullingModeProperty.getID())
+									{
+										case Properties::Front:
+											newModelEntry.m_shadowFaceCulling.m_backFaceCulling = false;
+											break;
+										case Properties::Back:
+										default:
+											newModelEntry.m_shadowFaceCulling.m_backFaceCulling = true;
+											break;
+									}
+								}
+							}
+
 							// Get meshes property
 							auto &meshesProperty = modelsProperty.getPropertySet(iModel).getPropertySetByID(Properties::Meshes);
 
@@ -1267,9 +1338,29 @@ void SceneLoader::exportToProperties(const GraphicsComponentsConstructionInfo &p
 			{
 				auto &modelPropertyArrayEntry = modelsPropertySet.addPropertySet(Properties::ArrayEntry);
 
-				// Add model data
+				// Add model filename
 				modelPropertyArrayEntry.addProperty(Properties::PropertyID::Filename, model.m_modelName);
 
+				// Add face culling data for drawing
+				{
+					auto &faceCullingPropertySet = modelPropertyArrayEntry.addPropertySet(Properties::FaceCullingDraw);
+					faceCullingPropertySet.addProperty(Properties::PropertyID::Enabled, model.m_drawFaceCulling.m_faceCullingEnabled);
+					if(model.m_drawFaceCulling.m_backFaceCulling)
+						faceCullingPropertySet.addProperty(Properties::PropertyID::Type, Properties::PropertyID::Back);
+					else
+						faceCullingPropertySet.addProperty(Properties::PropertyID::Type, Properties::PropertyID::Front);
+				}
+
+				// Add face culling data for shadow mapping
+				{
+					auto &faceCullingPropertySet = modelPropertyArrayEntry.addPropertySet(Properties::FaceCullingShadow);
+					faceCullingPropertySet.addProperty(Properties::PropertyID::Enabled, model.m_shadowFaceCulling.m_faceCullingEnabled);
+					if(model.m_shadowFaceCulling.m_backFaceCulling)
+						faceCullingPropertySet.addProperty(Properties::PropertyID::Type, Properties::PropertyID::Back);
+					else
+						faceCullingPropertySet.addProperty(Properties::PropertyID::Type, Properties::PropertyID::Front);
+				}
+
 				// Create Meshes entry
 				auto &meshesPropertySet = modelPropertyArrayEntry.addPropertySet(Properties::PropertyID::Meshes);
 

+ 6 - 0
Praxis3D/Source/SceneLoader.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <mutex>          // std::mutex
+
 #include "ErrorHandlerLocator.h"
 #include "NullSystemObjects.h"
 #include "PropertySet.h"
@@ -102,6 +104,7 @@ private:
 
 	// Mutex used to block calls from other threads while import operation is in progress
 	SpinWait m_mutex;
+	std::mutex m_mtx;
 
 	// Contains all the prefabs that have already been imported before. Saves the time of importing them again, upon requesting
 	std::map<std::string, ComponentsConstructionInfo> m_prefabs;
@@ -126,5 +129,8 @@ private:
 
 	// Denotes whether this is the first time that scenes are loading
 	bool m_firstLoad;
+
+	// Version of the engine that was used to export the scene
+	EngineVersion m_sceneEngineVersion;
 };
 

+ 36 - 9
Praxis3D/Source/ShaderUniforms.h

@@ -1017,43 +1017,70 @@ public:
 class DiffuseTextureUniform : public BaseUniform
 {
 public:
-	DiffuseTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().diffuseTextureUniform, p_shaderHandle) { }
+	DiffuseTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().diffuseTextureUniform, p_shaderHandle), m_uniformSet(false) { }
 
 	void update(const UniformData &p_uniformData)
 	{
-		//glUniform1i(m_uniformHandle, p_uniformData.getDiffuseTexturePos());
-		glUniform1i(m_uniformHandle, MaterialType_Diffuse);
+		if(!m_uniformSet)
+		{
+			m_uniformSet = true;
+			glUniform1i(m_uniformHandle, MaterialType_Diffuse);
+		}
 	}
+
+private:
+	bool m_uniformSet;
 };
 class NormalTextureUniform : public BaseUniform
 {
 public:
-	NormalTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().normalTextureUniform, p_shaderHandle) { }
+	NormalTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().normalTextureUniform, p_shaderHandle), m_uniformSet(false) { }
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Normal);
+		if(!m_uniformSet)
+		{
+			m_uniformSet = true;
+			glUniform1i(m_uniformHandle, MaterialType_Normal);
+		}
 	}
+
+private:
+	bool m_uniformSet;
 };
 class EmissiveTextureUniform : public BaseUniform
 {
 public:
-	EmissiveTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().emissiveTextureUniform, p_shaderHandle) { }
+	EmissiveTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().emissiveTextureUniform, p_shaderHandle), m_uniformSet(false) { }
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Emissive);
+		if(!m_uniformSet)
+		{
+			m_uniformSet = true;
+			glUniform1i(m_uniformHandle, MaterialType_Emissive);
+		}
 	}
+
+private:
+	bool m_uniformSet;
 };
 class CombinedTextureUniform : public BaseUniform
 {
 public:
-	CombinedTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().combinedTextureUniform, p_shaderHandle) { }
+	CombinedTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().combinedTextureUniform, p_shaderHandle), m_uniformSet(false) { }
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Combined);
+		if(!m_uniformSet)
+		{
+			m_uniformSet = true;
+			glUniform1i(m_uniformHandle, MaterialType_Combined);
+		}
 	}
+
+private:
+	bool m_uniformSet;
 };
 
 class NoiseTextureUniform : public BaseUniform

+ 106 - 13
Praxis3D/Source/ShadowMappingPass.h

@@ -42,9 +42,15 @@ public:
 			// Create a property-set used to load the shader
 			PropertySet shaderProperties(Properties::Shaders);
 			shaderProperties.addProperty(Properties::Name, std::string("csmPass"));
-			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_frag_shader);
-			shaderProperties.addProperty(Properties::GeometryShader, Config::rendererVar().csm_pass_geom_shader);
-			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_vert_shader);
+
+#if CSM_USE_MULTILAYER_DRAW
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_layered_frag_shader);
+			shaderProperties.addProperty(Properties::GeometryShader, Config::rendererVar().csm_pass_layered_geom_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_layered_vert_shader);
+#else
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_single_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_single_vert_shader);
+#endif
 
 			// Create the shader
 			m_csmPassShader = Loaders::shader().load(shaderProperties);
@@ -53,7 +59,7 @@ public:
 			if(ErrorCode shaderError = m_csmPassShader->loadToMemory(); shaderError == ErrorCode::Success)
 			{
 				// Disable alpha discard in the shader
-				if(ErrorCode shaderVariableError = m_csmPassShader->setDefineValue(ShaderType::ShaderType_Geometry, Config::shaderVar().define_alpha_discard, 0); shaderVariableError != ErrorCode::Success)
+				if(ErrorCode shaderVariableError = m_csmPassShader->setDefineValue(Config::shaderVar().define_alpha_discard, 0); shaderVariableError != ErrorCode::Success)
 					ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_alpha_discard, ErrorSource::Source_ShadowMappingPass);
 
 				// Set the number of shadow cascades in the shader
@@ -72,9 +78,15 @@ public:
 			// Create a property-set used to load the shader
 			PropertySet shaderProperties(Properties::Shaders);
 			shaderProperties.addProperty(Properties::Name, std::string("csmPass_AlphaDiscard"));
-			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_frag_shader);
-			shaderProperties.addProperty(Properties::GeometryShader, Config::rendererVar().csm_pass_geom_shader);
-			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_vert_shader);
+
+#if CSM_USE_MULTILAYER_DRAW
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_layered_frag_shader);
+			shaderProperties.addProperty(Properties::GeometryShader, Config::rendererVar().csm_pass_layered_geom_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_layered_vert_shader);
+#else
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_single_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_single_vert_shader);
+#endif
 
 			// Create the shader
 			m_csmPassAlphaDiscardShader = Loaders::shader().load(shaderProperties);
@@ -83,7 +95,7 @@ public:
 			if(ErrorCode shaderError = m_csmPassAlphaDiscardShader->loadToMemory(); shaderError == ErrorCode::Success)
 			{
 				// Enable alpha discard in the shader
-				if(ErrorCode shaderVariableError = m_csmPassAlphaDiscardShader->setDefineValue(ShaderType::ShaderType_Geometry, Config::shaderVar().define_alpha_discard, 1); shaderVariableError != ErrorCode::Success)
+				if(ErrorCode shaderVariableError = m_csmPassAlphaDiscardShader->setDefineValue(Config::shaderVar().define_alpha_discard, 1); shaderVariableError != ErrorCode::Success)
 					ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_alpha_discard, ErrorSource::Source_ShadowMappingPass);
 
 				// Set the number of shadow cascades in the shader
@@ -175,9 +187,7 @@ public:
 			if(shadowMappingData.m_zClipping)
 				glEnable(GL_DEPTH_CLAMP);
 
-			// Get known shader details
-			auto csmShaderHandle = m_csmPassShader->getShaderHandle();
-			auto &csmUniformUpdater = m_csmPassShader->getUniformUpdater();
+#if CSM_USE_MULTILAYER_DRAW
 
 			// Iterate over all objects to be rendered with CSM shader
 			for(auto entity : p_sceneObjects.m_models)
@@ -203,18 +213,101 @@ public:
 								if(modelData[modelIndex].m_meshes[meshIndex].m_alphaThreshold > 0.0f)
 								{
 									// ALPHA DISCARD enabled
-									m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), m_csmPassAlphaDiscardShader->getShaderHandle(), m_csmPassAlphaDiscardShader->getUniformUpdater(), modelMatrix, modelMatrix);
+									m_renderer.queueForDrawing(
+										modelData[modelIndex].m_model[meshIndex], 
+										modelData[modelIndex].m_meshes[meshIndex], 
+										modelData[modelIndex].m_model.getHandle(), 
+										m_csmPassAlphaDiscardShader->getShaderHandle(), 
+										m_csmPassAlphaDiscardShader->getUniformUpdater(), 
+										DrawCommandTextureBinding::DrawCommandTextureBinding_DiffuseOnly,
+										modelData[modelIndex].m_shadowFaceCulling, 
+										modelMatrix, 
+										modelMatrix);
 								}
 								else
 								{
 									// ALPHA DISCARD disabled
-									m_renderer.queueForDrawing(modelData[modelIndex].m_model[meshIndex], modelData[modelIndex].m_meshes[meshIndex], modelData[modelIndex].m_model.getHandle(), m_csmPassShader->getShaderHandle(), m_csmPassShader->getUniformUpdater(), modelMatrix, modelMatrix);
+									m_renderer.queueForDrawing(
+										modelData[modelIndex].m_model[meshIndex], 
+										modelData[modelIndex].m_meshes[meshIndex], 
+										modelData[modelIndex].m_model.getHandle(), 
+										m_csmPassShader->getShaderHandle(), 
+										m_csmPassShader->getUniformUpdater(), 
+										DrawCommandTextureBinding::DrawCommandTextureBinding_None,
+										modelData[modelIndex].m_shadowFaceCulling,
+										modelMatrix, 
+										modelMatrix);
+								}
+							}
+						}
+					}
+				}
+			}
+#else
+			for(decltype(m_csmDataSet.size()) i = 0, size = m_csmDataSet.size(); i < size; i++)
+			{
+				glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_renderer.m_backend.getCSMFramebuffer()->m_depthBuffers, 0, (GLint)i);
+				glClear(GL_DEPTH_BUFFER_BIT);	// Make sure to clear the depth buffer for the new frame
+
+				// Iterate over all objects to be rendered with CSM shader
+				for(auto entity : p_sceneObjects.m_models)
+				{
+					ModelComponent &model = p_sceneObjects.m_models.get<ModelComponent>(entity);
+					if(model.isObjectActive())
+					{
+						SpatialComponent &spatialData = p_sceneObjects.m_models.get<SpatialComponent>(entity);
+						auto &modelData = model.getModelData();
+
+						// Go over each model
+						for(decltype(modelData.size()) modelIndex = 0, modelSize = modelData.size(); modelIndex < modelSize; modelIndex++)
+						{
+							// Calculate model-view-projection matrix
+							const glm::mat4 &modelMatrix = m_csmDataSet[i].m_lightSpaceMatrix * p_sceneObjects.m_models.get<SpatialComponent>(entity).getSpatialDataChangeManager().getWorldTransformWithScale();
+
+							// Go over each mesh
+							for(decltype(modelData[modelIndex].m_model.getNumMeshes()) meshIndex = 0, meshSize = modelData[modelIndex].m_model.getNumMeshes(); meshIndex < meshSize; meshIndex++)
+							{
+								// Only draw active meshes
+								if(modelData[modelIndex].m_meshes[meshIndex].m_active)
+								{
+									if(modelData[modelIndex].m_meshes[meshIndex].m_alphaThreshold > 0.0f)
+									{
+										// ALPHA DISCARD enabled
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											m_csmPassAlphaDiscardShader->getShaderHandle(),
+											m_csmPassAlphaDiscardShader->getUniformUpdater(),
+											DrawCommandTextureBinding::DrawCommandTextureBinding_DiffuseOnly,
+											modelData[modelIndex].m_shadowFaceCulling,
+											modelMatrix, 
+											modelMatrix);
+									}
+									else
+									{
+										// ALPHA DISCARD disabled
+										m_renderer.queueForDrawing(
+											modelData[modelIndex].m_model[meshIndex], 
+											modelData[modelIndex].m_meshes[meshIndex], 
+											modelData[modelIndex].m_model.getHandle(), 
+											m_csmPassShader->getShaderHandle(),
+											m_csmPassShader->getUniformUpdater(),
+											DrawCommandTextureBinding::DrawCommandTextureBinding_None,
+											modelData[modelIndex].m_shadowFaceCulling,
+											modelMatrix, 
+											modelMatrix);
+									}
 								}
 							}
 						}
 					}
 				}
+
+				// Pass all the draw commands to be executed
+				m_renderer.passDrawCommandsToBackend();
 			}
+#endif
 
 			// Iterate over all objects to be rendered with a custom shader
 			//for(auto entity : p_sceneObjects.m_modelsWithShaders)

+ 8 - 6
Praxis3D/Source/SpinWait.cpp

@@ -17,20 +17,22 @@ SpinWait::SpinWait()
 	// exponential backoff technique, and supports cooperative behavior in case 
 	// of oversubscription)
 
-	int result = ::InitializeCriticalSectionAndSpinCount(reinterpret_cast<LPCRITICAL_SECTION>(m_lock), 1000);
-
-	//result;
+	//auto result = ::InitializeCriticalSectionAndSpinCount(reinterpret_cast<LPCRITICAL_SECTION>(m_lock), 1000);
+	auto result = ::InitializeCriticalSectionAndSpinCount(&m_lock, 1000);
 }
 SpinWait::~SpinWait()
 {
-	::DeleteCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_lock));
+	//::DeleteCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_lock));
+	::DeleteCriticalSection(&m_lock);
 }
 
 SpinWait::Lock::Lock(SpinWait &p_spinWait, bool p_readOnly) : m_spinWait(p_spinWait)
 {
-	::EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_spinWait.m_lock));
+	//::EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_spinWait.m_lock));
+	::EnterCriticalSection(&m_spinWait.m_lock);
 }
 SpinWait::Lock::~Lock()
 {
-	::LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_spinWait.m_lock));
+	//::LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_spinWait.m_lock));
+	::LeaveCriticalSection(&m_spinWait.m_lock);
 }

+ 4 - 1
Praxis3D/Source/SpinWait.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <windows.h>
+
 class SpinWait
 {
 	friend class Lock;
@@ -23,6 +25,7 @@ public:
 	};
 
 protected:
-	unsigned int m_lock[8];
+	//unsigned int m_lock[8];
+	CRITICAL_SECTION m_lock;
 };
 

+ 3 - 3
Praxis3D/Source/TaskManager.h

@@ -134,7 +134,7 @@ public:
 
 		for(unsigned int currentTask = 0; currentTask < p_count; currentTask++)
 		{
-#ifdef SETTING_MULTITHREADING_ENABLED
+#if SETTING_MULTITHREADING_ENABLED
 			if(p_tasks[currentTask]->isPrimaryThreadOnly())
 			{
 				m_primaryThreadSystemTaskList.push_back(p_tasks[currentTask]);
@@ -173,7 +173,7 @@ public:
 	inline void startBackgroundThread(const Function& p_func)
 	{
 		// If multi-threading is enabled 
-#ifdef SETTING_MULTITHREADING_ENABLED
+#if SETTING_MULTITHREADING_ENABLED
 		m_backgroundTaskGroup.run(p_func);
 #else
 		p_func();
@@ -186,7 +186,7 @@ public:
 	inline void parallelFor(Index p_first, Index p_last, Index p_step, const Function& p_func)
 	{
 		// If multi-threading is enabled 
-#ifdef SETTING_MULTITHREADING_ENABLED
+#if SETTING_MULTITHREADING_ENABLED
 		tbb::parallel_for(p_first, p_last, p_step, p_func);
 #else
 		for(Index i = p_first; i < p_last; i += p_step)

+ 108 - 0
Praxis3D/Source/TonemappingPass.h

@@ -0,0 +1,108 @@
+#pragma once
+
+#include "GraphicsDataSets.h"
+#include "RenderPassBase.h"
+
+class TonemappingPass : public RenderPass
+{
+public:
+	TonemappingPass(RendererFrontend &p_renderer) : 
+		RenderPass(p_renderer, RenderPassType::RenderPassType_Tonemapping),
+		m_shaderTonemappingPass(nullptr)
+	{
+		m_tonemappingMethod = 0;
+	}
+
+	~TonemappingPass() { }
+
+	ErrorCode init()
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		m_name = "Tonemapping Pass";
+
+		m_tonemappingMethod = Config::graphicsVar().tonemap_method;
+
+		// Load tonemapping pass shader
+		{
+			// Create a property-set used to load final pass shader
+			PropertySet tonemappingShaderProperties(Properties::Shaders);
+			tonemappingShaderProperties.addProperty(Properties::Name, std::string("tonemapping_pass"));
+			tonemappingShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().tonemapping_frag_shader);
+			tonemappingShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().tonemapping_vert_shader);
+
+			// Create the shader
+			m_shaderTonemappingPass = Loaders::shader().load(tonemappingShaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = loadTonemappingPassShaderToMemory(m_shaderTonemappingPass))
+				returnError = shaderError;
+		}
+
+		// Check for errors and log either a successful or a failed initialization
+		if(returnError == ErrorCode::Success)
+		{
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_success, ErrorSource::Source_FinalPass);
+			setInitialized(true);
+		}
+		else
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_failure, ErrorSource::Source_FinalPass);
+
+		return returnError;
+	}
+
+	void update(RenderPassData &p_renderPassData, const SceneObjects &p_sceneObjects, const float p_deltaTime)
+	{
+		// If tonemapping method has changed, reload the Tonemapping Pass shader
+		if(m_tonemappingMethod != Config::graphicsVar().tonemap_method)
+		{
+			m_tonemappingMethod = Config::graphicsVar().tonemap_method;
+
+			// Reset the loaded flag of the shader, so it can be reloaded to memory
+			m_shaderTonemappingPass->resetLoadedToVideoMemoryFlag();
+
+			// Reload the shader to memory
+			loadTonemappingPassShaderToMemory(m_shaderTonemappingPass);
+
+			// Process shader load command
+			m_renderer.passLoadCommandsToBackend();
+		}
+
+		glDisable(GL_DEPTH_TEST);
+
+		// Bind input texture for reading so it can be accessed in the shaders
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GBufferTextureType::GBufferInputTexture);
+
+		// Bind output texture for drawing to
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(p_renderPassData.getColorOutputMap());
+		
+		// Queue and render a full screen quad using a final pass shader
+		m_renderer.queueForDrawing(m_shaderTonemappingPass->getShaderHandle(), m_shaderTonemappingPass->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+		m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+		p_renderPassData.swapColorInputOutputMaps();
+	}
+
+private:
+	ErrorCode loadTonemappingPassShaderToMemory(ShaderLoader::ShaderProgram *p_shader)
+	{
+		ErrorCode shaderError = p_shader->loadToMemory();
+
+		if(shaderError == ErrorCode::Success)
+		{
+			// Set the tonemapping method
+			if(ErrorCode shaderVariableError = p_shader->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_tonemappingMethod, m_tonemappingMethod); shaderVariableError != ErrorCode::Success)
+				ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_tonemappingMethod, ErrorSource::Source_FinalPass);
+
+			// Queue the shader to be loaded to GPU
+			m_renderer.queueForLoading(*p_shader);
+		}
+
+		return shaderError;
+	}
+
+	ShaderLoader::ShaderProgram *m_shaderTonemappingPass;
+
+	// Tonemapping settings
+	int m_tonemappingMethod;
+};

+ 2 - 2
Praxis3D/Source/Version.h

@@ -5,11 +5,11 @@
 
 #define PRAXIS3D_COMMIT_DATE_YEAR 2024
 #define PRAXIS3D_COMMIT_DATE_MONTH 02
-#define PRAXIS3D_COMMIT_DATE_DAY 13
+#define PRAXIS3D_COMMIT_DATE_DAY 25
 
 #define PRAXIS3D_VERSION_MAJOR 0
 #define PRAXIS3D_VERSION_MINOR 2
-#define PRAXIS3D_VERSION_PATCH 4
+#define PRAXIS3D_VERSION_PATCH 5
 
 #define PRAXIS3D_COMMIT_DATE ((PRAXIS3D_COMMIT_DATE_YEAR << 16) | (PRAXIS3D_COMMIT_DATE_MONTH << 8) | PRAXIS3D_COMMIT_DATE_DAY)
 #define PRAXIS3D_COMMIT_DATE_STRING VERSION_TO_STRING(PRAXIS3D_COMMIT_DATE_YEAR) "-" VERSION_TO_STRING(PRAXIS3D_COMMIT_DATE_MONTH) "-" VERSION_TO_STRING(PRAXIS3D_COMMIT_DATE_DAY)

+ 11 - 3
Praxis3D/Source/Window.cpp

@@ -134,10 +134,18 @@ ErrorCode Window::createWindow()
 	if(Config::windowVar().resizable)
 		windowFlags |= SDL_WindowFlags::SDL_WINDOW_RESIZABLE;
 
+	// Define window position to either a set value or a centered position
+	const int windowPositionX = Config::windowVar().window_position_centered ? SDL_WINDOWPOS_CENTERED : Config::windowVar().window_position_x;
+	const int windowPositionY = Config::windowVar().window_position_centered ? SDL_WINDOWPOS_CENTERED : Config::windowVar().window_position_y;
+
 	// Spawn a window
-	m_SDLWindow = SDL_CreateWindow(Config::windowVar().name.c_str(),
-								   Config::windowVar().window_position_x, Config::windowVar().window_position_y,
-								   Config::graphicsVar().current_resolution_x, Config::graphicsVar().current_resolution_y, windowFlags);
+	m_SDLWindow = SDL_CreateWindow(	
+		Config::windowVar().name.c_str(),
+		windowPositionX, 
+		windowPositionY,
+		Config::graphicsVar().current_resolution_x, 
+		Config::graphicsVar().current_resolution_y, 
+		windowFlags);
 
 	// Check if the creation of the window was successful
 	if(!m_SDLWindow)

+ 4 - 0
Praxis3D/Source/WorldScene.h

@@ -30,11 +30,15 @@ struct WorldComponentsConstructionInfo
 	void deleteConstructionInfo()
 	{
 		if(m_spatialConstructionInfo != nullptr)
+		{
 			delete m_spatialConstructionInfo;
+		}
 		m_spatialConstructionInfo = nullptr;
 
 		if(m_objectMaterialConstructionInfo != nullptr)
+		{
 			delete m_objectMaterialConstructionInfo;
+		}
 		m_objectMaterialConstructionInfo = nullptr;
 	}
 

+ 1050 - 24
Praxis3D/imgui.ini

@@ -4,8 +4,8 @@ Size=400,400
 Collapsed=0
 
 [Window][Dear ImGui Demo]
-Pos=385,203
-Size=461,545
+Pos=740,181
+Size=799,986
 Collapsed=0
 
 [Window][Dear ImGui Stack Tool]
@@ -85,25 +85,25 @@ Collapsed=0
 
 [Window][##LeftWindow]
 Pos=0,69
-Size=329,1092
+Size=342,1086
 Collapsed=0
 DockId=0x00000001,0
 
 [Window][##RightWindow]
-Pos=1823,69
-Size=512,1309
+Pos=1797,69
+Size=538,1309
 Collapsed=0
 DockId=0x00000008,0
 
 [Window][##BottomWindow]
-Pos=0,1163
-Size=1821,215
+Pos=0,1157
+Size=1795,221
 Collapsed=0
 DockId=0x00000006,0
 
 [Window][##CenterWindow]
-Pos=331,69
-Size=1490,1092
+Pos=344,69
+Size=1451,1086
 Collapsed=0
 DockId=0x00000004,0
 
@@ -128,12 +128,12 @@ Size=666,477
 Collapsed=0
 
 [Window][Dear ImGui Metrics/Debugger]
-Pos=537,149
+Pos=107,298
 Size=608,708
 Collapsed=0
 
 [Window][Dear ImGui Debug Log]
-Pos=60,60
+Pos=210,725
 Size=623,156
 Collapsed=0
 
@@ -193,7 +193,7 @@ Size=240,71
 Collapsed=0
 
 [Window][Open a texture file##OpenTextureFileFileDialog]
-Pos=394,273
+Pos=400,192
 Size=1007,703
 Collapsed=0
 
@@ -213,7 +213,7 @@ Size=855,618
 Collapsed=0
 
 [Window][##AddEntityPopup]
-Pos=281,818
+Pos=288,973
 Size=400,155
 Collapsed=0
 
@@ -223,8 +223,8 @@ Size=801,605
 Collapsed=0
 
 [Window][##LoadingStatusWindow]
-Pos=1044,591
-Size=32,32
+Pos=1037,588
+Size=64,70
 Collapsed=0
 
 [Window][Open a shader file##OpenShaderFileFileDialog]
@@ -233,12 +233,12 @@ Size=920,703
 Collapsed=0
 
 [Window][Edit Map File##fileBrowserEditMap]
-Pos=522,320
+Pos=165,174
 Size=1007,705
 Collapsed=0
 
 [Window][##AboutWindow]
-Size=2335,1378
+Size=1920,1080
 Collapsed=0
 
 [Window][ExampleMapInfo]
@@ -247,6 +247,7 @@ Size=80,71
 Collapsed=0
 
 [Window][##ExampleMapInfo]
+Pos=0,0
 Size=2335,1378
 Collapsed=0
 
@@ -265,6 +266,11 @@ Pos=432,261
 Size=916,583
 Collapsed=0
 
+[Window][The file Already Exist !##Save prefabSavePrefabFileDialogOverWriteDialog]
+Pos=770,526
+Size=240,71
+Collapsed=0
+
 [Table][0x9A4BDFDE,4]
 RefScale=13
 Column 0  Sort=0v
@@ -3885,14 +3891,1034 @@ Column 0  Sort=0v
 RefScale=13
 Column 0  Sort=0v
 
+[Table][0x33C71CF4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5FAE8E53,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA432C39C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x92BAA263,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6BBDD999,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF308FA43,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE062C906,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x03B4C8C8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF2DCEE38,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4E881089,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE7A0A7D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5A758314,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD0D172AB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEB07298B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x56243383,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD51A227A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x166FA8EE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE632331,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x287AE2E4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA5AFE19D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBDE0AE95,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3DAB6FC5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x15DA80B1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA8505BED,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x507B4CE1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x06D7C046,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDCE58C4D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD2A85013,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCC2D177E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA1111A7C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA0BD8383,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8A332192,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6981BA88,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x9D533706,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1825776F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xFD34901C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB5B479F7,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x110AEFF6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x981C7867,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1A142637,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2EC054B4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD3FF2A4D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC1ABF42F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE92DC287,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3BC049D4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x847D8595,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAF90E7DC,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x08F0104D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x62A4681E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x032768B5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x41D61C61,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x48287323,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCD042832,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4503670E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4121B584,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD7F85A4F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBB91C8E8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x52D4015E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD3DA88C5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD0BB54CA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA7A61A8A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCBCF882D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x228A419B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA384C800,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE3B07F8E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x99F4CF9F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x168F385A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD7BA69C6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1186256F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDD65F96B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x424C0E65,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x16CEAF1B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCDC63E47,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x32DE8CB2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBEF553AE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCB5D2714,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x01CE2AFB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB56A2CA4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4340A23E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA10758F9,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF8E516EE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x380E612A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF31961D0,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x239192C5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x886964FA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCDAAE504,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDD106AA6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x34E96C9D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x93749033,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x95FEF49E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF6613627,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC35E4244,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA97FE6B0,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x53C86E24,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3FA1FC83,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD6E43535,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE4A8D085,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3E92FBD5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4C7A5768,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2013C5CF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA720A27B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x18F9287F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA717CEFF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCFF4C587,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x71F98769,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDF42AA91,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x59D3E052,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x557E53CE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3809FBAE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x038DA4DB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6FE4367C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE160AC53,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCACAB270,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA9B0F455,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x39D12975,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBC61D7AF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC79325AB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x427E157B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2C321DD4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x798598D2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x67F10D88,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x40A3689B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5306E10C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDE022BCE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5B2E70DF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE256BD0,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD27FBAA8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1ACC8EA8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x453620C2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEA891722,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD78BC02F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4D4359BE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF1A0DDF7,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD18FEDCB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB8F75E66,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x61DB0F11,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x833EB086,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0612EB97,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x11514F52,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE8E390E6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xACF5F5E2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x93613FA2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC09C3637,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA2905373,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF6BFE6F1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB579DE6F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA693DC18,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1C2258D4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5FE4604A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEFAB9517,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2B5EB3B7,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x68988B29,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAD47DCB9,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x289051D1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6AED983B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x292BA0A5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEFC1C32A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xED5189DE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE97B140,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x7B590282,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x74E2C758,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x38BA4DA1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCDC3B97A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8E1B0E7B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x87334D30,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6BCF149A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x85F08D7E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBDD7E94F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD76461BF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4E5E248C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xFE11D1D1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x327BC88E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x71BDF010,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8D19804D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x487F7DDD,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6CD47FAA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x7C5439A2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x52EC0831,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x385F80C1,4]
+RefScale=13
+Column 3  Sort=0^
+
+[Table][0x049A2D75,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1BD526BA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1696E074,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x35A87F18,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x71BE1A1C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x077D8F5C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF77B1613,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3519D4C2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x481A785A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC3677346,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCBC0A8A5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x6DD72AB2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x69F5F838,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0764A242,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x01BEB815,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE8FB71A3,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2E11122C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0CF22D86,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x94EA57FB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF3D2A49A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1E79876E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEDF04AAD,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1A5B55E4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1DCDA705,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC23185C3,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4FC6F90F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x938222AA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x45F859A1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2A37C0CE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x666C9CB5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE1AED7AE,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x995AD01A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xFCAC44DF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF99317D6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB718C5C6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE429A3B6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x8C5D4CD1,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1EAA4BED,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE704EA5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xE6AA6A9B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA683BC58,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x46B1F081,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x411B40E5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0522E2E2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF6AB2F21,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x413487E6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x800EB9F3,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x46E4DA7C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEC8C37DC,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xED76F750,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4BBDD74F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0FABB24B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDF0C41DC,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x83CF99A2,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1FC72488,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5C011C16,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xEC4EE94B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x1D189808,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAC122E2C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2A77261F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4A18A9A4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x09DE913A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xCF34F2B5,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0E0ECCA0,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB9916467,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xD6282766,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x7C5C477E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xB4D6B158,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF0C0D45C,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4838733E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4D93B0F7,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4230D77D,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x64338486,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xC8F45168,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x06B95423,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x255485B8,4]
+RefScale=13
+Column 0  Sort=0v
+
 [Docking][Data]
-DockSpace         ID=0x8B93E3BD Pos=0,69 Size=2335,1309 Split=X
-  DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1821,1011 Split=Y
-    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,794 Split=X
-      DockNode    ID=0x00000001 Parent=0x00000005 SizeRef=329,1011 Selected=0xB1B21E90
-      DockNode    ID=0x00000003 Parent=0x00000005 SizeRef=1490,1011 Split=X
+DockSpace         ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,69 Size=2335,1309 Split=X
+  DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1380,1011 Split=Y
+    DockNode      ID=0x00000005 Parent=0x00000007 SizeRef=1920,1148 Split=X
+      DockNode    ID=0x00000001 Parent=0x00000005 SizeRef=342,1011 Selected=0xB1B21E90
+      DockNode    ID=0x00000003 Parent=0x00000005 SizeRef=1676,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,215 Selected=0x51C0BD29
-  DockNode        ID=0x00000008 Parent=0x8B93E3BD SizeRef=512,1011 Selected=0x96D2BCA2
+    DockNode      ID=0x00000006 Parent=0x00000007 SizeRef=1920,221 Selected=0x51C0BD29
+  DockNode        ID=0x00000008 Parent=0x8B93E3BD SizeRef=538,1011 Selected=0x96D2BCA2
 

Some files were not shown because too many files changed in this diff