Przeglądaj źródła

Merge branch 'master' into master

Marco Bellan 8 lat temu
rodzic
commit
d6d4efa5f3
100 zmienionych plików z 4424 dodań i 1751 usunięć
  1. 12 0
      Data/Raw/Engine/DataList.json
  2. 1 9
      Data/Raw/Engine/Includes/ImageBasedLighting.bslinc
  3. 72 2
      Data/Raw/Engine/Includes/SHCommon.bslinc
  4. 40 0
      Data/Raw/Engine/Shaders/Clear.bsl
  5. 253 0
      Data/Raw/Engine/Shaders/IrradianceEvaluate.bsl
  6. 1 1
      Data/Raw/Engine/Shaders/Skybox.bsl
  7. 75 0
      Data/Raw/Engine/Shaders/TetrahedraRender.bsl
  8. 0 2
      Data/Raw/Engine/Shaders/TiledDeferredImageBasedLighting.bsl
  9. 0 2
      Data/Raw/Engine/Shaders/Transparent.bsl
  10. 44 6
      Documentation/Manuals/Native/customRenderer.md
  11. 8 3
      Source/BansheeCore/CMakeSources.cmake
  12. 57 21
      Source/BansheeCore/Include/BsAnimationClip.h
  13. 65 3
      Source/BansheeCore/Include/BsAnimationCurve.h
  14. 13 3
      Source/BansheeCore/Include/BsAnimationUtility.h
  15. 4 10
      Source/BansheeCore/Include/BsCCamera.h
  16. 19 19
      Source/BansheeCore/Include/BsCLight.h
  17. 98 0
      Source/BansheeCore/Include/BsCLightProbeVolume.h
  18. 12 7
      Source/BansheeCore/Include/BsCLightProbeVolumeRTTI.h
  19. 3 3
      Source/BansheeCore/Include/BsCReflectionProbe.h
  20. 36 67
      Source/BansheeCore/Include/BsCamera.h
  21. 2 4
      Source/BansheeCore/Include/BsCameraRTTI.h
  22. 17 6
      Source/BansheeCore/Include/BsCorePrerequisites.h
  23. 22 22
      Source/BansheeCore/Include/BsHardwareBuffer.h
  24. 71 0
      Source/BansheeCore/Include/BsIBLUtility.h
  25. 2 2
      Source/BansheeCore/Include/BsLight.h
  26. 135 19
      Source/BansheeCore/Include/BsLightProbeVolume.h
  27. 124 2
      Source/BansheeCore/Include/BsLightProbeVolumeRTTI.h
  28. 150 100
      Source/BansheeCore/Include/BsPixelData.h
  29. 0 3
      Source/BansheeCore/Include/BsPixelUtil.h
  30. 0 55
      Source/BansheeCore/Include/BsPostProcessSettings.h
  31. 2 2
      Source/BansheeCore/Include/BsProfilerGPU.h
  32. 52 31
      Source/BansheeCore/Include/BsReflectionProbe.h
  33. 12 1
      Source/BansheeCore/Include/BsReflectionProbeRTTI.h
  34. 64 18
      Source/BansheeCore/Include/BsRenderSettings.h
  35. 19 14
      Source/BansheeCore/Include/BsRenderSettingsRTTI.h
  36. 1 1
      Source/BansheeCore/Include/BsRenderable.h
  37. 102 10
      Source/BansheeCore/Include/BsRenderer.h
  38. 21 0
      Source/BansheeCore/Include/BsSceneManager.h
  39. 34 3
      Source/BansheeCore/Include/BsSkybox.h
  40. 14 2
      Source/BansheeCore/Include/BsSkyboxRTTI.h
  41. 1 1
      Source/BansheeCore/Include/BsTexture.h
  42. 0 1
      Source/BansheeCore/Source/BsAnimationClip.cpp
  43. 3 3
      Source/BansheeCore/Source/BsAnimationCurve.cpp
  44. 1 1
      Source/BansheeCore/Source/BsAnimationManager.cpp
  45. 108 18
      Source/BansheeCore/Source/BsAnimationUtility.cpp
  46. 2 1
      Source/BansheeCore/Source/BsCLight.cpp
  47. 72 0
      Source/BansheeCore/Source/BsCLightProbeVolume.cpp
  48. 10 0
      Source/BansheeCore/Source/BsCReflectionProbe.cpp
  49. 9 22
      Source/BansheeCore/Source/BsCamera.cpp
  50. 4 0
      Source/BansheeCore/Source/BsCoreApplication.cpp
  51. 14 0
      Source/BansheeCore/Source/BsIBLUtility.cpp
  52. 353 72
      Source/BansheeCore/Source/BsLightProbeVolume.cpp
  53. 5 2
      Source/BansheeCore/Source/BsMaterialRTTI.cpp
  54. 671 256
      Source/BansheeCore/Source/BsPixelUtil.cpp
  55. 0 17
      Source/BansheeCore/Source/BsPostProcessSettings.cpp
  56. 1 1
      Source/BansheeCore/Source/BsProfilerGPU.cpp
  57. 95 39
      Source/BansheeCore/Source/BsReflectionProbe.cpp
  58. 26 11
      Source/BansheeCore/Source/BsRenderSettings.cpp
  59. 126 0
      Source/BansheeCore/Source/BsRenderer.cpp
  60. 3 4
      Source/BansheeCore/Source/BsRendererMeshData.cpp
  61. 2 2
      Source/BansheeCore/Source/BsResources.cpp
  62. 23 1
      Source/BansheeCore/Source/BsSceneManager.cpp
  63. 91 6
      Source/BansheeCore/Source/BsSkybox.cpp
  64. 1 1
      Source/BansheeCore/Source/BsTextureImportOptions.cpp
  65. 4 4
      Source/BansheeCore/Source/BsTextureManager.cpp
  66. 1 1
      Source/BansheeCore/Source/Win32/BsWin32Platform.cpp
  67. 121 68
      Source/BansheeD3D11RenderAPI/Source/BsD3D11Mappings.cpp
  68. 1 1
      Source/BansheeD3D11RenderAPI/Source/BsD3D11RenderWindow.cpp
  69. 3 1
      Source/BansheeEditor/Source/BsEditorWindowBase.cpp
  70. 0 7
      Source/BansheeEngine/CMakeSources.cmake
  71. 0 94
      Source/BansheeEngine/Include/BsLightProbeCache.h
  72. 7 8
      Source/BansheeEngine/Include/BsPrerequisites.h
  73. 166 8
      Source/BansheeEngine/Include/BsRendererMaterial.h
  74. 1 0
      Source/BansheeEngine/Include/BsRendererMaterialManager.h
  75. 52 21
      Source/BansheeEngine/Include/BsRendererUtility.h
  76. 0 3
      Source/BansheeEngine/Source/BsApplication.cpp
  77. 4 4
      Source/BansheeEngine/Source/BsBuiltinResources.cpp
  78. 0 277
      Source/BansheeEngine/Source/BsLightProbeCache.cpp
  79. 70 0
      Source/BansheeEngine/Source/BsRendererMaterial.cpp
  80. 50 10
      Source/BansheeEngine/Source/BsRendererMaterialManager.cpp
  81. 118 73
      Source/BansheeEngine/Source/BsRendererUtility.cpp
  82. 2 1
      Source/BansheeFBXImporter/Include/BsFBXImportData.h
  83. 59 18
      Source/BansheeFBXImporter/Source/BsFBXImporter.cpp
  84. 1 1
      Source/BansheeFBXImporter/Source/BsFBXUtility.cpp
  85. 2 2
      Source/BansheeFontImporter/Source/BsFontImporter.cpp
  86. 41 13
      Source/BansheeFreeImgImporter/Source/BsFreeImgImporter.cpp
  87. 276 160
      Source/BansheeGLRenderAPI/Source/BsGLPixelFormat.cpp
  88. 4 4
      Source/BansheeGLRenderAPI/Source/BsGLRenderTexture.cpp
  89. 1 1
      Source/BansheeSL/Source/BsSLFXCompiler.cpp
  90. 20 5
      Source/BansheeUtility/Include/BsBitwise.h
  91. 25 1
      Source/BansheeUtility/Include/BsStringID.h
  92. 9 0
      Source/BansheeUtility/Include/BsVectorNI.h
  93. 3 3
      Source/BansheeVulkanRenderAPI/Source/BsVulkanTextureManager.cpp
  94. 74 18
      Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp
  95. 1 0
      Source/CMake/GenerateScriptBindings.cmake
  96. 10 3
      Source/CMakeLists.txt
  97. 2 1
      Source/Examples/ExampleGettingStarted/Source/Main.cpp
  98. 2 2
      Source/Examples/ExampleLowLevelRendering/Source/Main.cpp
  99. 1 1
      Source/Examples/ExamplePhysicallyBasedShading/Source/Main.cpp
  100. 15 25
      Source/MBansheeEditor/Inspectors/CameraInspector.cs

+ 12 - 0
Data/Raw/Engine/DataList.json

@@ -345,6 +345,18 @@
         {
             "Path": "PPSSRResolve.bsl",
             "UUID": "86b9699c-162d-4729-9985-c94e0f45bacb"
+        },
+        {
+            "Path": "TetrahedraRender.bsl",
+            "UUID": "5912b0ab-b2be-4c0e-a039-91fcc2d3e536"
+        },
+        {
+            "Path": "IrradianceEvaluate.bsl",
+            "UUID": "6f6ed59d-b94b-428e-91bc-82c94ed48892"
+        },
+        {
+            "Path": "Clear.bsl",
+            "UUID": "5a325167-5bab-4925-8b50-95434448930f"
         }
     ],
     "Skin": [

+ 1 - 9
Data/Raw/Engine/Includes/ImageBasedLighting.bslinc

@@ -25,9 +25,6 @@ mixin ImageBasedLighting
 		TextureCube gSkyReflectionTex;
 		SamplerState gSkyReflectionSamp;
 		
-		TextureCube gSkyIrradianceTex;
-		SamplerState gSkyIrradianceSamp;
-		
 		TextureCubeArray gReflProbeCubemaps;
 		SamplerState gReflProbeSamp;
 		
@@ -52,12 +49,7 @@ mixin ImageBasedLighting
 			uint gSkyCubemapNumMips;
 			float gSkyBrightness;
 		}	
-		
-		float3 getSkyIndirectDiffuse(float3 dir)
-		{
-			return gSkyIrradianceTex.SampleLevel(gSkyIrradianceSamp, dir, 0).rgb * gSkyBrightness;
-		}
-		
+
 		float getSphereReflectionContribution(float normalizedDistance)
 		{			
 			// If closer than 60% to the probe radius, then full contribution is used.

+ 72 - 2
Data/Raw/Engine/Includes/SHCommon.bslinc

@@ -45,12 +45,26 @@ mixin SHCommon
 			v.v6 = 0;
 		}
 		
+		void SHZero(inout SHVector5RGB v)
+		{
+			SHZero(v.R);
+			SHZero(v.G);
+			SHZero(v.B);
+		}				
+		
 		void SHZero(inout SHVector3 v)
 		{
 			v.v0 = 0;
 			v.v1 = 0;
 			v.v2 = 0;
-		}	
+		}
+		
+		void SHZero(inout SHVector3RGB v)
+		{
+			SHZero(v.R);
+			SHZero(v.G);
+			SHZero(v.B);
+		}		
 
 		void SHMultiplyAdd(inout SHVector5 lhs, SHVector5 rhs, float c)
 		{
@@ -70,6 +84,20 @@ mixin SHCommon
 			lhs.v2 += rhs.v2 * c;
 		}		
 		
+		void SHMultiplyAdd(inout SHVector5RGB lhs, SHVector5RGB rhs, float c)
+		{
+			SHMultiplyAdd(lhs.R, rhs.R, c);
+			SHMultiplyAdd(lhs.G, rhs.G, c);
+			SHMultiplyAdd(lhs.B, rhs.B, c);
+		}
+		
+		void SHMultiplyAdd(inout SHVector3RGB lhs, SHVector3RGB rhs, float c)
+		{
+			SHMultiplyAdd(lhs.R, rhs.R, c);
+			SHMultiplyAdd(lhs.G, rhs.G, c);
+			SHMultiplyAdd(lhs.B, rhs.B, c);
+		}			
+		
 		void SHAdd(inout SHVector5 lhs, SHVector5 rhs)
 		{
 			lhs.v0 += rhs.v0;
@@ -86,6 +114,20 @@ mixin SHCommon
 			lhs.v0 += rhs.v0;
 			lhs.v1 += rhs.v1;
 			lhs.v2 += rhs.v2;
+		}
+		
+		void SHAdd(inout SHVector5RGB lhs, SHVector5RGB rhs)
+		{
+			SHAdd(lhs.R, rhs.R);
+			SHAdd(lhs.G, rhs.G);
+			SHAdd(lhs.B, rhs.B);
+		}
+		
+		void SHAdd(inout SHVector3RGB lhs, SHVector3RGB rhs)
+		{
+			SHAdd(lhs.R, rhs.R);
+			SHAdd(lhs.G, rhs.G);
+			SHAdd(lhs.B, rhs.B);
 		}		
 		
 		void SHMultiply(inout SHVector5 lhs, SHVector5 rhs)
@@ -104,7 +146,21 @@ mixin SHCommon
 			lhs.v0 *= rhs.v0;
 			lhs.v1 *= rhs.v1;
 			lhs.v2 *= rhs.v2;
-		}		
+		}
+		
+		void SHMultiply(inout SHVector5RGB lhs, SHVector5RGB rhs)
+		{
+			SHMultiply(lhs.R, rhs.R);
+			SHMultiply(lhs.G, rhs.G);
+			SHMultiply(lhs.B, rhs.B);
+		}	
+		
+		void SHMultiply(inout SHVector3RGB lhs, SHVector3RGB rhs)
+		{
+			SHMultiply(lhs.R, rhs.R);
+			SHMultiply(lhs.G, rhs.G);
+			SHMultiply(lhs.B, rhs.B);
+		}	
 		
 		void SHMultiply(inout SHVector5 lhs, float rhs)
 		{
@@ -122,6 +178,20 @@ mixin SHCommon
 			lhs.v0 *= rhs;
 			lhs.v1 *= rhs;
 			lhs.v2 *= rhs;
+		}
+
+		void SHMultiply(inout SHVector5RGB lhs, float rhs)
+		{
+			SHMultiply(lhs.R, rhs);
+			SHMultiply(lhs.G, rhs);
+			SHMultiply(lhs.B, rhs);
+		}	
+		
+		void SHMultiply(inout SHVector3RGB lhs, float rhs)
+		{
+			SHMultiply(lhs.R, rhs);
+			SHMultiply(lhs.G, rhs);
+			SHMultiply(lhs.B, rhs);
 		}		
 		
 		SHVector5 SHBasis5(float3 dir)

+ 40 - 0
Data/Raw/Engine/Shaders/Clear.bsl

@@ -0,0 +1,40 @@
+technique Clear
+{
+	depth
+	{
+		read = false;
+		write = false;
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		cbuffer Params
+		{
+			uint gClearValue;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+			output.position = float4(input.screenPos, 0, 1);
+
+			return output;
+		}			
+
+		uint4 fsmain(VStoFS input) : SV_Target0
+		{
+			return uint4(gClearValue, gClearValue, gClearValue, gClearValue);
+		}
+	};
+};

+ 253 - 0
Data/Raw/Engine/Shaders/IrradianceEvaluate.bsl

@@ -0,0 +1,253 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\SHCommon.bslinc"
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+technique IrradianceEvaluate
+{
+	mixin PPBase;
+	mixin SHCommon;
+	mixin GBufferInput;
+	mixin PerCameraData;
+
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			color = { one, one, add };
+		};
+	};	
+	
+	code
+	{
+		#if MSAA_COUNT > 1
+			Texture2DMS<uint> gInputTex;
+		#else
+			Texture2D<uint> gInputTex;
+		#endif
+		
+		struct Tetrahedron
+		{
+			uint4 indices;
+			float3x4 transform;
+		};
+		
+		struct TetrahedronFace
+		{
+			float3 corners[3];
+			float3 normals[3];
+			uint isQuadratic;
+		};		
+		
+		StructuredBuffer<SHVector3RGB> gSHCoeffs;
+		StructuredBuffer<Tetrahedron> gTetrahedra;
+		StructuredBuffer<TetrahedronFace> gTetFaces;
+		
+		TextureCube gSkyIrradianceTex;
+		SamplerState gSkyIrradianceSamp;
+
+		cbuffer Params
+		{
+			float gSkyBrightness;
+			uint gNumTetrahedra;
+		}				
+		
+		float3 getSkyIndirectDiffuse(float3 dir)
+		{
+			return gSkyIrradianceTex.SampleLevel(gSkyIrradianceSamp, dir, 0).rgb * gSkyBrightness;
+		}
+		
+		float evaluateLambert(SHVector3 coeffs)
+		{
+			// Multiply irradiance SH coefficients by cosine lobe (Lambert diffuse) and evaluate resulting SH
+			// See: http://cseweb.ucsd.edu/~ravir/papers/invlamb/josa.pdf for derivation of the
+			// cosine lobe factors
+			float output = 0.0f;
+			
+			// Band 0 (factor 1.0)
+			output += coeffs.v0[0];
+			
+			// Band 1 (factor 2/3)
+			float f = (2.0f/3.0f);
+			float4 f4 = float4(f, f, f, f);
+			
+			output += dot(coeffs.v0.gba, f4.rgb);
+			
+			// Band 2 (factor 1/4)
+			f = (1.0f/4.0f);
+			f4 = float4(f, f, f, f);
+			
+			output += dot(coeffs.v1, f4);
+			output += coeffs.v2 * f;
+						
+			return output;
+		}	
+
+		float solveQuadratic(float A, float B, float C)
+		{
+			const float EPSILON = 0.00001f;
+		
+			if (abs(A) > EPSILON)
+			{
+				float p = B / (2 * A);
+				float q = C / A;
+				float D = p * p - q;
+
+				return sqrt(D) - p;
+			}
+			else
+			{
+				if(abs(B) > EPSILON)
+					return -C / B;
+			
+				return 0.0f;
+			}
+		}
+		
+		float solveCubic(float A, float B, float C)
+		{
+			const float EPSILON = 0.00001f;
+			const float THIRD = 1.0f / 3.0f;
+		
+			float sqA = A * A;
+			float p = THIRD * (-THIRD * sqA + B);
+			float q = (0.5f) * ((2.0f / 27.0f) * A * sqA - THIRD * A * B + C);
+
+			float cbp = p * p * p;
+			float D = q * q + cbp;
+			
+			float t;
+			if(D < 0.0f)
+			{
+				float phi = THIRD * acos(-q / sqrt(-cbp));
+				t = (2 * sqrt(-p)) * cos(phi);
+			}
+			else
+			{
+				float sqrtD = sqrt(D);
+				float u = pow(sqrtD + abs(q), 1.0f / 3.0f);
+
+				
+				if (q > 0.0f)
+					t = -u + p / u;
+				else
+					t = u - p / u;
+			}
+			
+			return t - THIRD * A;
+		}
+		
+		float3 calcTriBarycentric(float3 p, float3 a, float3 b, float3 c)
+		{
+			float3 v0 = b - a;
+			float3 v1 = c - a;
+			float3 v2 = p - a;
+			
+			float d00 = dot(v0, v0);
+			float d01 = dot(v0, v1);
+			float d11 = dot(v1, v1);
+			float d20 = dot(v2, v0);
+			float d21 = dot(v2, v1);
+			
+			float denom = d00 * d11 - d01 * d01;
+			float3 coords;
+			coords.x = (d11 * d20 - d01 * d21) / denom;
+			coords.y = (d00 * d21 - d01 * d20) / denom;
+			coords.z = 1.0f - coords.x - coords.y;
+			
+			return coords;
+		}
+		
+		float3 fsmain(VStoFS input
+			#if MSAA_COUNT > 1
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			uint2 pixelPos = trunc(input.uv0);
+		
+			SurfaceData surfaceData;
+			#if MSAA_COUNT > 1
+				surfaceData = getGBufferData(pixelPos, sampleIdx);
+			#else
+				surfaceData = getGBufferData(pixelPos);
+			#endif		
+			
+			float3 irradiance = 0;
+			#if SKY_ONLY
+				irradiance = gSkyIrradianceTex.SampleLevel(gSkyIrradianceSamp, surfaceData.worldNormal, 0).rgb * gSkyBrightness;
+			#else
+				uint volumeIdx;
+				#if MSAA_COUNT > 1
+					volumeIdx = gInputTex.Load(uint3(pixelPos, 0), sampleIdx).x;
+				#else
+					volumeIdx = gInputTex.Load(uint3(pixelPos, 0)).x;
+				#endif
+				
+				if(volumeIdx == 0xFFFF) // Using 16-bit texture, so need to compare like this
+					irradiance = gSkyIrradianceTex.SampleLevel(gSkyIrradianceSamp, surfaceData.worldNormal, 0).rgb * gSkyBrightness;
+				else
+				{
+					Tetrahedron volume = gTetrahedra[volumeIdx];
+					
+					float3 P = NDCToWorld(input.screenPos, surfaceData.depth);
+					
+					float4 coords;
+					[branch]
+					if(volumeIdx >= gNumTetrahedra)
+					{
+						uint faceIdx = volumeIdx - gNumTetrahedra;
+						TetrahedronFace face = gTetFaces[faceIdx];
+					
+						float3 factors = mul(volume.transform, float4(P, 1.0f));
+						float A = factors.x;
+						float B = factors.y;
+						float C = factors.z;
+
+						float t;
+						if(face.isQuadratic > 0)
+							t = solveQuadratic(A, B, C);
+						else
+							t = solveCubic(A, B, C);
+							
+						float3 triA = face.corners[0] + t * face.normals[0];
+						float3 triB = face.corners[1] + t * face.normals[1];
+						float3 triC = face.corners[2] + t * face.normals[2];
+						
+						float3 bary = calcTriBarycentric(P, triA, triB, triC);
+						
+						coords.x = bary.z;
+						coords.yz = bary.xy;
+						coords.w = 0.0f;
+					}
+					else
+					{
+						float3 factors = mul(volume.transform, float4(P, 1.0f));			
+						coords = float4(factors, 1.0f - factors.x - factors.y - factors.z);
+					}
+
+					SHVector3RGB shCoeffs;
+					SHZero(shCoeffs);
+					
+					for(uint i = 0; i < 4; ++i)
+					{
+						if(coords[i] > 0.0f)
+							SHMultiplyAdd(shCoeffs, gSHCoeffs[volume.indices[i]], coords[i]);
+					}
+					
+					SHVector3 shBasis = SHBasis3(surfaceData.worldNormal);
+					SHMultiply(shCoeffs.R, shBasis);
+					SHMultiply(shCoeffs.G, shBasis);
+					SHMultiply(shCoeffs.B, shBasis);
+					
+					irradiance.r = evaluateLambert(shCoeffs.R);
+					irradiance.g = evaluateLambert(shCoeffs.G);
+					irradiance.b = evaluateLambert(shCoeffs.B);
+				}
+			#endif // SKY_ONLY
+			
+			return irradiance * surfaceData.albedo.rgb;
+		}	
+	};
+};

+ 1 - 1
Data/Raw/Engine/Shaders/Skybox.bsl

@@ -42,7 +42,7 @@ technique Skybox
 			in float4 inPos : SV_Position, 
 			in float3 dir : TEXCOORD0) : SV_Target
 		{
-			#ifdef SOLID_COLOR
+			#if SOLID_COLOR
 				return gClearColor;
 			#else
 				return gSkyTex.SampleLevel(gSkySamp, dir, 0);

+ 75 - 0
Data/Raw/Engine/Shaders/TetrahedraRender.bsl

@@ -0,0 +1,75 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+
+technique TetrahedraRender
+{
+	mixin PerCameraData;
+
+	raster
+	{
+		cull = cw;
+	};
+	
+	depth
+	{
+		compare = lte;
+	};
+
+	code
+	{
+		struct VertexInput
+		{
+			float3 position : POSITION;
+			uint index : TEXCOORD0;			
+		};		
+	
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+			float4 clipPos : TEXCOORD0;
+			uint index : TEXCOORD1;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = mul(gMatViewProj, float4(input.position, 1.0f));
+			output.clipPos = output.position;
+			output.index = input.index;
+			
+			return output;
+		}
+		
+		#ifndef MSAA
+			#define MSAA 0
+		#endif
+
+		#if MSAA
+		Texture2DMS<float> gDepthBufferTex;
+		#else
+		Texture2D gDepthBufferTex;
+		SamplerState gDepthBufferSamp;
+		#endif		
+		
+		uint fsmain(VStoFS input
+		#if MSAA
+			,uint sampleIdx : SV_SampleIndex
+		#endif
+		) : SV_Target0
+		{
+			float sceneDepth;
+			#if MSAA
+				sceneDepth = gDepthBufferTex.Load(trunc(input.position.xy), sampleIdx);
+			#else
+				float2 ndcPos = input.clipPos.xy / input.clipPos.w;
+				sceneDepth = gDepthBufferTex.Sample(gDepthBufferSamp, NDCToUV(ndcPos));
+			#endif
+			
+			float currentDepth = input.position.z;
+			if(currentDepth < sceneDepth)
+				discard;
+				
+			return input.index;
+		}
+	};
+};

+ 0 - 2
Data/Raw/Engine/Shaders/TiledDeferredImageBasedLighting.bsl

@@ -164,11 +164,9 @@ technique TiledDeferredImageBasedLighting
 			existingColor = gInColor.Load(int3(pixelPos.xy, 0));
 			#endif				
 			
-			float3 indirectDiffuse = getSkyIndirectDiffuse(N) * surfaceData.albedo.rgb;
 			float3 imageBasedSpecular = getImageBasedSpecular(worldPosition, V, specR, surfaceData, probeOffset, numProbes);
 
 			float4 totalLighting = existingColor;
-			totalLighting.rgb += indirectDiffuse;
 			totalLighting.rgb += imageBasedSpecular;
 			
 			return totalLighting;				

+ 0 - 2
Data/Raw/Engine/Shaders/Transparent.bsl

@@ -82,12 +82,10 @@ mixin Surface
 			float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
 			
 			float4 directLighting = getDirectLighting(input.worldPosition, V, specR, surfaceData, lightOffsets);
-			float3 indirectDiffuse = getSkyIndirectDiffuse(surfaceData.worldNormal) * surfaceData.albedo;
 			float3 imageBasedSpecular = getImageBasedSpecular(input.worldPosition, V, specR, surfaceData, 
 				reflProbeOffsetAndSize.x, reflProbeOffsetAndSize.y);
 
 			float3 totalLighting = directLighting.rgb;
-			totalLighting.rgb += indirectDiffuse;
 			totalLighting.rgb += imageBasedSpecular;
 
 			return float4(totalLighting, gOpacity);

+ 44 - 6
Documentation/Manuals/Native/customRenderer.md

@@ -182,7 +182,14 @@ class DownsampleMat : public RendererMaterial<DownsampleMat>
 };
 ~~~~~~~~~~~~~
 
-After that you can simply instantiate your renderer material, and access the underlying material by calling @ref bs::ct::RendererMaterial::getMaterial() "ct::RendererMaterial::getMaterial()", and the material parameters by calling @ref bs::ct::RendererMaterial::getParamsSet() "ct::RendererMaterial::getParamsSet()". Then you can bind the material for rendering as normal (using **RendererUtility** or directly through **RenderAPI**).
+Once defined the renderer material can be accessed through the static @ref bs::ct::RendererMaterial::get<T>() "ct::RendererMaterial::get<T>()" method. Underlying material can be accessed by calling @ref bs::ct::RendererMaterial::getMaterial() "ct::RendererMaterial::getMaterial()", and the material parameters by calling @ref bs::ct::RendererMaterial::getParamsSet() "ct::RendererMaterial::getParamsSet()". Then you can bind the material for rendering as normal (using **RendererUtility** or directly through **RenderAPI**).
+
+~~~~~~~~~~~~~{.cpp}
+DownsampleMat* renderMat = DownsampleMat::get():
+SPtr<Material> material = renderMat->getMaterial();
+
+// Render using the material as normal
+~~~~~~~~~~~~~
 
 You will normally also want to add a constructor in which you look up any necessary parameters the material might require, so they can be set more easily when rendering.
 ~~~~~~~~~~~~~{.cpp}
@@ -213,7 +220,7 @@ class DownsampleMat : public RendererMaterial<DownsampleMat>
 	// Set up parameters and render a full screen quad using the material
 	void execute(const SPtr<Texture>& input)
 	{
-		// Actually assign parameters before rendering
+		// Assign parameters before rendering
 		mInputTexture.set(input);
 		
 		const TextureProperties& props = input->getProperties();
@@ -233,17 +240,48 @@ class DownsampleMat : public RendererMaterial<DownsampleMat>
 };
 ~~~~~~~~~~~~~
 
-Renderer materials also support variations for cases where you might require slightly different versions of a shader for different use cases. The variations are handled by setting up preprocessor \#defines, which the shader code can then use to conditionally add or remove parts of code (via \#ifdef or similar). To determine which defines are set implement the _initDefines() method in your **RendererMaterial** implementation, and append your defines to the @ref bs::ShaderDefines "ShaderDefines" object. Note that this method must be present, even if not using any defines.
+Renderer materials also support variations for cases where you might require slightly different versions of a shader for different use cases. The variations are handled by setting up preprocessor \#defines, which the shader code can then use to conditionally add or remove parts of code (via \#if or similar). To determine which defines are set implement the _initVariations() method in your **RendererMaterial** implementation, and append your defines to the @ref bs::ct::ShaderVariations "ShaderVariations" object. Note that this method must be present, even if not using any variations (simply leave it empty).
 
 ~~~~~~~~~~~~~{.cpp}
+// Make our downsample shader come in two variations
+class DownsampleMat : public RendererMaterial<DownsampleMat>
+{
+	RMAT_DEF("Downsample.bsl");
+	
+	// ... other DownsampleMat code ...
+	
+private:
+	static ShaderVariation VAR_PointFiltering;
+	static ShaderVariation VAR_BilinearFiltering;
+};
+
+// Set up optional defines to control shader compilation
+ShaderVariation DownsampleMat::VAR_PointFiltering = ShaderVariation({
+	ShaderVariation::Param("BILINEAR_FILTERING", false)
+});
+
+ShaderVariation DownsampleMat::VAR_BilinearFiltering = ShaderVariation({
+	ShaderVariation::Param("BILINEAR_FILTERING", true)
+});
+
 // Method defined in RMAT_DEF macro
-void DownsampleMat::_initDefines(ShaderDefines& defines)
+void DownsampleMat::_initVariations(ShaderVariations& variations)
 {
-	// Set up optional defines to control shader compilation
-	defines.set("BILINEAR_FILTERING", 1);
+	variations.add(VAR_PointFiltering);
+	variations.add(VAR_BilinearFiltering);
 }
 ~~~~~~~~~~~~~
 
+Specific variation of the material can the be retrieved by calling the static @ref bs::ct::RendererMaterial::get<T>(const ShaderVariation&) "ct::RendererMaterial::get<T>(const ShaderVariation&)" method.
+
+~~~~~~~~~~~~~{.cpp}
+// Get the variation that performs bilinear filtering
+DownsampleMat* renderMat = DownsampleMat::get(VAR_BilinearFiltering):
+SPtr<Material> material = renderMat->getMaterial();
+
+// Render using the material as normal
+~~~~~~~~~~~~~
+
 > All builtin shaders are cached. The system will automatically pick up any changes to shaders in *Data/Raw/Engine* folder and rebuild the cache when needed. However if you are changing defines as above you must manually force the system to rebuild by deleting the *Timestamp.asset* file in *Data/Engine* folder.
 
 ## Parameter blocks {#renderer_c_d}

+ 8 - 3
Source/BansheeCore/CMakeSources.cmake

@@ -23,6 +23,7 @@ set(BS_BANSHEECORE_INC_COMPONENTS
 	"Include/BsCBone.h"	
 	"Include/BsCReflectionProbe.h"
 	"Include/BsCSkybox.h"
+	"Include/BsCLightProbeVolume.h"
 )
 
 set(BS_BANSHEECORE_INC_PHYSICS
@@ -97,11 +98,12 @@ set(BS_BANSHEECORE_INC_RENDERER
 	"Include/BsRendererMeshData.h"
 	"Include/BsParamBlocks.h"
 	"Include/BsCamera.h"
-	"Include/BsPostProcessSettings.h"
+	"Include/BsRenderSettings.h"
 	"Include/BsRendererExtension.h"
 	"Include/BsReflectionProbe.h"
 	"Include/BsSkybox.h"
 	"Include/BsLightProbeVolume.h"
+	"Include/BsIBLUtility.h"
 )
 
 set(BS_BANSHEECORE_SRC_LOCALIZATION
@@ -263,6 +265,7 @@ set(BS_BANSHEECORE_SRC_COMPONENTS
 	"Source/BsCBone.cpp"	
 	"Source/BsCReflectionProbe.cpp"
 	"Source/BsCSkybox.cpp"
+	"Source/BsCLightProbeVolume.cpp"
 )
 
 set(BS_BANSHEECORE_SRC_IMPORTER
@@ -350,7 +353,7 @@ set(BS_BANSHEECORE_INC_RTTI
 	"Include/BsSkeletonRTTI.h"
 	"Include/BsCCameraRTTI.h"
 	"Include/BsCameraRTTI.h"
-	"Include/BsPostProcessSettingsRTTI.h"
+	"Include/BsRenderSettingsRTTI.h"
 	"Include/BsMorphShapesRTTI.h"
 	"Include/BsAudioClipImportOptionsRTTI.h"
 	"Include/BsCRenderableRTTI.h"
@@ -363,6 +366,7 @@ set(BS_BANSHEECORE_INC_RTTI
 	"Include/BsCReflectionProbeRTTI.h"
 	"Include/BsSkyboxRTTI.h"
 	"Include/BsLightProbeVolumeRTTI.h"
+	"Include/BsCLightProbeVolumeRTTI.h"
 )
 
 set(BS_BANSHEECORE_SRC_RENDERER
@@ -373,11 +377,12 @@ set(BS_BANSHEECORE_SRC_RENDERER
 	"Source/BsRendererMeshData.cpp"
 	"Source/BsParamBlocks.cpp"
 	"Source/BsCamera.cpp"
-	"Source/BsPostProcessSettings.cpp"
+	"Source/BsRenderSettings.cpp"
 	"Source/BsRendererExtension.cpp"
 	"Source/BsReflectionProbe.cpp"
 	"Source/BsSkybox.cpp"
 	"Source/BsLightProbeVolume.cpp"
+	"Source/BsIBLUtility.cpp"
 )
 
 set(BS_BANSHEECORE_SRC_RESOURCES

+ 57 - 21
Source/BansheeCore/Include/BsAnimationClip.h

@@ -17,8 +17,11 @@ namespace bs
 	struct AnimationCurveMapping;
 
 	/** A set of animation curves representing translation/rotation/scale and generic animation. */
-	struct AnimationCurves
+	struct BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Animation) AnimationCurves
 	{
+		BS_SCRIPT_EXPORT()
+		AnimationCurves() {}
+
 		/** 
 		 * Registers a new curve used for animating position. 
 		 *
@@ -26,6 +29,7 @@ namespace bs
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
 		 */
+		BS_SCRIPT_EXPORT(n:AddPositionCurve)
 		void addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve);
 
 		/** 
@@ -35,6 +39,7 @@ namespace bs
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
 		 */
+		BS_SCRIPT_EXPORT(n:AddRotationCurve)
 		void addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve);
 
 		/** 
@@ -44,6 +49,7 @@ namespace bs
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
 		 */
+		BS_SCRIPT_EXPORT(n:AddScaleCurve)
 		void addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve);
 
 		/** 
@@ -53,50 +59,74 @@ namespace bs
 		 *							from animation.
 		 * @param[in]	curve		Curve to add to the clip.
 		 */
+		BS_SCRIPT_EXPORT(n:AddGenericCurve)
 		void addGenericCurve(const String& name, const TAnimationCurve<float>& curve);
 
 		/** Removes an existing curve from the clip. */
+		BS_SCRIPT_EXPORT(n:RemovePositionCurve)
 		void removePositionCurve(const String& name);
 
 		/** Removes an existing curve from the clip. */
+		BS_SCRIPT_EXPORT(n:RemoveRotationCurve)
 		void removeRotationCurve(const String& name);
 
 		/** Removes an existing curve from the clip. */
+		BS_SCRIPT_EXPORT(n:RemoveScaleCurve)
 		void removeScaleCurve(const String& name);
 
 		/** Removes an existing curve from the clip. */
+		BS_SCRIPT_EXPORT(n:RemoveGenericCurve)
 		void removeGenericCurve(const String& name);
 
+		/** Curves for animating scene object's position. */
 		Vector<TNamedAnimationCurve<Vector3>> position;
+
+		/** Curves for animating scene object's rotation. */
 		Vector<TNamedAnimationCurve<Quaternion>> rotation;
+
+		/** Curves for animating scene object's scale. */
 		Vector<TNamedAnimationCurve<Vector3>> scale;
+
+		/** Curves for animating generic component properties. */
 		Vector<TNamedAnimationCurve<float>> generic;
 	};
 
 	/** Contains a set of animation curves used for moving and rotating the root bone. */
-	struct RootMotion
+	struct BS_SCRIPT_EXPORT(m:Animation) RootMotion
 	{
 		RootMotion() { }
 		RootMotion(const TAnimationCurve<Vector3>& position, const TAnimationCurve<Quaternion>& rotation)
 			:position(position), rotation(rotation)
 		{ }
 
+		/** Animation curve representing the movement of the root bone. */
 		TAnimationCurve<Vector3> position;
+
+		/** Animation curve representing the rotation of the root bone. */
 		TAnimationCurve<Quaternion> rotation;
 	};
 
 	/** Event that is triggered when animation reaches a certain point. */
-	struct AnimationEvent
+	struct BS_SCRIPT_EXPORT(m:Animation,pl:true) AnimationEvent
 	{
 		AnimationEvent()
 			:time(0.0f)
 		{ }
 
+		/** 
+		 * Constructs a new animation event.
+		 *
+		 * @param[in]	name	Name used to identify the event when triggered.
+		 * @param[in]	time	Time at which to trigger the event, in seconds.
+		 */
 		AnimationEvent(const String& name, float time)
 			:name(name), time(time)
 		{ }
 
+		/** Name used to identify the event when triggered. */
 		String name;
+
+		/** Time at which to trigger the event, in seconds. */
 		float time;
 	};
 
@@ -116,24 +146,28 @@ namespace bs
 	 * Contains animation curves for translation/rotation/scale of scene objects/skeleton bones, as well as curves for
 	 * generic property animation. 
 	 */
-	class BS_CORE_EXPORT AnimationClip : public Resource
+	class BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Animation) AnimationClip : public Resource
 	{
 	public:
 		virtual ~AnimationClip() { }
 
-		/** 
-		 * Returns all curves stored in the animation. Returned value will not be updated if the animation clip curves are
-		 * added or removed. Caller must monitor for changes and retrieve a new set of curves when that happens.
-		 */
+		/** @copydoc setCurves() */
+		BS_SCRIPT_EXPORT(n:Curves,pr:getter)
 		SPtr<AnimationCurves> getCurves() const { return mCurves; }
 
-		/** Assigns a new set of curves to be used by the animation. The clip will store a copy of this object.*/
+		/** 
+		 * A set of all curves stored in the animation. Returned value will not be updated if the animation clip curves are
+		 * added or removed, as it is a copy of clip's internal values.
+		 */
+		BS_SCRIPT_EXPORT(n:Curves,pr:setter)
 		void setCurves(const AnimationCurves& curves);
 
-		/** Returns all events that will be triggered by the animation. */
+		/** @copydoc setEvents() */
+		BS_SCRIPT_EXPORT(n:Events,pr:getter)
 		const Vector<AnimationEvent>& getEvents() const { return mEvents; }
 
-		/** Sets events that will be triggered as the animation is playing. */
+		/** A set of all events to be triggered as the animation is playing. */
+		BS_SCRIPT_EXPORT(n:Events,pr:setter)
 		void setEvents(const Vector<AnimationEvent>& events) { mEvents = events; }
 
 		/** 
@@ -141,9 +175,11 @@ namespace bs
 		 * animation curves manually, instead of through the normal animation process. This property is only available
 		 * if animation clip was imported with root motion import enabled. 
 		 */
+		BS_SCRIPT_EXPORT(n:RootMotion,pr:getter)
 		SPtr<RootMotion> getRootMotion() const { return mRootMotion; }
 
 		/** Checks if animation clip has root motion curves separate from the normal animation curves. */
+		BS_SCRIPT_EXPORT(n:HasRootMotion,pr:getter)
 		bool hasRootMotion() const;
 
 		/**
@@ -181,25 +217,23 @@ namespace bs
 		 * Checks are the curves contained within the clip additive. Additive clips are intended to be added on top of
 		 * other clips.
 		 */
+		BS_SCRIPT_EXPORT(n:IsAddtive,pr:getter)
 		bool isAdditive() const { return mIsAdditive; }
 
 		/** Returns the length of the animation clip, in seconds. */
+		BS_SCRIPT_EXPORT(n:Length,pr:getter)
 		float getLength() const { return mLength; }
 
-		/** 
-		 * Returns the number of samples per second the animation clip curves were sampled at.
-		 *
-		 * @note	This value is not used by the animation clip or curves directly since unevenly spaced keyframes are
-		 *			supported. But it can be of value when determining the original sample rate of an imported animation
-		 *			or similar.
-		 */
+		/** @copydoc setSampleRate() */
+		BS_SCRIPT_EXPORT(n:SampleRate,pr:getter)
 		UINT32 getSampleRate() const { return mSampleRate; }
 
 		/** 
-		 * Sets the number of samples per second the animation clip curves were sampled at.
-		 *
-		 * @see	getSampleRate()
+		 * Number of samples per second the animation clip curves were sampled at. This value is not used by the animation
+		 * clip or curves directly since unevenly spaced keyframes are supported. But it can be of value when determining
+		 * the original sample rate of an imported animation or similar.
 		 */
+		BS_SCRIPT_EXPORT(n:SampleRate,pr:setter)
 		void setSampleRate(UINT32 sampleRate) { mSampleRate = sampleRate; }
 
 		/** 
@@ -212,6 +246,7 @@ namespace bs
 		 * Creates an animation clip with no curves. After creation make sure to register some animation curves before
 		 * using it. 
 		 */
+		BS_SCRIPT_EXPORT(ec:AnimationClip)
 		static HAnimationClip create(bool isAdditive = false);
 
 		/** 
@@ -225,6 +260,7 @@ namespace bs
 		 * @param[in]	rootMotion	Optional set of curves that can be used for animating the root bone. Not used by the
 		 *							animation system directly but is instead provided to the user for manual evaluation.
 		 */
+		BS_SCRIPT_EXPORT(ec:AnimationClip)
 		static HAnimationClip create(const SPtr<AnimationCurves>& curves, bool isAdditive = false, UINT32 sampleRate = 1, 
 			const SPtr<RootMotion>& rootMotion = nullptr);
 

+ 65 - 3
Source/BansheeCore/Include/BsAnimationCurve.h

@@ -4,6 +4,8 @@
 
 #include "BsCorePrerequisites.h"
 #include "BsCurveCache.h"
+#include "BsVector3.h"
+#include "BsQuaternion.h"
 
 namespace bs
 {
@@ -21,6 +23,10 @@ namespace bs
 		float time; /**< Position of the key along the animation spline. */
 	};
 
+	template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrame,pl:true) TKeyframe<float>;
+	template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameVec3,pl:true) TKeyframe<Vector3>;
+	template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameQuat,pl:true) TKeyframe<Quaternion>;
+
 	/**
 	 * Animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve. The
 	 * spline can be evaluated at any time, and uses caching to speed up multiple sequential evaluations.
@@ -32,6 +38,13 @@ namespace bs
 		typedef TKeyframe<T> KeyFrame;
 
 		TAnimationCurve();
+
+		/**
+		 * Creates a new animation curve.
+		 * 
+		 * @param[in]	keyframes	Keyframes to initialize the curve with
+		 */
+		BS_SCRIPT_EXPORT()
 		TAnimationCurve(const Vector<KeyFrame>& keyframes);
 
 		/**
@@ -58,6 +71,7 @@ namespace bs
 		 *						value will be clamped.
 		 * @return				Interpolated value from the curve at provided time.
 		 */
+		BS_SCRIPT_EXPORT(n:Evaluate)
 		T evaluate(float time, bool loop = true) const;
 
 		/**
@@ -96,6 +110,10 @@ namespace bs
 		/** Returns a keyframe at the specified index. */
 		const TKeyframe<T>& getKeyFrame(UINT32 idx) const { return mKeyframes[idx]; }
 
+		/** Returns a list of all keyframes in the curve. */
+		BS_SCRIPT_EXPORT(n:KeyFrames,pr:getter)
+		const Vector<TKeyframe<T>>& getKeyFrames() const { return mKeyframes; }
+
 	private:
 		friend struct RTTIPlainType<TAnimationCurve<T>>;
 
@@ -154,10 +172,20 @@ namespace bs
 		float mLength;
 	};
 
-	/** Flags that described an TAnimationCurve<T>. */
-	enum class AnimationCurveFlag
+#ifdef BS_SBGEN
+	template class BS_SCRIPT_EXPORT(m:Animation,n:AnimationCurve) TAnimationCurve<float>;
+	template class BS_SCRIPT_EXPORT(m:Animation,n:Vector3Curve) TAnimationCurve<Vector3>;
+	template class BS_SCRIPT_EXPORT(m:Animation,n:QuaternionCurve) TAnimationCurve<Quaternion>;
+#endif
+
+	/** Flags that describe an animation curve. */
+	enum BS_SCRIPT_EXPORT(n:AnimationCurveFlags) class AnimationCurveFlag
 	{
-		/** Signifies that the curve was imported from an external file, and not created manually in-engine. */
+		/**
+		 * If enabled, the curve was imported from an external file and not created within the engine. This will affect
+		 * how are animation results applied to scene objects (with imported animations it is assumed the curve is
+		 * animating bones and with in-engine curves it is assumed the curve is animating scene objects).
+		 */
 		ImportedCurve = 1 << 0,
 		/** Signifies the curve is used to animate between different frames within a morph channel. In range [0, 1]. */
 		MorphFrame = 1 << 1,
@@ -172,10 +200,44 @@ namespace bs
 	template <class T>
 	struct TNamedAnimationCurve
 	{
+		TNamedAnimationCurve() { }
+
+		/**
+		 * Constructs a new named animation curve.
+		 * 
+		 * @param[in]	name	Name of the curve.
+		 * @param[in]	curve	Curve containing the animation data.
+		 */
+		TNamedAnimationCurve(const String& name, const TAnimationCurve<T> curve)
+			:name(name), curve(curve)
+		{ }
+
+		/**
+		 * Constructs a new named animation curve.
+		 * 
+		 * @param[in]	name	Name of the curve.
+		 * @param[in]	flags	Flags that describe the animation curve.
+		 * @param[in]	curve	Curve containing the animation data.
+		 */
+		TNamedAnimationCurve(const String& name, AnimationCurveFlags flags, const TAnimationCurve<T> curve)
+			:name(name), curve(curve)
+		{ }
+
+		/** Name of the curve. */
 		String name;
+
+		/** Flags that describe the animation curve. */
 		AnimationCurveFlags flags;
+
+		/** Actual curve containing animation data. */
 		TAnimationCurve<T> curve;
 	};
 
+#ifdef BS_SBGEN
+	template class BS_SCRIPT_EXPORT(m:Animation,n:NamedFloatCurve,pl:true) TNamedAnimationCurve<float>;
+	template class BS_SCRIPT_EXPORT(m:Animation,n:NamedVector3Curve,pl:true) TNamedAnimationCurve<Vector3>;
+	template class BS_SCRIPT_EXPORT(m:Animation,n:NamedQuaternionCurve,pl:true) TNamedAnimationCurve<Quaternion>;
+#endif
+
 	/** @} */
 }

+ 13 - 3
Source/BansheeCore/Include/BsAnimationUtility.h

@@ -12,7 +12,7 @@ namespace bs
 	 */
 
 	/** Helper class for dealing with animations, animation clips and curves. */
-	class BS_CORE_EXPORT AnimationUtility
+	class BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Animation) AnimationUtility
 	{
 	public:
 		/**
@@ -26,10 +26,20 @@ namespace bs
 		static void wrapTime(float& time, float start, float end, bool loop);
 
 		/** Converts a curve in euler angles (in degrees) into a curve using quaternions. */
-		static TAnimationCurve<Quaternion> eulerToQuaternionCurve(const TAnimationCurve<Vector3>& eulerCurve);
+		BS_SCRIPT_EXPORT(n:EulerToQuaternionCurve)
+		static SPtr<TAnimationCurve<Quaternion>> eulerToQuaternionCurve(const SPtr<TAnimationCurve<Vector3>>& eulerCurve);
 
 		/** Converts a curve in quaternions into a curve using euler angles (in degrees). */
-		static TAnimationCurve<Vector3> quaternionToEulerCurve(const TAnimationCurve<Quaternion>& quatCurve);
+		BS_SCRIPT_EXPORT(n:QuaternionToEulerCurve)
+		static SPtr<TAnimationCurve<Vector3>> quaternionToEulerCurve(const SPtr<TAnimationCurve<Quaternion>>& quatCurve);
+
+		/** Splits a Vector3 curve into three individual curves, one for each component. */
+		BS_SCRIPT_EXPORT(n:SplitCurve)
+		static Vector<SPtr<TAnimationCurve<float>>> splitCurve(const SPtr<TAnimationCurve<Vector3>>& compoundCurve);
+
+		/** Combines three single component curves into a Vector3 curve. */
+		BS_SCRIPT_EXPORT(n:CombineCurve)
+		static SPtr<TAnimationCurve<Vector3>> combineCurve(const Vector<SPtr<TAnimationCurve<float>>>& curveComponents);
 
 		/** Scales all curve values and tangents by the specified scale factor. */
 		template<class T> 

+ 4 - 10
Source/BansheeCore/Include/BsCCamera.h

@@ -142,17 +142,11 @@ namespace bs
 		/** @copydoc Camera::setMSAACount */
 		void setMSAACount(UINT32 count) { mInternal->setMSAACount(count); }
 
-		/** Returns settings that are used for controling post-process operations like tonemapping. */
-		const SPtr<PostProcessSettings>& getPostProcessSettings() const { return mInternal->getPostProcessSettings(); }
+		/** @copydoc Camera::getRenderSettings() */
+		const SPtr<RenderSettings>& getRenderSettings() const { return mInternal->getRenderSettings(); }
 
-		/** Sets settings that are used for controling post-process operations like tonemapping. */
-		void setPostProcessSettings(const SPtr<PostProcessSettings>& settings) { mInternal->setPostProcessSettings(settings); }
-
-		/** @copydoc Camera::getFlags */
-		CameraFlags getFlags() const { return mInternal->getFlags(); }
-
-		/** @copydoc Camera::setFlag */
-		void setFlag(const CameraFlag& flag, bool enable) { mInternal->setFlag(flag, enable); }
+		/** @copydoc Camera::setRenderSettings() */
+		void setRenderSettings(const SPtr<RenderSettings>& settings) { mInternal->setRenderSettings(settings); }
 
 		/** @copydoc Camera::worldToScreenPoint */
 		Vector2I worldToScreenPoint(const Vector3& worldPoint) const { updateView(); return mInternal->worldToScreenPoint(worldPoint); }

+ 19 - 19
Source/BansheeCore/Include/BsCLight.h

@@ -17,25 +17,25 @@ namespace bs
 	 *
 	 * Wraps Light as a Component.
 	 */
-    class BS_CORE_EXPORT CLight : public Component
-    {
-    public:
+	class BS_CORE_EXPORT CLight : public Component
+	{
+	public:
 		CLight(const HSceneObject& parent, LightType type = LightType::Radial, Color color = Color::White, 
 			float intensity = 100.0f, float range = 1.0f, bool castsShadows = false, Degree spotAngle = Degree(45), 
 			Degree spotFalloffAngle = Degree(40));
 
 		virtual ~CLight();
 
-	    /** @copydoc Light::getType */
+		/** @copydoc Light::getType */
 		LightType getType() const { return mInternal->getType(); }
 
-	    /** @copydoc Light::setType */
+		/** @copydoc Light::setType */
 		void setType(LightType type) { mInternal->setType(type); }
 
-	    /** @copydoc Light::getCastsShadow */
+		/** @copydoc Light::getCastsShadow */
 		bool getCastsShadow() const { return mInternal->getCastsShadow(); }
 
-	    /** @copydoc Light::setCastsShadow */
+		/** @copydoc Light::setCastsShadow */
 		void setCastsShadow(bool castsShadow) { mInternal->setCastsShadow(castsShadow); }
 
 		/** @copydoc Light::setShadowBias */
@@ -44,16 +44,16 @@ namespace bs
 		/** @copydoc Light::setShadowBias() */
 		float getShadowBias() const { return mInternal->getShadowBias(); }
 
-	    /** @copydoc Light::getColor */
+		/** @copydoc Light::getColor */
 		Color getColor() const { return mInternal->getColor(); }
 
-	    /** @copydoc Light::setColor */
+		/** @copydoc Light::setColor */
 		void setColor(const Color& color) { mInternal->setColor(color); }
 
-	    /** @copydoc Light::getAttenuationRadius */
+		/** @copydoc Light::getAttenuationRadius */
 		float getAttenuationRadius() const { return mInternal->getAttenuationRadius(); }
 
-	    /** @copydoc Light::setAttenuationRadius */
+		/** @copydoc Light::setAttenuationRadius */
 		void setAttenuationRadius(float radius) { mInternal->setAttenuationRadius(radius); }
 
 		/** @copydoc Light::getSourceRadius */
@@ -62,16 +62,16 @@ namespace bs
 		/** @copydoc Light::setSourceRadius */
 		void setSourceRadius(float radius) { mInternal->setSourceRadius(radius); }
 
-	    /** @copydoc Light::getIntensity */
+		/** @copydoc Light::getIntensity */
 		float getIntensity() const { return mInternal->getIntensity(); }
 
-	    /** @copydoc Light::setIntensity */
+		/** @copydoc Light::setIntensity */
 		void setIntensity(float intensity) { mInternal->setIntensity(intensity); }
 
-	    /** @copydoc Light::getSpotAngle */
+		/** @copydoc Light::getSpotAngle */
 		Degree getSpotAngle() const { return mInternal->getSpotAngle(); }
 
-	    /** @copydoc Light::setSpotAngle */
+		/** @copydoc Light::setSpotAngle */
 		void setSpotAngle(const Degree& spotAngle) { mInternal->setSpotAngle(spotAngle); }
 
 		/** @copydoc Light::getSpotFalloffAngle */
@@ -86,19 +86,19 @@ namespace bs
 		/**  @copydoc Light::setUseAutoAttenuation  */
 		void setUseAutoAttenuation(bool enabled) { mInternal->setUseAutoAttenuation(enabled); }
 
-	    /** @copydoc Light::getBounds  */
+		/** @copydoc Light::getBounds  */
 		Sphere getBounds() const;
 
 		/** @name Internal 
 		 *  @{
 		 */
 
-	    /**	Returns the light that this component wraps. */
+		 /** Returns the light that this component wraps. */
 		SPtr<Light> _getLight() const { return mInternal; }
 
 		/** @} */
 
-    protected:
+	protected:
 		mutable SPtr<Light> mInternal;
 
 		// Only valid during construction
@@ -135,7 +135,7 @@ namespace bs
 
 	protected:
 		CLight(); // Serialization only
-     };
+	 };
 
 	 /** @} */
 }

+ 98 - 0
Source/BansheeCore/Include/BsCLightProbeVolume.h

@@ -0,0 +1,98 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsLightProbeVolume.h"
+#include "BsComponent.h"
+
+namespace bs 
+{
+	/** @addtogroup Components
+	 *  @{
+	 */
+
+	/**
+	 * @copydoc	LightProbeVolume
+	 *
+	 * Wraps LightProbeVolume as a Component.
+	 */
+	class BS_CORE_EXPORT CLightProbeVolume : public Component
+	{
+	public:
+		CLightProbeVolume(const HSceneObject& parent, const AABox& volume = AABox::UNIT_BOX, 
+			const Vector3I& cellCount = {1, 1, 1});
+		virtual ~CLightProbeVolume();
+
+		/** @copydoc LightProbeVolume::addProbe() */
+		UINT32 addProbe(const Vector3& position) { return mInternal->addProbe(position); }
+
+		/** @copydoc LightProbeVolume::setProbePosition() */
+		void setProbePosition(UINT32 handle, const Vector3& position) { mInternal->setProbePosition(handle, position); }
+
+		/** @copydoc LightProbeVolume::getProbePosition() */
+		Vector3 getProbePosition(UINT32 handle) const { return mInternal->getProbePosition(handle); }
+
+		/** @copydoc LightProbeVolume::removeProbe() */
+		void removeProbe(UINT32 handle) { mInternal->removeProbe(handle); }
+
+		/** @copydoc LightProbeVolume::renderProbe() */
+		void renderProbe(UINT32 handle);
+
+		/** @copydoc LightProbeVolume::renderProbes() */
+		void renderProbes();
+
+		/** @copydoc LightProbeVolume::resize() */
+		void resize(const AABox& volume, const Vector3I& cellCount = { 1, 1, 1 }) { mInternal->resize(volume, cellCount); }
+
+		/** @copydoc LightProbeVolume::clip() */
+		void clip() { mInternal->clip(); }
+
+		/** @copydoc LightProbeVolume::reset() */
+		void reset() { mInternal->reset(); }
+
+		/** @name Internal 
+		 *  @{
+		 */
+
+		/**	Returns the light probe volume that this component wraps. */
+		SPtr<LightProbeVolume> _getInternal() const { return mInternal; }
+
+		/** @} */
+
+	protected:
+		mutable SPtr<LightProbeVolume> mInternal;
+
+		// Only valid during construction
+		AABox mVolume;
+		Vector3I mCellCount;
+
+		/************************************************************************/
+		/* 						COMPONENT OVERRIDES                      		*/
+		/************************************************************************/
+	protected:
+		friend class SceneObject;
+
+		/** @copydoc Component::onInitialized */
+		void onInitialized() override;
+
+		/** @copydoc Component::onDestroyed */
+		void onDestroyed() override;
+
+		/** @copydoc Component::update */
+		void update() override { }
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class CLightProbeVolumeRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		RTTITypeBase* getRTTI() const override;
+
+	protected:
+		CLightProbeVolume(); // Serialization only
+	};
+
+	 /** @} */
+}

+ 12 - 7
Source/BansheeCore/Include/BsPostProcessSettingsRTTI.h → Source/BansheeCore/Include/BsCLightProbeVolumeRTTI.h

@@ -4,7 +4,8 @@
 
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
-#include "BsPostProcessSettings.h"
+#include "BsCLightProbeVolume.h"
+#include "BsGameObjectRTTI.h"
 
 namespace bs
 {
@@ -13,28 +14,32 @@ namespace bs
 	 *  @{
 	 */
 
-	class BS_CORE_EXPORT PostProcessSettingsRTTI : public RTTIType<PostProcessSettings, IReflectable, PostProcessSettingsRTTI>
+	class BS_CORE_EXPORT CLightProbeVolumeRTTI : public RTTIType <CLightProbeVolume, Component, CLightProbeVolumeRTTI>
 	{
 	private:
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_REFLPTR(mInternal, 0)
+		BS_END_RTTI_MEMBERS
 
 	public:
-		PostProcessSettingsRTTI() { }
+		CLightProbeVolumeRTTI()
+			:mInitMembers(this)
+		{ }
 
 		const String& getRTTIName() override
 		{
-			static String name = "PostProcessSettings";
+			static String name = "CLightProbeVolume";
 			return name;
 		}
 
 		UINT32 getRTTIId() override
 		{
-			return TID_PostProcessSettings;
+			return TID_CLightProbeVolume;
 		}
 
 		SPtr<IReflectable> newRTTIObject() override
 		{
-			assert(false); // Abstract class
-			return nullptr;
+			return GameObjectRTTI::createGameObject<CLightProbeVolume>();
 		}
 	};
 

+ 3 - 3
Source/BansheeCore/Include/BsCReflectionProbe.h

@@ -64,9 +64,9 @@ namespace bs
 		/** @copydoc ReflectionProbe::getBounds */
 		Sphere getBounds() const;
 
-		/** @copydoc ReflectionProbe::generate */
-		BS_SCRIPT_EXPORT(n:Generate)
-		void generate() { mInternal->generate(); }
+		/** @copydoc ReflectionProbe::capture */
+		BS_SCRIPT_EXPORT(n:Capture)
+		void capture() { mInternal->capture(); }
 
 		/** @name Internal
 		 *  @{

+ 36 - 67
Source/BansheeCore/Include/BsCamera.h

@@ -13,7 +13,7 @@
 #include "BsRay.h"
 #include "BsCoreObject.h"
 #include "BsConvexVolume.h"
-#include "BsPostProcessSettings.h"
+#include "BsRenderSettings.h"
 
 namespace bs 
 {
@@ -26,36 +26,9 @@ namespace bs
 	{
 		Transform = 1<<0,
 		Everything = 1<<1,
-		PostProcess = 1<<2
+		RenderSettings = 1<<2
 	};
 
-	/**	Flags that describe a camera. */
-	enum class CameraFlag
-	{
-		/** 
-		 * This flag is a signal to the renderer that his camera will only render overlays and doesn't require depth   
-		 * buffer or multi-sampled render targets. Such cameras will not render any scene objects. This can improve
-		 * performance and memory usage. 
-		 */
-		Overlay = 1 << 0,
-		/** 
-		 * High dynamic range allows light intensity to be more correctly recorded when rendering by allowing for a larger
-		 * range of values. The stored light is then converted into visible color range using exposure and a tone mapping 
-		 * operator.
-		 */
-		HDR = 1 << 1,
-		/** 
-		 * Specify that no lighting should be applied to scene objects and everything should be rendered using their
-		 * albedo texture.
-		 */
-		NoLighting = 1 << 2,
-		/** Specify that no shadows should be applied to scene objects. Only relevant if lighting is turned on. */
-		NoShadows = 1 << 3
-	};
-
-	typedef Flags<CameraFlag> CameraFlags;
-	BS_FLAGS_OPERATORS(CameraFlag);
-
 	/** @} */
 	/** @addtogroup Implementation
 	 *  @{
@@ -68,48 +41,48 @@ namespace bs
 	 * This class contains funcionality common to both core and non-core versions of the camera.
 	 */
 	class BS_CORE_EXPORT CameraBase
-    {
-    public:
+	{
+	public:
 		virtual ~CameraBase() { }
 
 		/**
 		 * Sets the camera horizontal field of view. This determines how wide the camera viewing angle is along the
 		 * horizontal axis. Vertical FOV is calculated from the horizontal FOV and the aspect ratio.
 		 */
-        virtual void setHorzFOV(const Radian& fovy);
+		virtual void setHorzFOV(const Radian& fovy);
 
 		/**	Retrieves the camera horizontal field of view. */
-        virtual const Radian& getHorzFOV() const;
+		virtual const Radian& getHorzFOV() const;
 
 		/**
 		 * Sets the distance from the frustum to the near clipping plane. Anything closer than the near clipping plane will
 		 * not be rendered. Decreasing this value decreases depth buffer precision.
 		 */
-        virtual void setNearClipDistance(float nearDist);
+		virtual void setNearClipDistance(float nearDist);
 
 		/**
 		 * Retrieves the distance from the frustum to the near clipping plane. Anything closer than the near clipping plane
 		 * will not be rendered. Decreasing this value decreases depth buffer precision.
 		 */
-        virtual float getNearClipDistance() const;
+		virtual float getNearClipDistance() const;
 
 		/**
 		 * Sets the distance from the frustum to the far clipping plane. Anything farther than the far clipping plane will
 		 * not be rendered. Increasing this value decreases depth buffer precision.
 		 */
-        virtual void setFarClipDistance(float farDist);
+		virtual void setFarClipDistance(float farDist);
 
 		/**
 		 * Retrieves the distance from the frustum to the far clipping plane. Anything farther than the far clipping plane
 		 * will not be rendered. Increasing this value decreases depth buffer precision.
 		 */
-        virtual float getFarClipDistance() const;
+		virtual float getFarClipDistance() const;
 
 		/**	Sets the current viewport aspect ratio (width / height). */
-        virtual void setAspectRatio(float ratio);
+		virtual void setAspectRatio(float ratio);
 
 		/**	Returns current viewport aspect ratio (width / height). */
-        virtual float getAspectRatio() const;
+		virtual float getAspectRatio() const;
 
 		/**	Sets camera world space position. */
 		virtual void setPosition(const Vector3& position);
@@ -164,14 +137,14 @@ namespace bs
 		 * You should use this matrix when sending the matrix to the render system to make sure everything works 
 		 * consistently when other render systems are used.
 		 */
-        virtual const Matrix4& getProjectionMatrixRS() const;
+		virtual const Matrix4& getProjectionMatrixRS() const;
 
 		/** 
 		 * Returns the inverse of the render-system specific projection matrix.
 		 *
 		 * @see		getProjectionMatrixRS
 		 */
-        virtual const Matrix4& getProjectionMatrixRSInv() const;
+		virtual const Matrix4& getProjectionMatrixRSInv() const;
 
 		/** 
 		 * Returns the standard projection matrix that determines how are 3D points projected to two dimensions. Returned
@@ -180,18 +153,18 @@ namespace bs
 		 * @note	
 		 * Different render systems will expect different projection matrix layouts, in which case use 
 		 * getProjectionMatrixRS().
-         */
-        virtual const Matrix4& getProjectionMatrix() const;
+		 */
+		virtual const Matrix4& getProjectionMatrix() const;
 
 		/** 
 		 * Returns the inverse of the projection matrix.
 		 *
 		 * @see		getProjectionMatrix
 		 */
-        virtual const Matrix4& getProjectionMatrixInv() const;
+		virtual const Matrix4& getProjectionMatrixInv() const;
 
 		/** Gets the camera view matrix. Used for positioning/orienting the camera. */
-        virtual const Matrix4& getViewMatrix() const;
+		virtual const Matrix4& getViewMatrix() const;
 
 		/** 
 		 * Returns the inverse of the view matrix.
@@ -203,7 +176,7 @@ namespace bs
 		/** 
 		 * Sets whether the camera should use the custom view matrix. When this is enabled camera will no longer calculate
 		 * its view matrix based on position/orientation and caller will be resonsible to keep the view matrix up to date.
-         */
+		 */
 		virtual void setCustomViewMatrix(bool enable, const Matrix4& viewMatrix = Matrix4::IDENTITY);
 
 		/** Returns true if a custom view matrix is used. */
@@ -213,32 +186,32 @@ namespace bs
 		 * Sets whether the camera should use the custom projection matrix. When this is enabled camera will no longer
 		 * calculate its projection matrix based on field of view, aspect and other parameters and caller will be resonsible
 		 * to keep the projection matrix up to date.
-         */
+		 */
 		virtual void setCustomProjectionMatrix(bool enable, const Matrix4& projectionMatrix = Matrix4::IDENTITY);
 
 		/** Returns true if a custom projection matrix is used. */
 		virtual bool isCustomProjectionMatrixEnabled() const { return mCustomProjMatrix; }
 
 		/** Returns a convex volume representing the visible area of the camera, in local space. */
-        virtual const ConvexVolume& getFrustum() const;
+		virtual const ConvexVolume& getFrustum() const;
 
 		/** Returns a convex volume representing the visible area of the camera, in world space. */
-        virtual ConvexVolume getWorldFrustum() const;
+		virtual ConvexVolume getWorldFrustum() const;
 
 		/**	Returns the bounding of the frustum. */
-        const AABox& getBoundingBox() const;
+		const AABox& getBoundingBox() const;
 
 		/**
 		 * Sets the type of projection used by the camera. Projection type controls how is 3D geometry projected onto a 
 		 * 2D plane.
 		 */
-        virtual void setProjectionType(ProjectionType pt);
+		virtual void setProjectionType(ProjectionType pt);
 
 		/**
 		 * Returns the type of projection used by the camera. Projection type controls how is 3D geometry projected onto a
 		 * 2D plane.
 		 */
-        virtual ProjectionType getProjectionType() const;
+		virtual ProjectionType getProjectionType() const;
 
 		/**
 		 * Sets the orthographic window height, for use with orthographic rendering only. 
@@ -309,17 +282,14 @@ namespace bs
 		 */
 		void setMSAACount(UINT32 count) { mMSAA = count; _markCoreDirty(); }
 
-		/** Returns settings that are used for controling post-process operations like tonemapping. */
-		const SPtr<PostProcessSettings>& getPostProcessSettings() const { return mPPSettings; }
-
-		/** Sets settings that are used for controling post-process operations like tonemapping. */
-		void setPostProcessSettings(const SPtr<PostProcessSettings>& settings) { mPPSettings = settings; _markCoreDirty(CameraDirtyFlag::PostProcess); }
-
-		/**	Retrieves flags that define the camera. */
-		CameraFlags getFlags() const { return mCameraFlags; }
+		/** @copydoc setRenderSettings() */
+		const SPtr<RenderSettings>& getRenderSettings() const { return mRenderSettings; }
 
-		/**	Enables or disables flags that define the camera's behaviour. */
-		void setFlag(const CameraFlag& flag, bool enable);
+		/** 
+		 * Settings that control rendering for this view. They determine how will the renderer process this view, which
+		 * effects will be enabled, and what properties will those effects use.
+		 */
+		void setRenderSettings(const SPtr<RenderSettings>& settings) { mRenderSettings = settings; _markCoreDirty(CameraDirtyFlag::RenderSettings); }
 
 		/**
 		 * Converts a point in world space to screen coordinates (in pixels corresponding to the render target attached to
@@ -456,9 +426,8 @@ namespace bs
 		 */
 		virtual void _markCoreDirty(CameraDirtyFlag flag = CameraDirtyFlag::Everything) { }
 
-    protected:
+	protected:
 		UINT64 mLayers; /**< Bitfield that can be used for filtering what objects the camera sees. */
-		CameraFlags mCameraFlags; /**< Flags that further determine type of camera. */
 
 		Vector3 mPosition; /**< World space position. */
 		Quaternion mRotation; /**< World space rotation. */
@@ -476,7 +445,7 @@ namespace bs
 		bool mCustomProjMatrix; /**< Is custom projection matrix set. */
 		UINT8 mMSAA; /**< Number of samples to render the scene with. */
 
-		SPtr<PostProcessSettings> mPPSettings; /**< Settings used to control post-process operations. */
+		SPtr<RenderSettings> mRenderSettings; /**< Settings used to control rendering for this camera. */
 
 		bool mFrustumExtentsManuallySet; /**< Are frustum extents manually set. */
 
@@ -497,7 +466,7 @@ namespace bs
 
 	/** @} */
 
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 
@@ -572,7 +541,7 @@ namespace bs
 		friend class CameraRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		RTTITypeBase* getRTTI() const override;
-     };
+	};
 
 	namespace ct
 	{

+ 2 - 4
Source/BansheeCore/Include/BsCameraRTTI.h

@@ -38,10 +38,10 @@ namespace bs
 			BS_RTTI_MEMBER_PLAIN(mRight, 19)
 			BS_RTTI_MEMBER_PLAIN(mTop, 20)
 			BS_RTTI_MEMBER_PLAIN(mBottom, 21)
-			BS_RTTI_MEMBER_PLAIN(mCameraFlags, 22)
+			//BS_RTTI_MEMBER_PLAIN(mCameraFlags, 22)
 			BS_RTTI_MEMBER_PLAIN(mMSAA, 23)
 			/** BS_RTTI_MEMBER_PLAIN(mPPSettings, 24) */
-			BS_RTTI_MEMBER_REFLPTR(mPPSettings, 25)
+			BS_RTTI_MEMBER_REFLPTR(mRenderSettings, 25)
 			/** BS_RTTI_MEMBER_REFL(mSkyTexture, 26) */
 		BS_END_RTTI_MEMBERS
 			
@@ -75,8 +75,6 @@ namespace bs
 		}
 	};
 
-	BS_ALLOW_MEMCPY_SERIALIZATION(CameraFlags);
-
 	/** @} */
 	/** @endcond */
 }

+ 17 - 6
Source/BansheeCore/Include/BsCorePrerequisites.h

@@ -344,8 +344,9 @@ namespace bs
 	class GraphicsPipelineState;
 	class ComputePipelineState;
 	class ReflectionProbe;
-    class CReflectionProbe;
-    class CSkybox;
+	class CReflectionProbe;
+	class CSkybox;
+	class CLightProbeVolume;
 	// Asset import
 	class SpecificImporter;
 	class Importer;
@@ -447,7 +448,7 @@ namespace bs
 		class RenderStateManager;
 		class HardwareBufferManager;
 		class ReflectionProbe;
-        class Skybox;
+		class Skybox;
 	}
 }
 
@@ -562,7 +563,7 @@ namespace bs
 		TID_ImportedAnimationEvents = 1124,
 		TID_CBone = 1125,
 		TID_MaterialParamData = 1126,
-		TID_PostProcessSettings = 1127,
+		TID_RenderSettings = 1127,
 		TID_MorphShape = 1128,
 		TID_MorphShapes = 1129,
 		TID_MorphChannel = 1130,
@@ -572,6 +573,8 @@ namespace bs
 		TID_Skybox = 1134,
 		TID_CSkybox = 1135,
 		TID_LightProbeVolume = 1136,
+		TID_SavedLightProbeInfo = 1137,
+		TID_CLightProbeVolume = 1138,
 
 		// Moved from Engine layer
 		TID_CCamera = 30000,
@@ -580,6 +583,13 @@ namespace bs
 		TID_Renderable = 30004,
 		TID_Light = 30011,
 		TID_CLight = 30012,
+		TID_AutoExposureSettings = 30016,
+		TID_TonemappingSettings = 30017,
+		TID_WhiteBalanceSettings = 30018,
+		TID_ColorGradingSettings = 30019,
+		TID_DepthOfFieldSettings = 30020,
+		TID_AmbientOcclusionSettings = 30021,
+		TID_ScreenSpaceReflectionsSettings = 30022
 	};
 }
 
@@ -643,8 +653,9 @@ namespace bs
 	typedef GameObjectHandle<CFixedJoint> HFixedJoint;
 	typedef GameObjectHandle<CD6Joint> HD6Joint;
 	typedef GameObjectHandle<CCharacterController> HCharacterController;
-    typedef GameObjectHandle<CReflectionProbe> HReflectionProbe;
-    typedef GameObjectHandle<CSkybox> HSkybox;
+	typedef GameObjectHandle<CReflectionProbe> HReflectionProbe;
+	typedef GameObjectHandle<CSkybox> HSkybox;
+	typedef GameObjectHandle<CLightProbeVolume> HLightProbeVolume;
 
 	/** @} */
 }

+ 22 - 22
Source/BansheeCore/Include/BsHardwareBuffer.h

@@ -18,9 +18,9 @@ namespace bs
 	 * @note	Be aware that reading from non-system memory hardware buffers is usually slow and should be avoided.
 	 */
 	class BS_CORE_EXPORT HardwareBuffer
-    {
-    public:
-        virtual ~HardwareBuffer() {}
+	{
+	public:
+		virtual ~HardwareBuffer() {}
 
 		/**
 		 * Locks a portion of the buffer and returns pointer to the locked area. You must call unlock() when done.
@@ -35,15 +35,15 @@ namespace bs
 		 * @param[in]	queueIdx	Device queue to perform any read/write operations on. See @ref queuesDoc.
 		 */
 		virtual void* lock(UINT32 offset, UINT32 length, GpuLockOptions options, UINT32 deviceIdx = 0, UINT32 queueIdx = 0)
-        {
-            assert(!isLocked() && "Cannot lock this buffer, it is already locked!");
-            void* ret = map(offset, length, options, deviceIdx, queueIdx);
-            mIsLocked = true;
+		{
+			assert(!isLocked() && "Cannot lock this buffer, it is already locked!");
+			void* ret = map(offset, length, options, deviceIdx, queueIdx);
+			mIsLocked = true;
 
 			mLockStart = offset;
 			mLockSize = length;
-            return ret;
-        }
+			return ret;
+		}
 
 		/**
 		 * Locks the entire buffer and returns pointer to the locked area. You must call unlock() when done.
@@ -55,19 +55,19 @@ namespace bs
 		 *							the method returns null.
 		 * @param[in]	queueIdx	Device queue to perform any read/write operations on. See @ref queuesDoc.
 		 */
-        void* lock(GpuLockOptions options, UINT32 deviceIdx = 0, UINT32 queueIdx = 0)
-        {
-            return this->lock(0, mSize, options, deviceIdx, queueIdx);
-        }
+		void* lock(GpuLockOptions options, UINT32 deviceIdx = 0, UINT32 queueIdx = 0)
+		{
+			return this->lock(0, mSize, options, deviceIdx, queueIdx);
+		}
 
 		/**	Releases the lock on this buffer. */
 		virtual void unlock()
-        {
-            assert(isLocked() && "Cannot unlock this buffer, it is not locked!");
+		{
+			assert(isLocked() && "Cannot unlock this buffer, it is not locked!");
 
-            unmap();
-            mIsLocked = false;
-        }
+			unmap();
+			mIsLocked = false;
+		}
 
 		/**
 		 * Reads data from a portion of the buffer and copies it to the destination buffer. Caller must ensure destination 
@@ -81,7 +81,7 @@ namespace bs
 		 *							no data will be read.		
 		 * @param[in]	queueIdx	Device queue to perform the read operation on. See @ref queuesDoc.
 		 */
-        virtual void readData(UINT32 offset, UINT32 length, void* dest, UINT32 deviceIdx = 0, UINT32 queueIdx = 0) = 0;
+		virtual void readData(UINT32 offset, UINT32 length, void* dest, UINT32 deviceIdx = 0, UINT32 queueIdx = 0) = 0;
 
 		/**
 		 * Writes data into a portion of the buffer from the source memory. 
@@ -93,7 +93,7 @@ namespace bs
 		 * @param[in]	writeFlags	Optional write flags that may affect performance.
 		 * @param[in]	queueIdx	Device queue to perform the write operation on. See @ref queuesDoc.
 		 */
-        virtual void writeData(UINT32 offset, UINT32 length, const void* source,
+		virtual void writeData(UINT32 offset, UINT32 length, const void* source,
 				BufferWriteType writeFlags = BWT_NORMAL, UINT32 queueIdx = 0) = 0;
 
 		/**
@@ -125,10 +125,10 @@ namespace bs
 		}
 			
 		/** Returns the size of this buffer in bytes. */
-        UINT32 getSize() const { return mSize; }
+		UINT32 getSize() const { return mSize; }
 
 		/**	Returns whether or not this buffer is currently locked. */
-        bool isLocked() const { return mIsLocked; }	
+		bool isLocked() const { return mIsLocked; }
 
 	protected:
 		friend class HardwareBufferManager;

+ 71 - 0
Source/BansheeCore/Include/BsIBLUtility.h

@@ -0,0 +1,71 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsModule.h"
+
+namespace bs { namespace ct
+{
+	/** @addtogroup Renderer-Internal
+	 *  @{
+	 */
+
+	/** Helper class that handles generation and processing of textures used for image based lighting. */
+	class BS_CORE_EXPORT IBLUtility : public Module<IBLUtility>
+	{
+	public:
+		/**
+		 * Performs filtering on the cubemap, populating its mip-maps with filtered values that can be used for
+		 * evaluating specular reflections.
+		 * 
+		 * @param[in, out]	cubemap		Cubemap to filter. Its mip level 0 will be read, filtered and written into
+		 *								other mip levels.
+		 * @param[in]		scratch		Temporary cubemap texture to use for the filtering process. Must match the size of
+		 *								the source cubemap. Provide null to automatically create a scratch cubemap.
+		 */
+		virtual void filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch) const = 0;
+
+		/**
+		 * Performs filtering on the cubemap, populating the output cubemap with values that can be used for evaluating
+		 * irradiance for use in diffuse lighting. Uses order-5 SH (25 coefficients) and outputs the values in the form of
+		 * a cubemap.
+		 * 
+		 * @param[in]		cubemap		Cubemap to filter. Its mip level 0 will be used as source.
+		 * @param[in]		output		Output cubemap to store the irradiance data in.
+		 */
+		virtual void filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output) const = 0;
+
+		/**
+		 * Performs filtering on the cubemap, populating the output cubemap with values that can be used for evaluating
+		 * irradiance for use in diffuse lighting. Uses order-5 SH (9 coefficients) and outputs the values in the form of
+		 * a cubemap.
+		 * 
+		 * @param[in]		cubemap		Cubemap to filter. Its mip level 0 will be used as source.
+		 * @param[in]		output		Output buffer in which to place the results. Must be allocated using 
+		 *								IrradianceReduceMat<ORDER>::createOutputBuffer();
+		 * @param[in]		outputIdx	Index in the output buffer at which to write the output coefficients to.
+		 */
+		virtual void filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<GpuBuffer>& output, 
+			UINT32 outputIdx) const = 0;
+
+		/**
+		 * Scales a cubemap and outputs it in the destination texture, using hardware acceleration. If both textures are the
+		 * same size, performs a copy instead.
+		 *
+		 * @param[in]   src				Source cubemap to scale.
+		 * @param[in]   srcMip			Determines which mip level of the source texture to scale.
+		 * @param[in]   dst				Desination texture to output the scaled data to. Must be usable as a render target.
+		 * @param[in]   dstMip			Determines which mip level of the destination texture to scale.
+		 */
+		virtual void scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip) const = 0;
+
+		static const UINT32 REFLECTION_CUBEMAP_SIZE;
+		static const UINT32 IRRADIANCE_CUBEMAP_SIZE;
+	};
+
+	/**	Provides easy access to IBLUtility. */
+	BS_CORE_EXPORT const IBLUtility& gIBLUtility();
+
+	/** @} */
+}}

+ 2 - 2
Source/BansheeCore/Include/BsLight.h

@@ -12,7 +12,7 @@
 
 namespace bs
 {
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 
@@ -221,7 +221,7 @@ namespace bs
 	};
 
 	/** @} */
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 

+ 135 - 19
Source/BansheeCore/Include/BsLightProbeVolume.h

@@ -7,9 +7,15 @@
 #include "BsAABox.h"
 #include "BsVector3.h"
 #include "BsQuaternion.h"
+#include "BsVectorNI.h"
 
 namespace bs
 {
+	namespace ct
+	{
+		class RendererTask;
+	}
+
 	/** @addtogroup Implementation
 	 *  @{
 	 */
@@ -58,12 +64,31 @@ namespace bs
 	};
 
 	/** @} */
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 
 	namespace ct { class LightProbeVolume; }
 
+	/** Vector representing spherical harmonic coefficients for a light probe. */
+	struct LightProbeSHCoefficients
+	{
+		LightProbeSHCoefficients()
+			:coeffsR(), coeffsG(), coeffsB()
+		{ }
+
+		float coeffsR[9];
+		float coeffsG[9];
+		float coeffsB[9];
+	};
+
+	/** SH coefficients for a specific light probe, and its handle. */
+	struct LightProbeCoefficientInfo
+	{
+		UINT32 handle;
+		LightProbeSHCoefficients coefficients;
+	};
+
 	/** 
 	 * Allows you to define a volume of light probes that will be used for indirect lighting. Lighting information in the
 	 * scene will be interpolated from nearby probes to calculate the amount of indirect lighting at that position. It is
@@ -78,13 +103,18 @@ namespace bs
 		{
 			ProbeInfo() {}
 			ProbeInfo(LightProbeFlags flags, const Vector3& position)
-				:flags(flags), position(position) 
+				:flags(flags), position(position)
 			{ }
 
 			LightProbeFlags flags;
 			Vector3 position;
+
+			/** Coefficients are only valid directly after deserialization, or after updateCoefficients() is called. */
+			LightProbeSHCoefficients coefficients;
 		};
 	public:
+		~LightProbeVolume();
+
 		/** Adds a new probe at the specified position and returns a handle to the probe. */
 		UINT32 addProbe(const Vector3& position);
 
@@ -100,6 +130,44 @@ namespace bs
 		 */
 		void removeProbe(UINT32 handle);
 
+		/**
+		 * Causes the information for this specific light probe to be updated. You generally want to call this when the
+		 * probe is moved or the scene around the probe changes.
+		 */
+		void renderProbe(UINT32 handle);
+
+		/**
+		 * Causes the information for all lights probes to be updated. You generally want to call this if you move the
+		 * entire light volume or the scene around the volume changes.
+		 */
+		void renderProbes();
+
+		/** 
+		 * Resizes the light probe grid and inserts new light probes, if the new size is larger than previous size.
+		 * New probes are inserted in a grid pattern matching the new size and density parameters. 
+		 * 
+		 * Note that shrinking the volume will not remove light probes. In order to remove probes outside of the new volume
+		 * call clip().
+		 * 
+		 * Resize will not change the positions of current light probes. If you wish to reset all probes to the currently
+		 * set grid position, call reset().
+
+		 * @param[in]	volume		Axis aligned volume to be covered by the light probes.
+		 * @param[in]	cellCount	Number of grid cells to split the volume into. Minimum number of 1, in which case each
+		 *							corner of the volume is represented by a single probe. Higher values subdivide the
+		 *							volume in an uniform way.
+		 */
+		void resize(const AABox& volume, const Vector3I& cellCount = {1, 1, 1});
+
+		/** Removes any probes outside of the current grid volume. */
+		void clip();
+
+		/** 
+		 * Resets all probes to match the original grid pattern. This will reset probe positions, as well as add/remove
+		 * probes as necessary, essentially losing any custom changes to the probes.
+		 */
+		void reset();
+
 		/**	Retrieves an implementation of the object usable only from the core thread. */
 		SPtr<ct::LightProbeVolume> getCore() const;
 
@@ -107,16 +175,33 @@ namespace bs
 		 * Creates a new light volume with probes aligned in a grid pattern.
 		 * 
 		 * @param[in]	volume		Axis aligned volume to be covered by the light probes.
-		 * @param[in]	density		Density of light probes in each direction. Starting on one side of the volume, a new
-		 *							probe will be added every 1/density meters (per-axis). Note that one probe will be
-		 *							placed at the start and at the end of the volume, regardless of density. This means the
-		 *							smallest volume will have 8 probes.
+		 * @param[in]	cellCount	Number of grid cells to split the volume into. Minimum number of 1, in which case each
+		 *							corner of the volume is represented by a single probe. Higher values subdivide the
+		 *							volume in an uniform way.
 		 */
-		static SPtr<LightProbeVolume> create(const AABox& volume = AABox::UNIT_BOX, const Vector3& density = Vector3::ONE);
+		static SPtr<LightProbeVolume> create(const AABox& volume = AABox::UNIT_BOX, const Vector3I& cellCount = {1, 1, 1});
+
+		/**	Returns the hash value that can be used to identify if the internal data needs an update. */
+		UINT32 _getLastModifiedHash() const { return mLastUpdateHash; }
+
+		/**	Sets the hash value that can be used to identify if the internal data needs an update. */
+		void _setLastModifiedHash(UINT32 hash) { mLastUpdateHash = hash; }
+
+		/** Updates the transfrom from the provided scene object, if the scene object's data is detected to be dirty. */
+		void _updateTransform(const HSceneObject& so, bool force = false);
 	protected:
 		friend class ct::LightProbeVolume;
 
-		LightProbeVolume(const AABox& volume, const Vector3& density);
+		LightProbeVolume(const AABox& volume, const Vector3I& cellCount);
+
+		/** Renders the light probe data on the core thread. */
+		void runRenderProbeTask();
+
+		/** 
+		 * Fetches latest SH coefficient data from the core thread. Note this method will block the caller thread until
+		 * the data is fetched from the core thread. It will also force any in-progress light probe updated to finish.
+		 */
+		void updateCoefficients();
 
 		/** @copydoc CoreObject::createCore */
 		SPtr<ct::CoreObject> createCore() const override;
@@ -132,7 +217,12 @@ namespace bs
 
 	private:
 		UnorderedMap<UINT32, ProbeInfo> mProbes;
+		AABox mVolume = AABox::UNIT_BOX;
+		Vector3I mCellCount;
+		UINT32 mLastUpdateHash;
+
 		UINT32 mNextProbeId = 0;
+		SPtr<ct::RendererTask> mRendererTask;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -173,20 +263,23 @@ namespace bs
 		/**	Retrieves an ID that can be used for uniquely identifying this object by the renderer. */
 		UINT32 getRendererId() const { return mRendererId; }
 
+		/** Returns the number of light probes that are active. */
+		UINT32 getNumActiveProbes() const { return (UINT32)mProbeMap.size(); }
+
+		/** Returns a list of positions for all light probes. Only the first getNumActiveProbes() entries are active. */
+		const Vector<Vector3>& getLightProbePositions() const { return mProbePositions; }
+
 		/** 
-		 * Parses the list of probes and reorganizes it by removing gaps so that all probes are sequential. 
-		 * 
-		 * @param[out]	freedEntries	A list of entries mapping to the GPU buffer where probe SH coefficients are stored.
-		 *								These are the entries that have been freed since the last call to prune().
-		 * @param[in]	freeAll			If true, all probes held by this volume will be marked as freed.
+		 * Returns non-positional information about all light probes. Only the first getNumActiveProbes() entries are 
+		 * active. 
 		 */
-		void prune(Vector<UINT32>& freedEntries, bool freeAll = false);
+		const Vector<LightProbeInfo>& getLightProbeInfos() const { return mProbeInfos; }
 
-		/** Returns information about all light probes. */
-		Vector<LightProbeInfo>& getLightProbeInfos() { return mProbeInfos; }
+		/** Populates the vector with SH coefficients for each light probe. Involves reading the GPU buffer. */
+		void getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const;
 
-		/** Returns a list of positions for all light probes. */
-		Vector<Vector3>& getLightProbePositions() { return mProbePositions; }
+		/** Returns the GPU buffer containing SH coefficients. */
+		SPtr<GpuBuffer> getCoefficientsBuffer() const { return mCoefficients; }
 	protected:
 		friend class bs::LightProbeVolume;
 
@@ -198,13 +291,36 @@ namespace bs
 		/** @copydoc CoreObject::syncToCore */
 		void syncToCore(const CoreSyncData& data) override;
 
+		/** 
+		 * Renders dirty probes and updates their SH coefficients in the local GPU buffer. 
+		 *
+		 * @param[in]	maxProbes	Maximum number of probes to render. Set to zero to render all dirty probes. Limiting the
+		 *							number of probes allows the rendering to be distributed over multiple frames.
+		 * @return					True if there are no more dirty probes to process.
+		 */
+		bool renderProbes(UINT32 maxProbes);
+
+		/** 
+		 * Resizes the internal GPU buffer that stores light probe SH coefficients, to the specified size (in the number
+		 * of probes). 
+		 */
+		void resizeCoefficientBuffer(UINT32 count);
+
 		UINT32 mRendererId = 0;
 		UnorderedMap<UINT32, UINT32> mProbeMap; // Map from static indices to compact list of probes
+		UINT32 mFirstDirtyProbe = 0;
 
 		Vector<Vector3> mProbePositions;
 		Vector<LightProbeInfo> mProbeInfos;
+
+		// Contains SH coefficients for the probes
+		SPtr<GpuBuffer> mCoefficients;
+		UINT32 mCoeffBufferSize = 0;
+
+		// Temporary until initialization
+		Vector<LightProbeSHCoefficients> mInitCoefficients;
 	};
 	}
 
 	/** @} */
-}
+}

+ 124 - 2
Source/BansheeCore/Include/BsLightProbeVolumeRTTI.h

@@ -5,6 +5,9 @@
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
 #include "BsLightProbeVolume.h"
+#include "BsRenderer.h"
+#include "BsCoreThread.h"
+#include "BsTextureRTTI.h"
 
 namespace bs
 {
@@ -13,17 +16,136 @@ namespace bs
 	 *  @{
 	 */
 
+	BS_ALLOW_MEMCPY_SERIALIZATION(LightProbeSHCoefficients)
+
+	/** Serializable information about a single light probe. */
+	struct SavedLightProbeInfo
+	{
+		Vector<Vector3> positions;
+		Vector<LightProbeSHCoefficients> coefficients;
+	};
+
+	template<> struct RTTIPlainType<SavedLightProbeInfo>
+	{	
+		enum { id = TID_SavedLightProbeInfo }; enum { hasDynamicSize = 1 };
+
+		static void toMemory(const SavedLightProbeInfo& data, char* memory)
+		{ 
+			UINT32 size = getDynamicSize(data);
+
+			UINT32 curSize = sizeof(UINT32);
+			memcpy(memory, &size, curSize);
+			memory += curSize;
+
+			UINT32 version = 0;
+
+			memory = rttiWriteElem(version, memory);
+			memory = rttiWriteElem(data.positions, memory);
+			memory = rttiWriteElem(data.coefficients, memory);
+		}
+
+		static UINT32 fromMemory(SavedLightProbeInfo& data, char* memory)
+		{ 
+			UINT32 size;
+			memcpy(&size, memory, sizeof(UINT32)); 
+			memory += sizeof(UINT32);
+
+			UINT32 version;
+			memory = rttiReadElem(version, memory);
+
+			switch(version)
+			{
+			case 0:
+				rttiReadElem(data.positions, memory);
+				rttiReadElem(data.coefficients, memory);
+				break;
+			default:
+				LOGERR("Unknown version of SavedLightProbeInfo data. Unable to deserialize.");
+				break;
+			}
+
+			return size;
+		}
+
+		static UINT32 getDynamicSize(const SavedLightProbeInfo& data)	
+		{ 
+			UINT64 dataSize = rttiGetElemSize(data.positions) + rttiGetElemSize(data.coefficients) + sizeof(UINT32) * 2;
+
+#if BS_DEBUG_MODE
+			if(dataSize > std::numeric_limits<UINT32>::max())
+			{
+				BS_EXCEPT(InternalErrorException, "Data overflow! Size doesn't fit into 32 bits.");
+			}
+#endif
+
+			return (UINT32)dataSize;
+		}	
+	}; 
+
 	class BS_CORE_EXPORT LightProbeVolumeRTTI : public RTTIType <LightProbeVolume, IReflectable, LightProbeVolumeRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
 			BS_RTTI_MEMBER_PLAIN(mPosition, 0)
 			BS_RTTI_MEMBER_PLAIN(mRotation, 1)
+			BS_RTTI_MEMBER_PLAIN(mVolume, 3)
+			BS_RTTI_MEMBER_PLAIN(mCellCount, 4)
 		BS_END_RTTI_MEMBERS
+
+		SavedLightProbeInfo& getProbeInfo(LightProbeVolume* obj)
+		{
+			obj->updateCoefficients();
+
+			UINT32 numProbes = (UINT32)obj->mProbes.size();
+			SavedLightProbeInfo savedLightProbeInfo;
+			savedLightProbeInfo.coefficients.resize(numProbes);
+			savedLightProbeInfo.positions.resize(numProbes);
+
+			UINT32 idx = 0;
+			for(auto& entry : obj->mProbes)
+			{
+				savedLightProbeInfo.positions[idx] = entry.second.position;
+				savedLightProbeInfo.coefficients[idx] = entry.second.coefficients;
+
+				idx++;
+			}
+
+			obj->mRTTIData = savedLightProbeInfo;
+			return any_cast_ref<SavedLightProbeInfo>(obj->mRTTIData);
+		}
+
+		void setProbeInfo(LightProbeVolume* obj, SavedLightProbeInfo& data)
+		{
+			obj->mProbes.clear();
+
+			UINT32 numProbes = (UINT32)data.positions.size();
+			for(UINT32 i = 0; i < numProbes; ++i)
+			{
+				UINT32 handle = obj->mNextProbeId++;
+
+				LightProbeVolume::ProbeInfo probeInfo;
+				probeInfo.flags = LightProbeFlags::Clean;
+				probeInfo.position = data.positions[i];
+				probeInfo.coefficients = data.coefficients[i];
+
+				obj->mProbes[handle] = probeInfo;
+			}
+		}
 	public:
 		LightProbeVolumeRTTI()
 			:mInitMembers(this)
-		{ }
+		{
+			
+			addPlainField("mProbeInfo", 2, &LightProbeVolumeRTTI::getProbeInfo, &LightProbeVolumeRTTI::setProbeInfo, 
+				RTTI_Flag_SkipInReferenceSearch);
+		}
+
+		void onSerializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
+		{
+			// Clear temporary data
+			LightProbeVolume* volume = static_cast<LightProbeVolume*>(obj);
+			volume->mRTTIData = nullptr;
+		}
 
 		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
 		{
@@ -52,4 +174,4 @@ namespace bs
 
 	/** @} */
 	/** @endcond */
-}
+}

+ 150 - 100
Source/BansheeCore/Include/BsPixelData.h

@@ -14,30 +14,30 @@ namespace bs
 	 */
 
 	/** Pixel formats usable by images, textures and render surfaces. */
-    enum BS_SCRIPT_EXPORT() PixelFormat
-    {
-        /** Unknown pixel format. */
-        PF_UNKNOWN				BS_SCRIPT_EXPORT(ex:true) = 0,
-        /** 8-bit pixel format, all bits red. */
-        PF_R8					BS_SCRIPT_EXPORT(n:R8) = 1,
-		/** 2 byte pixel format, 1 byte red, 1 byte green. */
-		PF_R8G8					BS_SCRIPT_EXPORT(n:R8G8) = 2,
-        /** 24-bit pixel format, 8 bits for red, green and blue. */
-        PF_R8G8B8				BS_SCRIPT_EXPORT(n:R8G8B8) = 3,
-        /** 24-bit pixel format, 8 bits for blue, green and red. */
-        PF_B8G8R8				BS_SCRIPT_EXPORT(n:B8G8R8) = 4,
-        /** 32-bit pixel format, 8 bits for blue, green, red and alpha. */
-        PF_B8G8R8A8				BS_SCRIPT_EXPORT(n:B8G8R8A8) = 7,
-		/** 32-bit pixel format, 8 bits for red, green, blue and alpha. */
-		PF_R8G8B8A8				BS_SCRIPT_EXPORT(n:R8G8B8A8) = 8,
-        /** DXT1/BC1 format containing opaque RGB or 1-bit alpha RGB. 4 bits per pixel. */
-        PF_BC1					BS_SCRIPT_EXPORT(n:BC1) = 13,
+	enum BS_SCRIPT_EXPORT() PixelFormat
+	{
+		/** Unknown pixel format. */
+		PF_UNKNOWN				BS_SCRIPT_EXPORT(ex:true) = 0,
+		/** 8-bit 1-channel pixel format, unsigned normalized. */
+		PF_R8					BS_SCRIPT_EXPORT(n:R8) = 1,
+		/** 8-bit 2-channel pixel format, unsigned normalized. */
+		PF_RG8					BS_SCRIPT_EXPORT(n:RG8) = 2,
+		/** 8-bit 3-channel pixel format, unsigned normalized. */
+		PF_RGB8					BS_SCRIPT_EXPORT(n:RGB8) = 3,
+		/** 8-bit 3-channel pixel format, unsigned normalized. */
+		PF_BGR8					BS_SCRIPT_EXPORT(n:BGR8) = 4,
+		/** 8-bit 4-channel pixel format, unsigned normalized. */
+		PF_BGRA8				BS_SCRIPT_EXPORT(n:BGRA8) = 7,
+		/** 8-bit 4-channel pixel format, unsigned normalized. */
+		PF_RGBA8				BS_SCRIPT_EXPORT(n:RGBA8) = 8,
+		/** DXT1/BC1 format containing opaque RGB or 1-bit alpha RGB. 4 bits per pixel. */
+		PF_BC1					BS_SCRIPT_EXPORT(n:BC1) = 13,
 		/** DXT3/BC2 format containing RGB with premultiplied alpha. 4 bits per pixel. */
 		PF_BC1a					BS_SCRIPT_EXPORT(ex:true) = 14,
-        /** DXT3/BC2 format containing RGB with explicit alpha. 8 bits per pixel. */
-        PF_BC2					BS_SCRIPT_EXPORT(n:BC2) = 15,
-        /** DXT5/BC2 format containing RGB with explicit alpha. 8 bits per pixel. Better alpha gradients than BC2. */
-        PF_BC3					BS_SCRIPT_EXPORT(n:BC3) = 16,
+		/** DXT3/BC2 format containing RGB with explicit alpha. 8 bits per pixel. */
+		PF_BC2					BS_SCRIPT_EXPORT(n:BC2) = 15,
+		/** DXT5/BC2 format containing RGB with explicit alpha. 8 bits per pixel. Better alpha gradients than BC2. */
+		PF_BC3					BS_SCRIPT_EXPORT(n:BC3) = 16,
 		/** One channel compressed format. 4 bits per pixel. */
 		PF_BC4					BS_SCRIPT_EXPORT(n:BC4) = 17,
 		/** Two channel compressed format. 8 bits per pixel. */
@@ -49,81 +49,131 @@ namespace bs
 		 * higher decompress overhead. 8 bits per pixel. 
 		 */
 		PF_BC7					BS_SCRIPT_EXPORT(n:BC7) = 20,
-		/** 16-bit pixel format, 16 bits (float) for red. */
-        PF_FLOAT16_R			BS_SCRIPT_EXPORT(n:Float16_R) = 21,
-		/** 32-bit, 2-channel s10e5 floating point pixel format, 16-bit red, 16-bit green. */
-		PF_FLOAT16_RG			BS_SCRIPT_EXPORT(n:Float16_RG) = 22,
-        /** 48-bit pixel format, 16 bits (float) for red, 16 bits (float) for green, 16 bits (float) for blue. */
-        PF_FLOAT16_RGB			BS_SCRIPT_EXPORT(n:Float16_RGB) = 23,
-        /** 
-		 * 64-bit pixel format, 16 bits (float) for red, 16 bits (float) for green, 16 bits (float) for blue, 16 bits 
-		 * (float) for alpha. 
-		 */
-        PF_FLOAT16_RGBA			BS_SCRIPT_EXPORT(n:Float16_RGBA) = 24,
-		/** 32-bit pixel format, 32 bits (float) for red. */
-        PF_FLOAT32_R			BS_SCRIPT_EXPORT(n:Float32_R) = 25,
-		/** 64-bit, 2-channel floating point pixel format, 32-bit red, 32-bit green. */
-		PF_FLOAT32_RG			BS_SCRIPT_EXPORT(n:Float32_RG) = 26,
-        /** 96-bit pixel format, 32 bits (float) for red, 32 bits (float) for green, 32 bits (float) for blue. */
-        PF_FLOAT32_RGB			BS_SCRIPT_EXPORT(n:Float32_RGB) = 27,
-        /** 
-		 * 128-bit pixel format, 32 bits (float) for red, 32 bits (float) for green, 32 bits (float) for blue, 32 bits 
-		 * (float) for alpha. 
-		 */
-        PF_FLOAT32_RGBA			BS_SCRIPT_EXPORT(n:Float32_RGBA) = 28,
-		/** Depth stencil format, 32bit depth, 8bit stencil + 24 unused. */
+		/** 16-bit 1-channel pixel format, signed float. */
+		PF_R16F					BS_SCRIPT_EXPORT(n:R16F) = 21,
+		/** 16-bit 2-channel pixel format, signed float. */
+		PF_RG16F				BS_SCRIPT_EXPORT(n:RG16F) = 22,
+		/** 16-bit 4-channel pixel format, signed float. */
+		PF_RGBA16F				BS_SCRIPT_EXPORT(n:RGBA16F) = 24,
+		/** 32-bit 1-channel pixel format, signed float. */
+		PF_R32F					BS_SCRIPT_EXPORT(n:R32F) = 25,
+		/** 32-bit 2-channel pixel format, signed float. */
+		PF_RG32F				BS_SCRIPT_EXPORT(n:RG32F) = 26,
+		/** 32-bit 3-channel pixel format, signed float. */
+		PF_RGB32F				BS_SCRIPT_EXPORT(n:RGB32F) = 27,
+		/** 32-bit 4-channel pixel format, signed float. */
+		PF_RGBA32F				BS_SCRIPT_EXPORT(n:RGBA32F) = 28,
+		/** Depth stencil format, 32bit depth, 8bit stencil + 24 unused. Depth stored as signed float. */
 		PF_D32_S8X24			BS_SCRIPT_EXPORT(n:D32_S8X24) = 29,
-		/** Depth stencil fomrat, 24bit depth + 8bit stencil. */
+		/** Depth stencil fomrat, 24bit depth + 8bit stencil. Depth stored as unsigned normalized. */
 		PF_D24S8				BS_SCRIPT_EXPORT(n:D24S8) = 30,
-		/** Depth format, 32bits. */
+		/** Depth format, 32bits. Signed float. */
 		PF_D32					BS_SCRIPT_EXPORT(n:D32) = 31,
-		/** Depth format, 16bits. */
+		/** Depth format, 16bits. Unsigned normalized. */
 		PF_D16					BS_SCRIPT_EXPORT(n:D16) = 32,
+		/** Packed unsigned float format, 11 bits for red, 11 bits for green, 10 bits for blue. */
+		PF_RG11B10F				BS_SCRIPT_EXPORT(ex:true) = 33,
 		/** 
-		 * 32-bit float format, 11 bits (float) for red, 11 bits (float) for green, 10 bits (float) for blue. Framebuffer 
-		 * only format, not for CPU use. 
-		 */
-		PF_FLOAT_R11G11B10		BS_SCRIPT_EXPORT(ex:true) = 33,
-		/** 
-		 * 32-bit unsigned normalized format, 10 bits (float) for red, 10 bits (float) for green, 10 bits (float) for blue, 
-		 * and two bits for alpha. Framebuffer only format, not for CPU use.
-		 */
-		PF_UNORM_R10G10B10A2	BS_SCRIPT_EXPORT(ex:true) = 34,
+		 * Packed unsigned normalized format, 10 bits for red, 10 bits for green, 10 bits for blue, and two bits for alpha.
+		 */
+		PF_RGB10A2				BS_SCRIPT_EXPORT(ex:true) = 34,
+		/** 8-bit 1-channel pixel format, signed integer. */
+		PF_R8I					BS_SCRIPT_EXPORT(n:R8I) = 35,
+		/** 8-bit 2-channel pixel format, signed integer. */
+		PF_RG8I					BS_SCRIPT_EXPORT(n:RG8I) = 36,
+		/** 8-bit 4-channel pixel format, signed integer. */
+		PF_RGBA8I				BS_SCRIPT_EXPORT(n:RGBA8I) = 37,
+		/** 8-bit 1-channel pixel format, unsigned integer. */
+		PF_R8U					BS_SCRIPT_EXPORT(n:R8U) = 38,
+		/** 8-bit 2-channel pixel format, unsigned integer. */
+		PF_RG8U					BS_SCRIPT_EXPORT(n:RG8U) = 39,
+		/** 8-bit 4-channel pixel format, unsigned integer. */
+		PF_RGBA8U				BS_SCRIPT_EXPORT(n:RGBA8U) = 40,
+		/** 8-bit 1-channel pixel format, signed normalized. */
+		PF_R8S					BS_SCRIPT_EXPORT(n:R8S) = 41,
+		/** 8-bit 2-channel pixel format, signed normalized. */
+		PF_RG8S					BS_SCRIPT_EXPORT(n:RG8S) = 42,
+		/** 8-bit 4-channel pixel format, signed normalized. */
+		PF_RGBA8S				BS_SCRIPT_EXPORT(n:RGBA8S) = 43,
+		/** 16-bit 1-channel pixel format, signed integer. */
+		PF_R16I					BS_SCRIPT_EXPORT(n:R16I) = 44,
+		/** 16-bit 2-channel pixel format, signed integer. */
+		PF_RG16I				BS_SCRIPT_EXPORT(n:RG16I) = 45,
+		/** 16-bit 4-channel pixel format, signed integer. */
+		PF_RGBA16I				BS_SCRIPT_EXPORT(n:RGBA16I) = 46,
+		/** 16-bit 1-channel pixel format, unsigned integer. */
+		PF_R16U					BS_SCRIPT_EXPORT(n:R16U) = 47,
+		/** 16-bit 2-channel pixel format, unsigned integer. */
+		PF_RG16U				BS_SCRIPT_EXPORT(n:RG16U) = 48,
+		/** 16-bit 4-channel pixel format, unsigned integer. */
+		PF_RGBA16U				BS_SCRIPT_EXPORT(n:RGBA16U) = 49,
+		/** 32-bit 1-channel pixel format, signed integer. */
+		PF_R32I					BS_SCRIPT_EXPORT(n:R32I) = 50,
+		/** 32-bit 2-channel pixel format, signed integer. */
+		PF_RG32I				BS_SCRIPT_EXPORT(n:RG32I) = 51,
+		/** 32-bit 3-channel pixel format, signed integer. */
+		PF_RGB32I				BS_SCRIPT_EXPORT(n:RGB32I) = 52,
+		/** 32-bit 4-channel pixel format, signed integer. */
+		PF_RGBA32I				BS_SCRIPT_EXPORT(n:RGBA32I) = 53,
+		/** 32-bit 1-channel pixel format, unsigned integer. */
+		PF_R32U					BS_SCRIPT_EXPORT(n:R32U) = 54,
+		/** 32-bit 2-channel pixel format, unsigned integer. */
+		PF_RG32U				BS_SCRIPT_EXPORT(n:RG32U) = 55,
+		/** 32-bit 3-channel pixel format, unsigned integer. */
+		PF_RGB32U				BS_SCRIPT_EXPORT(n:RGB32U) = 56,
+		/** 32-bit 4-channel pixel format, unsigned integer. */
+		PF_RGBA32U				BS_SCRIPT_EXPORT(n:RGBA32U) = 57,
+		/** 16-bit 1-channel pixel format, signed normalized. */
+		PF_R16S					BS_SCRIPT_EXPORT(n:R16S) = 58,
+		/** 16-bit 2-channel pixel format, signed normalized. */
+		PF_RG16S				BS_SCRIPT_EXPORT(n:RG16S) = 59,
+		/** 16-bit 4-channel pixel format, signed normalized. */
+		PF_RGBA16S				BS_SCRIPT_EXPORT(n:RGBA16S) = 60,
+		/** 16-bit 1-channel pixel format, unsigned normalized. */
+		PF_R16					BS_SCRIPT_EXPORT(n:R16) = 61,
+		/** 16-bit 2-channel pixel format, unsigned normalized. */
+		PF_RG16					BS_SCRIPT_EXPORT(n:RG16) = 62,
+		/** 16-bit 3-channel pixel format, unsigned normalized. */
+		PF_RGB16				BS_SCRIPT_EXPORT(n:RGB16) = 63,
+		/** 16-bit 4-channel pixel format, unsigned normalized. */
+		PF_RGBA16				BS_SCRIPT_EXPORT(n:RGBA16) = 64,
 		/** Number of pixel formats currently defined. */
-        PF_COUNT				BS_SCRIPT_EXPORT(ex:true) = 35
-    };
+		PF_COUNT				BS_SCRIPT_EXPORT(ex:true)
+	};
 
 	/**	Flags defining some properties of pixel formats. */
-    enum PixelFormatFlags {
-        /** This format has an alpha channel. */
-        PFF_HASALPHA = 0x00000001,      
-        /**
+	enum PixelFormatFlags {
+		/** This format has an alpha channel. */
+		PFF_HASALPHA = 0x1,
+		/**
 		 * This format is compressed. This invalidates the values in elemBytes, elemBits and the bit counts as these might
 		 * not be fixed in a compressed format.
 		 */
-        PFF_COMPRESSED = 0x00000002,
-        /** This is a floating point format. */
-        PFF_FLOAT = 0x00000004,         
-        /** This is a depth format (for depth textures). */
-        PFF_DEPTH = 0x00000008,
-        /** 
-		 * Format is in native endian. Generally true for the 16, 24 and 32 bits formats which can be represented as 
-		 * machine integers.
-		 */
-        PFF_NATIVEENDIAN = 0x00000010
-    };
-    
+		PFF_COMPRESSED = 0x2,
+		/** This is a floating point format. */
+		PFF_FLOAT = 0x4,
+		/** This is a depth format (for depth textures). */
+		PFF_DEPTH = 0x8,
+		/** This format stores data internally as integers. */
+		PFF_INTEGER = 0x10,
+		/** Format contains signed data. Absence of this flag implies unsigned data. */
+		PFF_SIGNED = 0x20,
+		/** Format contains normalized data. This will be [0, 1] for unsigned, and [-1,1] for signed formats. */
+		PFF_NORMALIZED = 0x40
+	};
+
 	/**	Types used for individual components of a pixel. */
-    enum PixelComponentType
-    {
-        PCT_BYTE = 0,    /**< Byte per component */
-        PCT_SHORT = 1,   /**< Short per component */
-        PCT_FLOAT16 = 2, /**< 16 bit float per component */
-        PCT_FLOAT32 = 3, /**< 32 bit float per component */
-		PCT_PACKED_R11G11B10 = 4, /**< 11 bits for first two components, 10 for third component. */
-		PCT_PACKED_R10G10B10A2 = 5, /**< 10 bits for first three components, 2 bits for last component */
-        PCT_COUNT = 4    /**< Number of pixel types */
-    };
+	enum PixelComponentType
+	{
+		PCT_BYTE = 0, /**< 8-bit integer per component */
+		PCT_SHORT = 1, /**< 16-bit integer per component. */
+		PCT_INT = 2, /**< 32-bit integer per component. */
+		PCT_FLOAT16 = 3, /**< 16 bit float per component */
+		PCT_FLOAT32 = 4, /**< 32 bit float per component */
+		PCT_PACKED_R11G11B10 = 5, /**< 11 bits for first two components, 10 for third component. */
+		PCT_PACKED_R10G10B10A2 = 6, /**< 10 bits for first three components, 2 bits for last component */
+		PCT_COUNT    /**< Number of pixel types */
+	};
 
 	/** Determines how are texture pixels filtered during sampling. */
 	enum TextureFilter
@@ -155,9 +205,9 @@ namespace bs
 	 *
 	 * @see		GpuResourceData
 	 */
-    class BS_CORE_EXPORT BS_SCRIPT_EXPORT() PixelData : public GpuResourceData
+	class BS_CORE_EXPORT BS_SCRIPT_EXPORT() PixelData : public GpuResourceData
 	{
-    public:
+	public:
 		PixelData();
 		~PixelData() {}
 
@@ -200,19 +250,19 @@ namespace bs
 		 * Sets the pitch (in pixels) that determines offset between depth slices of the pixel buffer. Call this before 
 		 * allocating the buffer.
 		 */
-        void setSlicePitch(UINT32 slicePitch) { mSlicePitch = slicePitch; }
+		void setSlicePitch(UINT32 slicePitch) { mSlicePitch = slicePitch; }
 
 		/**
 		 * Returns the number of extra pixels in a row (non-zero only if rows are not consecutive (row pitch is larger 
 		 * than width)).
 		 */
-        UINT32 getRowSkip() const { return mRowPitch - getWidth(); }
+		UINT32 getRowSkip() const { return mRowPitch - getWidth(); }
 
 		/**
 		 * Returns the number of extra pixels in a depth slice (non-zero only if slices aren't consecutive (slice pitch is 
 		 * larger than width*height).
 		 */
-        UINT32 getSliceSkip() const { return mSlicePitch - (getHeight() * mRowPitch); }
+		UINT32 getSliceSkip() const { return mSlicePitch - (getHeight() * mRowPitch); }
 
 		/** Returns the pixel format used by the internal buffer for storing the pixels. */
 		BS_SCRIPT_EXPORT(n:Format,pr:getter)
@@ -271,24 +321,24 @@ namespace bs
 		 * Return whether this buffer is laid out consecutive in memory (meaning the pitches are equal to the dimensions). 
 		 */
 		BS_SCRIPT_EXPORT(n:RawIsConsecutive,pr:getter)
-        bool isConsecutive() const 
+		bool isConsecutive() const
 		{ 
 			return mRowPitch == getWidth() && mSlicePitch == getWidth()*getHeight(); 
 		}
 
 		/** Return the size (in bytes) this image would take if it was laid out consecutive in memory. */
-      	UINT32 getConsecutiveSize() const;
+		UINT32 getConsecutiveSize() const;
 
 		/**	Return the size (in bytes) of the buffer this image requires. */
 		BS_SCRIPT_EXPORT(n:RawSize,pr:getter)
-      	UINT32 getSize() const;
+		UINT32 getSize() const;
 
 		/**
 		 * Returns pixel data containing a sub-volume of this object. Returned data will not have its own buffer, but will
 		 * instead point to this one. It is up to the caller to ensure this object outlives any sub-volume objects.
 		 */
-      	PixelData getSubVolume(const PixelVolume& volume) const;
-        
+		PixelData getSubVolume(const PixelVolume& volume) const;
+
 		/** 
 		 * Samples a color at the specified coordinates using a specific filter.
 		 * 
@@ -303,7 +353,7 @@ namespace bs
 		Color getColorAt(UINT32 x, UINT32 y, UINT32 z = 0) const;
 
 		/**	Sets the pixel color at the specified coordinates. */
-        void setColorAt(const Color& color, UINT32 x, UINT32 y, UINT32 z = 0);
+		void setColorAt(const Color& color, UINT32 x, UINT32 y, UINT32 z = 0);
 
 		/**
 		 * Converts all the internal data into an array of colors. Array is mapped as such: 
@@ -362,9 +412,9 @@ namespace bs
 
 	private:
 		PixelVolume mExtents;
-        PixelFormat mFormat;
-        UINT32 mRowPitch;
-        UINT32 mSlicePitch;
+		PixelFormat mFormat;
+		UINT32 mRowPitch;
+		UINT32 mSlicePitch;
 
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/
@@ -373,7 +423,7 @@ namespace bs
 		friend class PixelDataRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		RTTITypeBase* getRTTI() const override;
-    };
+	};
 
 	/** @} */
 }

+ 0 - 3
Source/BansheeCore/Include/BsPixelUtil.h

@@ -126,9 +126,6 @@ namespace bs
 		/**	Checks is the provided pixel format a depth/stencil buffer format. */
 		static bool isDepth(PixelFormat format);
 
-		/**	Checks is the provided format in native endian format. */
-		static bool isNativeEndian(PixelFormat format);
-		
 		/** 
 		 * Checks is the provided format valid for the texture type and usage. 
 		 * 

+ 0 - 55
Source/BansheeCore/Include/BsPostProcessSettings.h

@@ -1,55 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsIReflectable.h"
-
-namespace bs
-{
-	/** @addtogroup Renderer
-	 *  @{
-	 */
-	
-	/** Base class whose implementations contain settings that control post-process operations during rendering. */
-	struct BS_CORE_EXPORT PostProcessSettings : public IReflectable
-	{
-		PostProcessSettings() { }
-		virtual ~PostProcessSettings() { }
-
-		/** @name Internal
-		 *  @{
-		 */
-
-		/** 
-		 * Populates the provided buffer with data that can be used for syncing between two versions of this object.
-		 * Apply the retrieved buffer via _setSyncData().
-		 *
-		 * @param[in]		buffer		Pre-allocated buffer to store the sync data in. Set to null to calculate the size
-		 *								of the required buffer.
-		 * @param[in, out]	size		Size of the provided allocated buffer. Or if the buffer is null, this parameter will
-		 *								contain the required buffer size when the method executes.
-		 */
-		virtual void _getSyncData(UINT8* buffer, UINT32& size) = 0;
-
-		/** 
-		 * Updates the stored data from the provided buffer, allowing changes to be transfered between two versions of this
-		 * object. Buffer must be retrieved from _getSyncData(). 
-		 *
-		 * @param[in]		buffer		Buffer containing the dirty data.
-		 * @param[in, out]	size		Size of the provided buffer.
-		 */
-		virtual void _setSyncData(UINT8* buffer, UINT32 size) = 0;
-
-		/** @} */
-		/************************************************************************/
-		/* 								RTTI		                     		*/
-		/************************************************************************/
-	public:
-		friend class PostProcessSettingsRTTI;
-		static RTTITypeBase* getRTTIStatic();
-		RTTITypeBase* getRTTI() const override;
-	};
-
-	/** @} */
-}

+ 2 - 2
Source/BansheeCore/Include/BsProfilerGPU.h

@@ -9,8 +9,8 @@
 namespace bs
 {
 	/** @addtogroup Profiling
-	*  @{
-	*/
+	 *  @{
+	 */
 
 	/** Contains various profiler statistics about a single GPU profiling sample. */
 	struct GPUProfileSample

+ 52 - 31
Source/BansheeCore/Include/BsReflectionProbe.h

@@ -11,7 +11,7 @@
 
 namespace bs
 {
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 
@@ -135,48 +135,52 @@ namespace bs
 		Sphere mBounds; /**< Sphere that bounds the light area of influence. */
 	};
 
-	/** Templated base class for both core and sim thread implementations of a reflection probe. */
-	template<bool Core>
-	class BS_CORE_EXPORT TReflectionProbe : public ReflectionProbeBase
+	/** @} */
+	/** @addtogroup Renderer-Internal
+	 *  @{
+	 */
+
+	namespace ct 
 	{
-		typedef typename TTextureType<Core>::Type TextureType;
+		class RendererTask;
+		class ReflectionProbe; 
+	}
 
+	/**
+	 * Specifies a location at which a pre-computed texture containing scene radiance will be generated. This texture will
+	 * then be used by the renderer to provide specular reflections.
+	 */
+	class BS_CORE_EXPORT ReflectionProbe : public IReflectable, public CoreObject, public ReflectionProbeBase
+	{
 	public:
-		TReflectionProbe();
-		TReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents);
-		virtual ~TReflectionProbe() { }
+		~ReflectionProbe();
 
 		/** 
 		 * Allows you assign a custom texture to use as a reflection map. This will disable automatic generation of
 		 * reflections. To re-enable auto-generation call this with a null parameter.
 		 */
-		void setCustomTexture(const TextureType& texture) { mCustomTexture = texture; _markCoreDirty(); }
+		void setCustomTexture(const HTexture& texture) { mCustomTexture = texture; filter(); }
 
 		/** Gets the custom texture assigned through setCustomTexture(). */
-		TextureType getCustomTexture() const { return mCustomTexture; }
-
-		/** Forces the reflection probe to regenerate its texture. Call is ignored if the probe uses a custom texture. */
-		void generate();
-
-	protected:
-		TextureType mCustomTexture;
-	};
+		HTexture getCustomTexture() const { return mCustomTexture; }
 
+		/** 
+		 * Returns a pre-filtered texture that is generated either from the provided custom texture, or from scene capture.
+		 */
+		SPtr<Texture> getFilteredTexture() const { return mFilteredTexture; }
 
-	/** @} */
-	/** @addtogroup Renderer-Engine-Internal
-	 *  @{
-	 */
+		/** 
+		 * Captures the scene at the current location and generates a filtered reflection cubemap. No action is taken
+		 * if a custom texture is set.
+		 */
+		void capture();
 
-	namespace ct { class ReflectionProbe; }
+		/** 
+		 * Filters the custom texture, making it usable for rendering. Called automatically when custom texture changes. If 
+		 * no custom texture is set, no action is taken.
+		 */
+		void filter();
 
-	/**
-	 * Specifies a location at which a pre-computed texture containing scene radiance will be generated. This texture will
-	 * then be used by the renderer to provide specular reflections.
-	 */
-	class BS_CORE_EXPORT ReflectionProbe : public IReflectable, public CoreObject, public TReflectionProbe<false>
-	{
-	public:
 		/**	Retrieves an implementation of the reflection probe usable only from the core thread. */
 		SPtr<ct::ReflectionProbe> getCore() const;
 
@@ -222,10 +226,20 @@ namespace bs
 		/** @copydoc CoreObject::syncToCore */
 		CoreSyncData syncToCore(FrameAlloc* allocator) override;
 
+		/** 
+		 * Captures the scene color at current probe location and generates a filtered map. If a custom texture is set then
+		 * it will be filtered, instead of capturing scene color.
+		 */
+		void captureAndFilter();
+
 		/**	Creates a light with without initializing it. Used for serialization. */
 		static SPtr<ReflectionProbe> createEmpty();
 
 		UINT32 mLastUpdateHash;
+		HTexture mCustomTexture;
+
+		SPtr<ct::RendererTask> mRendererTask;
+		SPtr<Texture> mFilteredTexture;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -242,7 +256,7 @@ namespace bs
 	namespace ct
 	{
 	/** Core thread usable version of a bs::ReflectionProbe */
-	class BS_CORE_EXPORT ReflectionProbe : public CoreObject, public TReflectionProbe<true>
+	class BS_CORE_EXPORT ReflectionProbe : public CoreObject, public ReflectionProbeBase
 	{
 	public:
 		~ReflectionProbe();
@@ -252,10 +266,16 @@ namespace bs
 
 		/**	Retrieves an ID that can be used for uniquely identifying this object by the renderer. */
 		UINT32 getRendererId() const { return mRendererId; }
+
+		/** 
+		 * Returns a pre-filtered texture that is generated either from the provided custom texture, or from scene capture.
+		 */
+		SPtr<Texture> getFilteredTexture() const { return mFilteredTexture; }
 	protected:
 		friend class bs::ReflectionProbe;
 
-		ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents);
+		ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents, 
+			const SPtr<Texture>& filteredTexture);
 
 		/** @copydoc CoreObject::initialize */
 		void initialize() override;
@@ -264,6 +284,7 @@ namespace bs
 		void syncToCore(const CoreSyncData& data) override;
 
 		UINT32 mRendererId;
+		SPtr<Texture> mFilteredTexture;
 	};
 	}
 

+ 12 - 1
Source/BansheeCore/Include/BsReflectionProbeRTTI.h

@@ -5,6 +5,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
 #include "BsReflectionProbe.h"
+#include "BsRenderer.h"
 
 namespace bs
 {
@@ -26,12 +27,22 @@ namespace bs
 			BS_RTTI_MEMBER_PLAIN(mTransitionDistance, 6)
 			BS_RTTI_MEMBER_REFL(mCustomTexture, 7)
 			BS_RTTI_MEMBER_PLAIN(mUUID, 8)
+			BS_RTTI_MEMBER_REFLPTR(mFilteredTexture, 9)
 		BS_END_RTTI_MEMBERS
 	public:
 		ReflectionProbeRTTI()
 			:mInitMembers(this)
 		{ }
 
+		void onSerializationStarted(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
+		{
+			ReflectionProbe* probe = static_cast<ReflectionProbe*>(obj);
+
+			// Force the renderer task to complete, so the filtered texture is up to date
+			if (probe->mRendererTask != nullptr)
+				probe->mRendererTask->wait();
+		}
+
 		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
 		{
 			// Note: Since this is a CoreObject I should call initialize() right after deserialization,
@@ -59,4 +70,4 @@ namespace bs
 
 	/** @} */
 	/** @endcond */
-}
+}

+ 64 - 18
Source/BansheeEngine/Include/BsStandardPostProcessSettings.h → Source/BansheeCore/Include/BsRenderSettings.h

@@ -2,8 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
-#include "BsPrerequisites.h"
-#include "BsPostProcessSettings.h"
+#include "BsCorePrerequisites.h"
+#include "BsIReflectable.h"
 #include "BsVector3.h"
 
 namespace bs
@@ -13,7 +13,7 @@ namespace bs
 	 */
 
 	/** Settings that control automatic exposure (eye adaptation) post-process. */
-	struct BS_EXPORT AutoExposureSettings : public IReflectable
+	struct BS_CORE_EXPORT AutoExposureSettings : public IReflectable
 	{
 		AutoExposureSettings();
 
@@ -85,7 +85,7 @@ namespace bs
 	};
 
 	/** Settings that control tonemap post-process. */
-	struct BS_EXPORT TonemappingSettings : public IReflectable
+	struct BS_CORE_EXPORT TonemappingSettings : public IReflectable
 	{
 		TonemappingSettings();
 
@@ -132,7 +132,7 @@ namespace bs
 	};
 
 	/** Settings that control white balance post-process. */
-	struct BS_EXPORT WhiteBalanceSettings : public IReflectable
+	struct BS_CORE_EXPORT WhiteBalanceSettings : public IReflectable
 	{
 		WhiteBalanceSettings();
 
@@ -162,7 +162,7 @@ namespace bs
 	};
 
 	/** Settings that control color grading post-process. */
-	struct BS_EXPORT ColorGradingSettings : public IReflectable
+	struct BS_CORE_EXPORT ColorGradingSettings : public IReflectable
 	{
 		ColorGradingSettings();
 
@@ -200,7 +200,7 @@ namespace bs
 	};
 
 	/** Settings that control screen space ambient occlusion. */
-	struct BS_EXPORT AmbientOcclusionSettings : public IReflectable
+	struct BS_CORE_EXPORT AmbientOcclusionSettings : public IReflectable
 	{
 		AmbientOcclusionSettings();
 
@@ -263,7 +263,7 @@ namespace bs
 	};
 
 	/** Settings that control the depth-of-field effect. */
-	struct BS_EXPORT DepthOfFieldSettings : public IReflectable
+	struct BS_CORE_EXPORT DepthOfFieldSettings : public IReflectable
 	{
 		DepthOfFieldSettings();
 
@@ -322,7 +322,7 @@ namespace bs
 	 * for rougher (more glossy rather than mirror-like) surfaces. Those surfaces require a higher number of samples to
 	 * achieve the glossy look, so we instead fall back to refl. probes which are pre-filtered and can be quickly sampled.
 	 */
-	struct BS_EXPORT ScreenSpaceReflectionsSettings : public IReflectable
+	struct BS_CORE_EXPORT ScreenSpaceReflectionsSettings : public IReflectable
 	{
 		ScreenSpaceReflectionsSettings();
 
@@ -353,11 +353,12 @@ namespace bs
 		static RTTITypeBase* getRTTIStatic();
 		RTTITypeBase* getRTTI() const override;
 	};
-
-	/** Settings that control the post-process operations. */
-	struct BS_EXPORT StandardPostProcessSettings : public PostProcessSettings
+	
+	/** Settings that control rendering for a specific camera (view). */
+	struct BS_CORE_EXPORT RenderSettings : public IReflectable
 	{
-		StandardPostProcessSettings();
+		RenderSettings();
+		virtual ~RenderSettings() { }
 
 		/**
 		 * Determines should automatic exposure be applied to the HDR image. When turned on the average scene brightness
@@ -430,17 +431,62 @@ namespace bs
 		 */
 		float gamma;
 
-		/** @copydoc PostProcessSettings::_getSyncData */
-		void _getSyncData(UINT8* buffer, UINT32& size) override;
+		/** 
+		 * High dynamic range allows light intensity to be more correctly recorded when rendering by allowing for a larger
+		 * range of values. The stored light is then converted into visible color range using exposure and a tone mapping 
+		 * operator.
+		 */
+		bool enableHDR;
+
+		/** 
+		 * Determines if scene objects will be lit by lights. If disabled everything will be rendered using their albedo
+		 * texture with no lighting applied.
+		 */
+		bool enableLighting;
+
+		/** Determines if shadows cast by lights should be rendered. Only relevant if lighting is turned on. */
+		bool enableShadows;
+
+		/** Determines if indirect lighting (e.g. from light probes or the sky) is rendered. */
+		bool enableIndirectLighting;
+
+		/** 
+		 * Signals the renderer to only render overlays (like GUI), and not scene objects. Such rendering doesn't require
+		 * depth buffer or multi-sampled render targets and will not render any scene objects. This can improve performance
+		 * and memory usage for overlay-only views. 
+		 */
+		bool overlayOnly;
+
+		/** @name Internal
+		 *  @{
+		 */
+
+		/** 
+		 * Populates the provided buffer with data that can be used for syncing between two versions of this object.
+		 * Apply the retrieved buffer via _setSyncData().
+		 *
+		 * @param[in]		buffer		Pre-allocated buffer to store the sync data in. Set to null to calculate the size
+		 *								of the required buffer.
+		 * @param[in, out]	size		Size of the provided allocated buffer. Or if the buffer is null, this parameter will
+		 *								contain the required buffer size when the method executes.
+		 */
+		void _getSyncData(UINT8* buffer, UINT32& size);
 
-		/** @copydoc PostProcessSettings::_setSyncData */
-		void _setSyncData(UINT8* buffer, UINT32 size) override;
+		/** 
+		 * Updates the stored data from the provided buffer, allowing changes to be transfered between two versions of this
+		 * object. Buffer must be retrieved from _getSyncData(). 
+		 *
+		 * @param[in]		buffer		Buffer containing the dirty data.
+		 * @param[in, out]	size		Size of the provided buffer.
+		 */
+		void _setSyncData(UINT8* buffer, UINT32 size);
 
+		/** @} */
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
 	public:
-		friend class StandardPostProcessSettingsRTTI;
+		friend class RenderSettingsRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		RTTITypeBase* getRTTI() const override;
 	};

+ 19 - 14
Source/BansheeEngine/Include/BsStandardPostProcessSettingsRTTI.h → Source/BansheeCore/Include/BsRenderSettingsRTTI.h

@@ -2,9 +2,9 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
-#include "BsPrerequisites.h"
+#include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
-#include "BsStandardPostProcessSettings.h"
+#include "BsRenderSettings.h"
 
 namespace bs
 {
@@ -13,7 +13,7 @@ namespace bs
 	 *  @{
 	 */
 
-	class BS_EXPORT AutoExposureSettingsRTTI : public RTTIType <AutoExposureSettings, IReflectable, AutoExposureSettingsRTTI>
+	class BS_CORE_EXPORT AutoExposureSettingsRTTI : public RTTIType <AutoExposureSettings, IReflectable, AutoExposureSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -49,7 +49,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT TonemappingSettingsRTTI : public RTTIType <TonemappingSettings, IReflectable, TonemappingSettingsRTTI>
+	class BS_CORE_EXPORT TonemappingSettingsRTTI : public RTTIType <TonemappingSettings, IReflectable, TonemappingSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -84,7 +84,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT WhiteBalanceSettingsRTTI : public RTTIType <WhiteBalanceSettings, IReflectable, WhiteBalanceSettingsRTTI>
+	class BS_CORE_EXPORT WhiteBalanceSettingsRTTI : public RTTIType <WhiteBalanceSettings, IReflectable, WhiteBalanceSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -114,7 +114,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT ColorGradingSettingsRTTI : public RTTIType <ColorGradingSettings, IReflectable, ColorGradingSettingsRTTI>
+	class BS_CORE_EXPORT ColorGradingSettingsRTTI : public RTTIType <ColorGradingSettings, IReflectable, ColorGradingSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -146,7 +146,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT DepthOfFieldSettingsRTTI : public RTTIType <DepthOfFieldSettings, IReflectable, DepthOfFieldSettingsRTTI>
+	class BS_CORE_EXPORT DepthOfFieldSettingsRTTI : public RTTIType <DepthOfFieldSettings, IReflectable, DepthOfFieldSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -181,7 +181,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT AmbientOcclusionSettingsRTTI : public RTTIType <AmbientOcclusionSettings, IReflectable, AmbientOcclusionSettingsRTTI>
+	class BS_CORE_EXPORT AmbientOcclusionSettingsRTTI : public RTTIType <AmbientOcclusionSettings, IReflectable, AmbientOcclusionSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -217,7 +217,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT ScreenSpaceReflectionsSettingsRTTI : public RTTIType <ScreenSpaceReflectionsSettings, IReflectable, ScreenSpaceReflectionsSettingsRTTI>
+	class BS_CORE_EXPORT ScreenSpaceReflectionsSettingsRTTI : public RTTIType <ScreenSpaceReflectionsSettings, IReflectable, ScreenSpaceReflectionsSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -249,7 +249,7 @@ namespace bs
 		}
 	};
 
-	class BS_EXPORT StandardPostProcessSettingsRTTI : public RTTIType <StandardPostProcessSettings, PostProcessSettings, StandardPostProcessSettingsRTTI>
+	class BS_CORE_EXPORT RenderSettingsRTTI : public RTTIType <RenderSettings, IReflectable, RenderSettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
@@ -265,27 +265,32 @@ namespace bs
 			BS_RTTI_MEMBER_PLAIN(enableFXAA, 9)
 			BS_RTTI_MEMBER_REFL(ambientOcclusion, 10)
 			BS_RTTI_MEMBER_REFL(screenSpaceReflections, 11)
+			BS_RTTI_MEMBER_PLAIN(enableHDR, 12)
+			BS_RTTI_MEMBER_PLAIN(enableLighting, 13)
+			BS_RTTI_MEMBER_PLAIN(enableShadows, 14)
+			BS_RTTI_MEMBER_PLAIN(overlayOnly, 15)
+			BS_RTTI_MEMBER_PLAIN(enableIndirectLighting, 16)
 		BS_END_RTTI_MEMBERS
 			
 	public:
-		StandardPostProcessSettingsRTTI()
+		RenderSettingsRTTI()
 			:mInitMembers(this)
 		{ }
 
 		const String& getRTTIName() override
 		{
-			static String name = "StandardPostProcessSettings";
+			static String name = "RenderSettings";
 			return name;
 		}
 
 		UINT32 getRTTIId() override
 		{
-			return TID_StandardPostProcessSettings;
+			return TID_RenderSettings;
 		}
 
 		SPtr<IReflectable> newRTTIObject() override
 		{
-			return bs_shared_ptr_new<StandardPostProcessSettings>();
+			return bs_shared_ptr_new<RenderSettings>();
 		}
 	};
 

+ 1 - 1
Source/BansheeCore/Include/BsRenderable.h

@@ -183,7 +183,7 @@ namespace bs
 
 	/** @} */
 
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 

+ 102 - 10
Source/BansheeCore/Include/BsRenderer.h

@@ -10,10 +10,13 @@ namespace bs
 { 
 	class RendererExtension;
 	class LightProbeVolume;
-	struct PostProcessSettings;
+	struct RenderSettings;
 
 	namespace ct
 	{
+	class RendererTask;
+	class LightProbeVolume;
+
 	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
@@ -46,6 +49,9 @@ namespace bs
 		/** Initializes the renderer. Must be called before using the renderer. */
 		virtual void initialize() { }
 
+		/** Called every frame. Triggers render task callbacks. */
+		void update();
+
 		/**	Cleans up the renderer. Must be called before the renderer is deleted. */
 		virtual void destroy() { }
 
@@ -133,7 +139,7 @@ namespace bs
 		 *
 		 * @note	Core thread.
 		 */
-		virtual void notifyReflectionProbeUpdated(ReflectionProbe* probe) { }
+		virtual void notifyReflectionProbeUpdated(ReflectionProbe* probe, bool texture) { }
 
 		/**
 		 * Called whenever a reflection probe is destroyed.
@@ -171,18 +177,22 @@ namespace bs
 		virtual void notifySkyboxAdded(Skybox* skybox) { }
 
 		/**
-		 * Called whenever the texture assigned to a skybox is changed.
+		 * Called whenever a skybox is destroyed.
 		 *
 		 * @note	Core thread.
 		 */
-		virtual void notifySkyboxTextureChanged(Skybox* skybox) { }
+		virtual void notifySkyboxRemoved(Skybox* skybox) { }
 
-		/**
-		 * Called whenever a skybox is destroyed.
+		/** 
+		 * Captures the scene at the specified location into a cubemap. 
+		 * 
+		 * @param[in]	cubemap		Cubemap to store the results in.
+		 * @param[in]	position	Position to capture the scene at.
+		 * @param[in]	hdr			If true scene will be captured in a format that supports high dynamic range.
 		 *
 		 * @note	Core thread.
 		 */
-		virtual void notifySkyboxRemoved(Skybox* skybox) { }
+		virtual void captureSceneCubeMap(const SPtr<Texture>& cubemap, const Vector3& position, bool hdr) = 0;
 
 		/**
 		 * Creates a new empty renderer mesh data.
@@ -218,16 +228,22 @@ namespace bs
 		 */
 		void removePlugin(RendererExtension* plugin) { mCallbacks.erase(plugin); }
 
+		/**
+		 * Registers a new task for execution on the core thread.
+		 * 
+		 * @note	Thread safe.
+		 */
+		void addTask(const SPtr<RendererTask>& task);
+
 		/**	Sets options used for controlling the rendering. */
 		virtual void setOptions(const SPtr<RendererOptions>& options) { }
 
 		/**	Returns current set of options used for controlling the rendering. */
 		virtual SPtr<RendererOptions> getOptions() const { return SPtr<RendererOptions>(); }
 
-		/** Creates post process settings that can be attached to a camera and processed by the active renderer. */
-		virtual SPtr<PostProcessSettings> createPostProcessSettings() const = 0;
-
 	protected:
+		friend class RendererTask;
+
 		/**	Contains information about a render callback. */
 		struct RenderCallbackData
 		{
@@ -235,14 +251,90 @@ namespace bs
 			std::function<void()> callback;
 		};
 
+		/**
+		 * Executes all renderer tasks queued for this frame.
+		 *
+		 * @param[in]	forceAll	If true, multi-frame tasks will be forced to execute fully within this call.
+		 * 
+		 * @note	Core thread.
+		 */
+		void processTasks(bool forceAll);
+
+		/**
+		 * Executes the provided renderer task.
+		 *
+		 * @param[in]	task		Task to execute.
+		 * @param[in]	forceAll	If true, multi-frame tasks will be forced to execute fully within this call.
+		 * 
+		 * @note	Core thread.
+		 */
+		void processTask(RendererTask& task, bool forceAll);
+
 		/** Callback to trigger when comparing the order in which renderer extensions are called. */
 		static bool compareCallback(const RendererExtension* a, const RendererExtension* b);
 
 		Set<RendererExtension*, std::function<bool(const RendererExtension*, const RendererExtension*)>> mCallbacks;
+
+		Vector<SPtr<RendererTask>> mQueuedTasks; // Sim & core thread
+		Vector<SPtr<RendererTask>> mUnresolvedTasks; // Sim thread
+		Vector<SPtr<RendererTask>> mRemainingUnresolvedTasks; // Sim thread
+		Vector<SPtr<RendererTask>> mRunningTasks; // Core thread
+		Vector<SPtr<RendererTask>> mRemainingTasks; // Core thread
+		Mutex mTaskMutex;
 	};
 
 	/**	Provides easy access to Renderer. */
 	SPtr<Renderer> BS_CORE_EXPORT gRenderer();
 
+	/**
+	 * Task that represents an asynchonous operation queued for execution on the core thread. All such tasks are executed
+	 * before main rendering happens, every frame.
+	 *
+	 * @note	Thread safe except where stated otherwise.
+	 */
+	class BS_CORE_EXPORT RendererTask
+	{
+		struct PrivatelyConstruct {};
+
+	public:
+		RendererTask(const PrivatelyConstruct& dummy, const String& name, std::function<bool()> taskWorker);
+
+		/**
+		 * Creates a new task. Task should be provided to Renderer in order for it to start.
+		 *
+		 * @param[in]	name		Name you can use to more easily identify the task.
+		 * @param[in]	taskWorker	Worker method that does all of the work in the task. Tasks can run over the course of
+		 *							multiple frames, in which case this method should return false (if there's more
+		 *							work to be done), or true (if the task has completed).
+		 */
+		static SPtr<RendererTask> create(const String& name, std::function<bool()> taskWorker);
+
+		/** Returns true if the task has completed. */
+		bool isComplete() const;
+
+		/**	Returns true if the task has been canceled. */
+		bool isCanceled() const;
+
+		/** Blocks the current thread until the task has completed. */
+		void wait();
+
+		/** Cancels the task and removes it from the Renderer's queue. */
+		void cancel();
+
+		/** 
+		 * Callback triggered on the sim thread, when the task completes. Is not triggered if the task is cancelled.
+		 *
+		 * @note	Sim thread only.
+		 */
+		Event<void()> onComplete;
+
+	private:
+		friend class Renderer;
+
+		String mName;
+		std::function<bool()> mTaskWorker;
+		std::atomic<UINT32> mState; /**< 0 - Inactive, 1 - In progress, 2 - Completed, 3 - Canceled */
+	};
+
 	/** @} */
 }}

+ 21 - 0
Source/BansheeCore/Include/BsSceneManager.h

@@ -8,6 +8,8 @@
 
 namespace bs
 {
+	class LightProbeVolume;
+
 	/** @addtogroup Scene-Internal
 	 *  @{
 	 */
@@ -63,6 +65,18 @@ namespace bs
 		HSceneObject sceneObject;
 	};
 
+	/**	Contains information about a light probe volume managed by the scene manager. */
+	struct SceneLightProbeVolumeData
+	{
+		SceneLightProbeVolumeData() { }
+		SceneLightProbeVolumeData(const SPtr<LightProbeVolume>& volume, const HSceneObject& sceneObject)
+			:volume(volume), sceneObject(sceneObject)
+		{ }
+
+		SPtr<LightProbeVolume> volume;
+		HSceneObject sceneObject;
+	};
+
 	/** Possible states components can be in. Controls which component callbacks are triggered. */
 	enum class ComponentState
 	{
@@ -139,6 +153,12 @@ namespace bs
 		/**	Notifies the scene manager that a reflection probe was removed. */
 		void _unregisterReflectionProbe(const SPtr<ReflectionProbe>& probe);
 
+		/**	Notifies the scene manager that a new light probe volume was created. */
+		void _registerLightProbeVolume(const SPtr<LightProbeVolume>& volume, const HSceneObject& so);
+
+		/**	Notifies the scene manager that a light proble volume was removed. */
+		void _unregisterLightProbeVolume(const SPtr<LightProbeVolume>& volume);
+
 		/**	Notifies the scene manager that a camera either became the main camera, or has stopped being main camera. */
 		void _notifyMainCameraStateChanged(const SPtr<Camera>& camera);
 
@@ -215,6 +235,7 @@ namespace bs
 		Map<Renderable*, SceneRenderableData> mRenderables;
 		Map<Light*, SceneLightData> mLights;
 		Map<ReflectionProbe*, SceneReflectionProbeData> mReflectionProbes;
+		Map<LightProbeVolume*, SceneLightProbeVolumeData> mLightProbeVolumes;
 
 		Vector<HComponent> mActiveComponents;
 		Vector<HComponent> mInactiveComponents;

+ 34 - 3
Source/BansheeCore/Include/BsSkybox.h

@@ -8,6 +8,11 @@
 
 namespace bs
 {
+	namespace ct
+	{
+		class RendererTask;
+	}
+
 	/** @addtogroup Implementation
 	 *  @{
 	 */
@@ -80,7 +85,7 @@ namespace bs
 	};
 
 	/** @} */
-	/** @addtogroup Renderer-Engine-Internal
+	/** @addtogroup Renderer-Internal
 	 *  @{
 	 */
 
@@ -90,6 +95,8 @@ namespace bs
 	class BS_CORE_EXPORT Skybox : public IReflectable, public CoreObject, public TSkybox<false>
 	{
 	public:
+		~Skybox();
+		
 		/**	Retrieves an implementation of the skybox usable only from the core thread. */
 		SPtr<ct::Skybox> getCore() const;
 
@@ -99,6 +106,12 @@ namespace bs
 	protected:
 		Skybox();
 
+		/** 
+		 * Filters the skybox radiance texture, generating filtered radiance (for reflections) and irradiance. Should be 
+		 * called any time the skybox texture changes. 
+		 */
+		void filterTexture();
+
 		/** @copydoc CoreObject::createCore */
 		SPtr<ct::CoreObject> createCore() const override;
 
@@ -108,6 +121,10 @@ namespace bs
 		/** @copydoc CoreObject::syncToCore */
 		CoreSyncData syncToCore(FrameAlloc* allocator) override;
 
+		SPtr<Texture> mFilteredRadiance;
+		SPtr<Texture> mIrradiance;
+		SPtr<ct::RendererTask> mRendererTask;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
@@ -125,18 +142,32 @@ namespace bs
 		public:
 			~Skybox();
 
+			/** 
+			 * Returns a texture containing filtered version of the radiance texture used for reflections. This might not
+			 * be available if it hasn't been generated yet.
+			 */
+			SPtr<Texture> getFilteredRadiance() const { return mFilteredRadiance; }
+
+			/**
+			 * Returns a texture containing sky irradiance. This might not be available if it hasn't been generated yet.
+			 */
+			SPtr<Texture> getIrradiance() const { return mIrradiance; }
+
 		protected:
 			friend class bs::Skybox;
 
-			Skybox();
+			Skybox(const SPtr<Texture>& filteredRadiance, const SPtr<Texture>& irradiance);
 
 			/** @copydoc CoreObject::initialize */
 			void initialize() override;
 
 			/** @copydoc CoreObject::syncToCore */
 			void syncToCore(const CoreSyncData& data) override;
+
+			SPtr<Texture> mFilteredRadiance;
+			SPtr<Texture> mIrradiance;
 		};
 	}
 
 	/** @} */
-}
+}

+ 14 - 2
Source/BansheeCore/Include/BsSkyboxRTTI.h

@@ -5,6 +5,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
 #include "BsSkybox.h"
+#include "BsRenderer.h"
 
 namespace bs
 {
@@ -20,12 +21,23 @@ namespace bs
 			BS_RTTI_MEMBER_REFL(mTexture, 0)
 			BS_RTTI_MEMBER_PLAIN(mUUID, 1)
 			BS_RTTI_MEMBER_PLAIN(mBrightness, 2)
+			BS_RTTI_MEMBER_REFLPTR(mFilteredRadiance, 3)
+			BS_RTTI_MEMBER_REFLPTR(mIrradiance, 4)
 		BS_END_RTTI_MEMBERS
 	public:
-        SkyboxRTTI()
+		SkyboxRTTI()
 			:mInitMembers(this)
 		{ }
 
+		void onSerializationStarted(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
+		{
+			Skybox* skybox = static_cast<Skybox*>(obj);
+
+			// Make sure that the renderer finishes generating filtered radiance and irradiance before saving
+			if (skybox->mRendererTask)
+				skybox->mRendererTask->wait();
+		}
+
 		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
 		{
 			// Note: Since this is a CoreObject I should call initialize() right after deserialization,
@@ -53,4 +65,4 @@ namespace bs
 
 	/** @} */
 	/** @endcond */
-}
+}

+ 1 - 1
Source/BansheeCore/Include/BsTexture.h

@@ -48,7 +48,7 @@ namespace bs
 		TextureType type = TEX_TYPE_2D;
 
 		/** Format of pixels in the texture. */
-		PixelFormat format = PF_R8G8B8A8;
+		PixelFormat format = PF_RGBA8;
 
 		/** Width of the texture in pixels. */
 		UINT32 width = 1;

+ 0 - 1
Source/BansheeCore/Source/BsAnimationClip.cpp

@@ -7,7 +7,6 @@
 
 namespace bs
 {
-
 	void AnimationCurves::addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve)
 	{
 		auto iterFind = std::find_if(position.begin(), position.end(), [&](auto x) { return x.name == name; });

+ 3 - 3
Source/BansheeCore/Source/BsAnimationCurve.cpp

@@ -525,7 +525,7 @@ namespace bs
 			mKeyframes[i].value = getDiff(mKeyframes[i].value, refKey.value);
 	}
 
-	template class TAnimationCurve<Vector3>;
-	template class TAnimationCurve<Quaternion>;
-	template class TAnimationCurve<float>;
+	template BS_CORE_EXPORT class TAnimationCurve<Vector3>;
+	template BS_CORE_EXPORT class TAnimationCurve<Quaternion>;
+	template BS_CORE_EXPORT class TAnimationCurve<float>;
 }

+ 1 - 1
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -88,7 +88,7 @@ namespace bs
 		auto& allCameras = gSceneManager().getAllCameras();
 		for(auto& entry : allCameras)
 		{
-			bool isOverlayCamera = entry.second.camera->getFlags().isSet(CameraFlag::Overlay);
+			bool isOverlayCamera = entry.second.camera->getRenderSettings()->overlayOnly;
 			if (isOverlayCamera)
 				continue;
 

+ 108 - 18
Source/BansheeCore/Source/BsAnimationUtility.cpp

@@ -66,7 +66,7 @@ namespace bs
 		}
 	}
 
-	TAnimationCurve<Quaternion> AnimationUtility::eulerToQuaternionCurve(const TAnimationCurve<Vector3>& eulerCurve)
+	SPtr<TAnimationCurve<Quaternion>> AnimationUtility::eulerToQuaternionCurve(const SPtr<TAnimationCurve<Vector3>>& eulerCurve)
 	{
 		// TODO: We calculate tangents by sampling which can introduce error in the tangents. The error can be exacerbated
 		// by the fact we constantly switch between the two representations, possibly losing precision every time. Instead 
@@ -103,15 +103,15 @@ namespace bs
 			return quat;
 		};
 
-		INT32 numKeys = (INT32)eulerCurve.getNumKeyFrames();
+		INT32 numKeys = (INT32)eulerCurve->getNumKeyFrames();
 		Vector<TKeyframe<Quaternion>> quatKeyframes(numKeys);
 
 		// Calculate key values
 		Quaternion lastQuat(BsZero);
 		for (INT32 i = 0; i < numKeys; i++)
 		{
-			float time = eulerCurve.getKeyFrame(i).time;
-			Vector3 angles = eulerCurve.getKeyFrame(i).value;
+			float time = eulerCurve->getKeyFrame(i).time;
+			Vector3 angles = eulerCurve->getKeyFrame(i).value;
 			Quaternion quat = eulerToQuaternion(i, angles, lastQuat);
 
 			quatKeyframes[i].time = time;
@@ -129,15 +129,15 @@ namespace bs
 			TKeyframe<Quaternion>& currentKey = quatKeyframes[i];
 			TKeyframe<Quaternion>& nextKey = quatKeyframes[i + 1];
 
-			const TKeyframe<Vector3>& currentEulerKey = eulerCurve.getKeyFrame(i);
-			const TKeyframe<Vector3>& nextEulerKey = eulerCurve.getKeyFrame(i + 1);
+			const TKeyframe<Vector3>& currentEulerKey = eulerCurve->getKeyFrame(i);
+			const TKeyframe<Vector3>& nextEulerKey = eulerCurve->getKeyFrame(i + 1);
 
 			float dt = nextKey.time - currentKey.time;
 			float startFitTime = currentKey.time + dt * FIT_TIME;
 			float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
 
-			Vector3 anglesStart = eulerCurve.evaluate(startFitTime, false);
-			Vector3 anglesEnd = eulerCurve.evaluate(endFitTime, false);
+			Vector3 anglesStart = eulerCurve->evaluate(startFitTime, false);
+			Vector3 anglesEnd = eulerCurve->evaluate(endFitTime, false);
 			Quaternion startFitValue = eulerToQuaternion(i, anglesStart, currentKey.value);
 			Quaternion endFitValue = eulerToQuaternion(i, anglesEnd, startFitValue);
 
@@ -148,10 +148,10 @@ namespace bs
 			setStepTangent(currentEulerKey, nextEulerKey, currentKey, nextKey);
 		}
 
-		return TAnimationCurve<Quaternion>(quatKeyframes);
+		return bs_shared_ptr_new<TAnimationCurve<Quaternion>>(quatKeyframes);
 	}
 
-	TAnimationCurve<Vector3> AnimationUtility::quaternionToEulerCurve(const TAnimationCurve<Quaternion>& quatCurve)
+	SPtr<TAnimationCurve<Vector3>> AnimationUtility::quaternionToEulerCurve(const SPtr<TAnimationCurve<Quaternion>>& quatCurve)
 	{
 		// TODO: We calculate tangents by sampling. There must be an analytical way to calculate tangents when converting
 		// a curve.
@@ -171,14 +171,14 @@ namespace bs
 			return euler;
 		};
 
-		INT32 numKeys = (INT32)quatCurve.getNumKeyFrames();
+		INT32 numKeys = (INT32)quatCurve->getNumKeyFrames();
 		Vector<TKeyframe<Vector3>> eulerKeyframes(numKeys);
 
 		// Calculate key values
 		for (INT32 i = 0; i < numKeys; i++)
 		{
-			float time = quatCurve.getKeyFrame(i).time;
-			Quaternion quat = quatCurve.getKeyFrame(i).value;
+			float time = quatCurve->getKeyFrame(i).time;
+			Quaternion quat = quatCurve->getKeyFrame(i).value;
 			Vector3 euler = quaternionToEuler(quat);
 
 			eulerKeyframes[i].time = time;
@@ -194,15 +194,15 @@ namespace bs
 			TKeyframe<Vector3>& currentKey = eulerKeyframes[i];
 			TKeyframe<Vector3>& nextKey = eulerKeyframes[i + 1];
 
-			const TKeyframe<Quaternion>& currentQuatKey = quatCurve.getKeyFrame(i);
-			const TKeyframe<Quaternion>& nextQuatKey = quatCurve.getKeyFrame(i + 1);
+			const TKeyframe<Quaternion>& currentQuatKey = quatCurve->getKeyFrame(i);
+			const TKeyframe<Quaternion>& nextQuatKey = quatCurve->getKeyFrame(i + 1);
 
 			float dt = nextKey.time - currentKey.time;
 			float startFitTime = currentKey.time + dt * FIT_TIME;
 			float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
 
-			Quaternion startQuat = Quaternion::normalize(quatCurve.evaluate(startFitTime, false));
-			Quaternion endQuat = Quaternion::normalize(quatCurve.evaluate(endFitTime, false));
+			Quaternion startQuat = Quaternion::normalize(quatCurve->evaluate(startFitTime, false));
+			Quaternion endQuat = Quaternion::normalize(quatCurve->evaluate(endFitTime, false));
 			Vector3 startFitValue = quaternionToEuler(startQuat);
 			Vector3 endFitValue = quaternionToEuler(endQuat);
 
@@ -220,7 +220,97 @@ namespace bs
 			setStepTangent(currentQuatKey, nextQuatKey, currentKey, nextKey);
 		}
 
-		return TAnimationCurve<Vector3>(eulerKeyframes);
+		return bs_shared_ptr_new<TAnimationCurve<Vector3>>(eulerKeyframes);
+	}
+
+	Vector<SPtr<TAnimationCurve<float>>> AnimationUtility::splitCurve(const SPtr<TAnimationCurve<Vector3>>& compoundCurve)
+	{
+		UINT32 numKeyFrames = compoundCurve->getNumKeyFrames();
+		Vector<TKeyframe<float>> keyFrames[3];
+
+		for (UINT32 i = 0; i < numKeyFrames; i++)
+		{
+			const TKeyframe<Vector3>& key = compoundCurve->getKeyFrame(i);
+
+			TKeyframe<float> newKey;
+			newKey.time = key.time;
+
+			for (UINT32 j = 0; j < 3; j++)
+			{
+				bool addNew = true;
+				if (i > 0)
+				{
+					const TKeyframe<float>& prevKey = keyFrames[j].back();
+
+					bool isEqual = Math::approxEquals(prevKey.value, key.value[j]) &&
+						Math::approxEquals(prevKey.outTangent, key.inTangent[j]);
+
+					addNew = !isEqual;
+				}
+
+				if (addNew)
+				{
+					newKey.value = key.value[j];
+					newKey.inTangent = key.inTangent[j];
+					newKey.outTangent = key.outTangent[j];
+
+					keyFrames[j].push_back(newKey);
+				}
+			}
+		}
+
+		Vector<SPtr<TAnimationCurve<float>>> output(3);
+		for (UINT32 i = 0; i < 3; i++)
+			output[i] = bs_shared_ptr_new<TAnimationCurve<float>>(keyFrames[i]);
+
+		return output;
+	}
+
+	SPtr<TAnimationCurve<Vector3>> AnimationUtility::combineCurve(const Vector<SPtr<TAnimationCurve<float>>>& curveComponents)
+	{
+		// Find unique keyframe times
+		Map<float, TKeyframe<Vector3>> keyFrames;
+		for(UINT32 i = 0; i < 3; i++)
+		{
+			if (i >= (UINT32)curveComponents.size())
+				break;
+
+			UINT32 numKeyFrames = curveComponents[i]->getNumKeyFrames();
+			for (UINT32 j = 0; j < numKeyFrames; j++)
+			{
+				const TKeyframe<float>& keyFrame = curveComponents[i]->getKeyFrame(j);
+
+				auto iterFind = keyFrames.find(keyFrame.time);
+				if (iterFind == keyFrames.end())
+				{
+					TKeyframe<Vector3> newKeyFrame;
+					newKeyFrame.time = keyFrame.time;
+
+					keyFrames.insert(std::make_pair(keyFrame.time, newKeyFrame));
+				}
+			}
+		}
+
+		// Populate keyframe values
+		Vector<TKeyframe<Vector3>> keyframeList(keyFrames.size());
+		UINT32 idx = 0;
+		for(auto& entry : keyFrames)
+		{
+			TKeyframe<Vector3>& keyFrame = entry.second;
+			
+			for(UINT32 j = 0; j < 3; j++)
+			{
+				TKeyframe<float> currentKey = curveComponents[j]->evaluateKey(keyFrame.time, false);
+				keyFrame.value[j] = currentKey.value;
+				keyFrame.inTangent[j] = currentKey.inTangent;
+				keyFrame.outTangent[j] = currentKey.outTangent;
+			}
+
+			keyframeList[idx] = keyFrame;
+			idx++;
+		}
+
+		return bs_shared_ptr_new<TAnimationCurve<Vector3>>(keyframeList);
 	}
 
 	template<class T>

+ 2 - 1
Source/BansheeCore/Source/BsCLight.cpp

@@ -23,7 +23,8 @@ namespace bs
 
 	CLight::~CLight()
 	{
-		mInternal->destroy();
+		if(mInternal != nullptr)
+			mInternal->destroy();
 	}
 
 	Sphere CLight::getBounds() const

+ 72 - 0
Source/BansheeCore/Source/BsCLightProbeVolume.cpp

@@ -0,0 +1,72 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCLightProbeVolume.h"
+#include "BsCLightProbeVolumeRTTI.h"
+#include "BsSceneManager.h"
+
+namespace bs
+{
+	CLightProbeVolume::CLightProbeVolume()
+	{
+		setFlag(ComponentFlag::AlwaysRun, true);
+		setName("LightProbeVolume");
+	}
+
+	CLightProbeVolume::CLightProbeVolume(const HSceneObject& parent, const AABox& volume, const Vector3I& cellCount)
+		:Component(parent), mVolume(volume), mCellCount(cellCount)
+	{
+		setFlag(ComponentFlag::AlwaysRun, true);
+		setName("Light");
+	}
+
+	CLightProbeVolume::~CLightProbeVolume()
+	{
+		if(mInternal != nullptr)
+			mInternal->destroy();
+	}
+
+	void CLightProbeVolume::renderProbe(UINT32 handle)
+	{
+		if (mInternal != nullptr && SO()->getActive())
+		{
+			mInternal->_updateTransform(SO());
+			mInternal->renderProbe(handle);
+		}
+	}
+
+	void CLightProbeVolume::renderProbes()
+	{
+		if (mInternal != nullptr && SO()->getActive())
+		{
+			mInternal->_updateTransform(SO());
+			mInternal->renderProbes();
+		}
+	}
+
+	void CLightProbeVolume::onInitialized()
+	{
+		// If mInternal already exists this means this object was deserialized,
+		// so all we need to do is initialize it.
+		if (mInternal != nullptr)
+			mInternal->initialize();
+		else
+			mInternal = LightProbeVolume::create(mVolume, mCellCount);
+
+		gSceneManager()._registerLightProbeVolume(mInternal, sceneObject());
+	}
+
+	void CLightProbeVolume::onDestroyed()
+	{
+		gSceneManager()._unregisterLightProbeVolume(mInternal);
+	}
+	
+	RTTITypeBase* CLightProbeVolume::getRTTIStatic()
+	{
+		return CLightProbeVolumeRTTI::instance();
+	}
+
+	RTTITypeBase* CLightProbeVolume::getRTTI() const
+	{
+		return CLightProbeVolume::getRTTIStatic();
+	}
+}

+ 10 - 0
Source/BansheeCore/Source/BsCReflectionProbe.cpp

@@ -41,6 +41,16 @@ namespace bs
 			mInternal = ReflectionProbe::createBox(Vector3::ONE);
 
 		gSceneManager()._registerReflectionProbe(mInternal, sceneObject());
+
+		// If filtered texture doesn't exist, ensure it is generated
+		SPtr<Texture> filteredTexture = mInternal->getFilteredTexture();
+		if(filteredTexture == nullptr)
+		{
+			if (mInternal->getCustomTexture())
+				mInternal->filter();
+			else
+				mInternal->capture();
+		}
 	}
 
 	void CReflectionProbe::onDestroyed()

+ 9 - 22
Source/BansheeCore/Source/BsCamera.cpp

@@ -19,14 +19,14 @@ namespace bs
 	const float CameraBase::INFINITE_FAR_PLANE_ADJUST = 0.00001f;
 
 	CameraBase::CameraBase()
-		: mLayers(0xFFFFFFFFFFFFFFFF), mCameraFlags(CameraFlag::HDR), mPosition(BsZero), mRotation(BsIdentity)
+		: mLayers(0xFFFFFFFFFFFFFFFF), mPosition(BsZero), mRotation(BsIdentity)
 		, mIsActive(true), mProjType(PT_PERSPECTIVE), mHorzFOV(Degree(90.0f)), mFarDist(1000.0f), mNearDist(0.05f)
 		, mAspect(1.33333333333333f), mOrthoHeight(5), mPriority(0), mCustomViewMatrix(false), mCustomProjMatrix(false)
 		, mMSAA(1), mFrustumExtentsManuallySet(false), mProjMatrixRS(BsZero), mProjMatrix(BsZero), mViewMatrix(BsZero)
 		, mProjMatrixRSInv(BsZero), mProjMatrixInv(BsZero), mViewMatrixInv(BsZero), mRecalcFrustum(true)
 		, mRecalcFrustumPlanes(true), mRecalcView(true)
 	{
-		mPPSettings = RendererManager::instance().getActive()->createPostProcessSettings();
+		mRenderSettings = bs_shared_ptr_new<RenderSettings>();
 
 		invalidateFrustum();
 	}
@@ -461,16 +461,6 @@ namespace bs
 		outbottom = mBottom;
 	}
 
-	void CameraBase::setFlag(const CameraFlag& flag, bool enable)
-	{
-		if (enable)
-			mCameraFlags.set(flag);
-		else
-			mCameraFlags.unset(flag);
-			
-		_markCoreDirty();
-	}
-
 	void CameraBase::setPosition(const Vector3& position)
 	{
 		mPosition = position;
@@ -758,14 +748,13 @@ namespace bs
 			size += rttiGetElemSize(mCustomViewMatrix);
 			size += rttiGetElemSize(mCustomProjMatrix);
 			size += rttiGetElemSize(mFrustumExtentsManuallySet);
-			size += rttiGetElemSize(mCameraFlags);
 			size += rttiGetElemSize(mIsActive);
 			size += rttiGetElemSize(mMSAA);
 			size += sizeof(UINT32);
 
-			if(mPPSettings != nullptr)
+			if(mRenderSettings != nullptr)
 			{
-				mPPSettings->_getSyncData(nullptr, ppSize);
+				mRenderSettings->_getSyncData(nullptr, ppSize);
 				size += ppSize;
 			}
 		}
@@ -790,14 +779,13 @@ namespace bs
 			dataPtr = rttiWriteElem(mCustomViewMatrix, dataPtr);
 			dataPtr = rttiWriteElem(mCustomProjMatrix, dataPtr);
 			dataPtr = rttiWriteElem(mFrustumExtentsManuallySet, dataPtr);
-			dataPtr = rttiWriteElem(mCameraFlags, dataPtr);
 			dataPtr = rttiWriteElem(mIsActive, dataPtr);
 			dataPtr = rttiWriteElem(mMSAA, dataPtr);
 
 			dataPtr = rttiWriteElem(ppSize, dataPtr);
 
-			if(mPPSettings != nullptr)
-				mPPSettings->_getSyncData((UINT8*)dataPtr, ppSize);
+			if(mRenderSettings != nullptr)
+				mRenderSettings->_getSyncData((UINT8*)dataPtr, ppSize);
 
 			dataPtr += ppSize;
 		}
@@ -882,7 +870,6 @@ namespace bs
 			dataPtr = rttiReadElem(mCustomViewMatrix, dataPtr);
 			dataPtr = rttiReadElem(mCustomProjMatrix, dataPtr);
 			dataPtr = rttiReadElem(mFrustumExtentsManuallySet, dataPtr);
-			dataPtr = rttiReadElem(mCameraFlags, dataPtr);
 			dataPtr = rttiReadElem(mIsActive, dataPtr);
 			dataPtr = rttiReadElem(mMSAA, dataPtr);
 
@@ -891,10 +878,10 @@ namespace bs
 
 			if(ppSize > 0)
 			{
-				if (mPPSettings == nullptr)
-					mPPSettings = RendererManager::instance().getActive()->createPostProcessSettings();
+				if (mRenderSettings == nullptr)
+					mRenderSettings = bs_shared_ptr_new<RenderSettings>();
 
-				mPPSettings->_setSyncData((UINT8*)dataPtr, ppSize);
+				mRenderSettings->_setSyncData((UINT8*)dataPtr, ppSize);
 				dataPtr += ppSize;
 			}
 		}

+ 4 - 0
Source/BansheeCore/Source/BsCoreApplication.cpp

@@ -238,6 +238,10 @@ namespace bs
 			// Send out resource events in case any were loaded/destroyed/modified
 			ResourceListenerManager::instance().update();
 
+			// Trigger any renderer task callbacks (should be done before scene object update, or core sync, so objects have
+			// a chance to respond to the callback).
+			RendererManager::instance().getActive()->update();
+
 			gSceneManager()._updateCoreObjectTransforms();
 			PROFILE_CALL(RendererManager::instance().getActive()->renderAll(), "Render");
 

+ 14 - 0
Source/BansheeCore/Source/BsIBLUtility.cpp

@@ -0,0 +1,14 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsIBLUtility.h"
+
+namespace bs { namespace ct
+{
+	const UINT32 IBLUtility::REFLECTION_CUBEMAP_SIZE = 256;
+	const UINT32 IBLUtility::IRRADIANCE_CUBEMAP_SIZE = 32;
+
+	const IBLUtility& gIBLUtility()
+	{
+		return IBLUtility::instance();
+	}
+}}

+ 353 - 72
Source/BansheeCore/Source/BsLightProbeVolume.cpp

@@ -5,7 +5,10 @@
 #include "BsFrameAlloc.h"
 #include "BsRenderer.h"
 #include "BsLight.h"
-#include <atlalloc.h>
+#include "BsGpuBuffer.h"
+#include "BsTexture.h"
+#include "BsIBLUtility.h"
+#include "BsSceneObject.h"
 
 namespace bs
 {
@@ -14,17 +17,25 @@ namespace bs
 	{ }
 
 	LightProbeVolume::LightProbeVolume()
+		: mVolume(AABox::UNIT_BOX), mCellCount { 1, 1, 1 }, mLastUpdateHash(0)
 	{ }
 
-	LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3& density)
+	LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3I& cellCount)
+		:mVolume(volume), mCellCount(cellCount), mLastUpdateHash(0)
 	{
-		// TODO - Generates probes in the grid volume
+		reset();
+	}
+
+	LightProbeVolume::~LightProbeVolume()
+	{
+		if (mRendererTask)
+			mRendererTask->cancel();
 	}
 
 	UINT32 LightProbeVolume::addProbe(const Vector3& position)
 	{
 		UINT32 handle = mNextProbeId++;
-		mProbes[handle] = ProbeInfo(LightProbeFlags::Dirty, position);
+		mProbes[handle] = ProbeInfo(LightProbeFlags::Clean, position);
 
 		_markCoreDirty();
 		return handle;
@@ -45,7 +56,6 @@ namespace bs
 		auto iterFind = mProbes.find(handle);
 		if (iterFind != mProbes.end())
 		{
-			iterFind->second.flags = LightProbeFlags::Dirty;
 			iterFind->second.position = position;
 			_markCoreDirty();
 		}
@@ -60,14 +70,208 @@ namespace bs
 		return Vector3::ZERO;
 	}
 
+	void LightProbeVolume::resize(const AABox& volume, const Vector3I& cellCount)
+	{
+		UINT32 numProbesX = std::max(1, mCellCount.v[0]) + 1;
+		UINT32 numProbesY = std::max(1, mCellCount.v[1]) + 1;
+		UINT32 numProbesZ = std::max(1, mCellCount.v[2]) + 1;
+
+		Vector3 size = mVolume.getSize();
+		for(UINT32 z = 0; z < numProbesZ; ++z)
+		{
+			for(UINT32 y = 0; y < numProbesY; ++y)
+			{
+				for(UINT32 x = 0; x < numProbesX; ++x)
+				{
+					Vector3 position = mVolume.getMin();
+					position.x += size.x * (x / (float)numProbesX);
+					position.y += size.y * (y / (float)numProbesY);
+					position.z += size.z * (z / (float)numProbesZ);
+
+					if (mVolume.contains(position))
+						continue;
+
+					addProbe(position);
+				}
+			}
+		}
+
+		mVolume = volume;
+		mCellCount = cellCount;
+
+		_markCoreDirty();
+	}
+
+	void LightProbeVolume::reset()
+	{
+		UINT32 numProbesX = std::max(1, mCellCount.v[0]) + 1;
+		UINT32 numProbesY = std::max(1, mCellCount.v[1]) + 1;
+		UINT32 numProbesZ = std::max(1, mCellCount.v[2]) + 1;
+
+		UINT32 numProbes = numProbesX * numProbesY * numProbesZ;
+
+		// Make sure there are adequate number of probes to fill the volume
+		while((UINT32)mProbes.size() < numProbes)
+			addProbe(Vector3::ZERO);
+
+		UINT32 idx = 0;
+		UINT32 rowPitch = numProbesX;
+		UINT32 slicePitch = numProbesX * numProbesY;
+
+		Vector3 size = mVolume.getSize();
+
+		auto iter = mProbes.begin();
+		while (iter != mProbes.end())
+		{
+			UINT32 x = idx % numProbesX;
+			UINT32 y = (idx / rowPitch) % numProbesY;
+			UINT32 z = (idx / slicePitch);
+
+			Vector3 position = mVolume.getMin();
+			position.x += size.x * (x / (float)(numProbesX - 1));
+			position.y += size.y * (y / (float)(numProbesY - 1));
+			position.z += size.z * (z / (float)(numProbesZ - 1));
+
+			iter->second.position = position;
+			iter->second.flags = LightProbeFlags::Clean;
+
+			++idx;
+			++iter;
+
+			if (idx >= numProbes)
+				break;
+		}
+
+		// Set remaining probes to removed state
+		while(iter != mProbes.end())
+		{
+			iter->second.flags = LightProbeFlags::Removed;
+			++iter;
+		}
+
+		_markCoreDirty();
+	}
+
+	void LightProbeVolume::clip()
+	{
+		for (auto& entry : mProbes)
+		{
+			if (!mVolume.contains(entry.second.position))
+				entry.second.flags = LightProbeFlags::Removed;
+		}
+
+		_markCoreDirty();
+	}
+
+	void LightProbeVolume::renderProbe(UINT32 handle)
+	{
+		auto iterFind = mProbes.find(handle);
+		if (iterFind != mProbes.end())
+		{
+			if (iterFind->second.flags == LightProbeFlags::Clean)
+			{
+				iterFind->second.flags = LightProbeFlags::Dirty;
+
+				_markCoreDirty();
+				runRenderProbeTask();
+			}
+		}
+	}
+
+	void LightProbeVolume::renderProbes()
+	{
+		bool anyModified = false;
+		for(auto& entry : mProbes)
+		{
+			if (entry.second.flags == LightProbeFlags::Clean)
+			{
+				entry.second.flags = LightProbeFlags::Dirty;
+				anyModified = true;
+			}
+		}
+
+		if (anyModified)
+		{
+			_markCoreDirty();
+			runRenderProbeTask();
+		}
+	}
+
+	void LightProbeVolume::_updateTransform(const HSceneObject& so, bool force)
+	{
+		UINT32 curHash = so->getTransformHash();
+		if (curHash != _getLastModifiedHash() || force)
+		{
+			mPosition = so->getWorldPosition();
+			mRotation = so->getWorldRotation();
+
+			_markCoreDirty();
+			_setLastModifiedHash(curHash);
+		}
+	}
+
+	void LightProbeVolume::runRenderProbeTask()
+	{
+		// If a task is already running cancel it
+		// Note: If the task is just about to start processing, cancelling it will skip the update this frame
+		// (which might be fine if we just changed positions of dirty probes it was about to update, but it might also
+		// waste a frame if those positions needed to be updated anyway). For now I'm ignoring it as it seems like a rare
+		// enough situation, plus it's one that will only happen during development time.
+		if (mRendererTask)
+			mRendererTask->cancel();
+
+		auto renderComplete = [this]()
+		{
+			mRendererTask = nullptr;
+		};
+
+		SPtr<ct::LightProbeVolume> coreProbeVolume = getCore();
+		auto renderProbes = [coreProbeVolume]()
+		{
+			return coreProbeVolume->renderProbes(3);
+		};
+
+		mRendererTask = ct::RendererTask::create("RenderLightProbes", renderProbes);
+
+		mRendererTask->onComplete.connect(renderComplete);
+		ct::gRenderer()->addTask(mRendererTask);
+	}
+
+	void LightProbeVolume::updateCoefficients()
+	{
+		// Ensure all light probe coefficients are generated
+		if (mRendererTask)
+			mRendererTask->wait();
+
+		ct::LightProbeVolume* coreVolume = getCore().get();
+
+		Vector<LightProbeCoefficientInfo> coeffInfo;
+		auto getSaveData = [coreVolume, &coeffInfo]()
+		{
+			coreVolume->getProbeCoefficients(coeffInfo);
+		};
+
+		gCoreThread().queueCommand(getSaveData);
+		gCoreThread().submit(true);
+
+		for(auto& entry : coeffInfo)
+		{
+			auto iterFind = mProbes.find(entry.handle);
+			if (iterFind == mProbes.end())
+				continue;
+
+			iterFind->second.coefficients = entry.coefficients;
+		}
+	}
+
 	SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
 	{
 		return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
 	}
 
-	SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3& density)
+	SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3I& cellCount)
 	{
-		LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, density);
+		LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, cellCount);
 		SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
 		probeVolumePtr->_setThisPtr(probeVolumePtr);
 		probeVolumePtr->initialize();
@@ -116,6 +320,9 @@ namespace bs
 				}
 			}
 
+			for (auto& probe : removedProbes)
+				mProbes.erase(probe);
+
 			UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
 			UINT32 numRemovedProbes = (UINT32)removedProbes.size();
 
@@ -170,6 +377,10 @@ namespace bs
 	{
 	LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
 	{
+		mInitCoefficients.resize(probes.size());
+		mProbePositions.resize(probes.size());
+		mProbeInfos.resize(probes.size());
+
 		UINT32 probeIdx = 0;
 		for(auto& entry : probes)
 		{
@@ -178,10 +389,12 @@ namespace bs
 			
 			LightProbeInfo probeInfo;
 			probeInfo.flags = LightProbeFlags::Dirty;
-			probeInfo.bufferIdx = -1;
-			probeInfo.handle = probeIdx;
+			probeInfo.bufferIdx = probeIdx;
+			probeInfo.handle = entry.first;
 
 			mProbeInfos[probeIdx] = probeInfo;
+			mInitCoefficients[probeIdx] = entry.second.coefficients;
+
 			probeIdx++;
 		}
 	}
@@ -193,73 +406,58 @@ namespace bs
 
 	void LightProbeVolume::initialize()
 	{
-		gRenderer()->notifyLightProbeVolumeAdded(this);
+		// Set SH coefficients loaded from the file
+		UINT32 numCoefficients = (UINT32)mInitCoefficients.size();
+		assert(mInitCoefficients.size() == mProbeMap.size());
+
+		resizeCoefficientBuffer(std::max(32U, numCoefficients));
+		mCoefficients->writeData(0, sizeof(LightProbeSHCoefficients) * numCoefficients, mInitCoefficients.data());
+		mInitCoefficients.clear();
 
+		gRenderer()->notifyLightProbeVolumeAdded(this);
 		CoreObject::initialize();
 	}
 
-	void LightProbeVolume::prune(Vector<UINT32>& freedEntries, bool freeAll)
+	bool LightProbeVolume::renderProbes(UINT32 maxProbes)
 	{
-		UINT32 numProbes = (UINT32)mProbeInfos.size();
-		INT32 lastSearchIdx = numProbes - 1;
-		
-		for (UINT32 i = 0; i < (UINT32)mProbeInfos.size(); ++i)
+		// Probe map only contains active probes
+		UINT32 numUsedProbes = (UINT32)mProbeMap.size();
+		if(numUsedProbes > mCoeffBufferSize)
+			resizeCoefficientBuffer(std::max(32U, numUsedProbes * 2));
+
+		UINT32 numProbeUpdates = 0;
+		for (; mFirstDirtyProbe < (UINT32)mProbeInfos.size(); ++mFirstDirtyProbe)
 		{
-			LightProbeInfo& info = mProbeInfos[i];
+			LightProbeInfo& probeInfo = mProbeInfos[mFirstDirtyProbe];
 
-			if (info.flags == LightProbeFlags::Removed)
+			if(probeInfo.flags == LightProbeFlags::Dirty)
 			{
-				if (info.bufferIdx != -1)
-					freedEntries.push_back(info.bufferIdx);
+				TEXTURE_DESC cubemapDesc;
+				cubemapDesc.type = TEX_TYPE_CUBE_MAP;
+				cubemapDesc.format = PF_RGBA16F;
+				cubemapDesc.width = 256; // Note: Test different sizes and their effect on quality
+				cubemapDesc.height = 256;
+				cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
 
-				info.flags = LightProbeFlags::Empty;
+				SPtr<Texture> cubemap = Texture::create(cubemapDesc);
 
-				// Replace the empty spot with an element from the back
-				while (lastSearchIdx >= (INT32)i)
-				{
-					bool foundNonEmpty = false;
-					LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
-					if (flags != LightProbeFlags::Empty)
-					{
-						std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
-						std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
-
-						mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
-						foundNonEmpty = true;
-					}
+				Vector3 localPos = mProbePositions[mFirstDirtyProbe];
+				Vector3 transformedPos = mRotation.rotate(localPos) + mPosition;
 
-					// Remove last element
-					mProbeInfos.erase(mProbeInfos.begin() + lastSearchIdx);
-					mProbePositions.erase(mProbePositions.begin() + lastSearchIdx);
-					lastSearchIdx--;
+				gRenderer()->captureSceneCubeMap(cubemap, transformedPos, true);
+				gIBLUtility().filterCubemapForIrradiance(cubemap, mCoefficients, probeInfo.bufferIdx);
 
-					// Search is done, we found an element to fill the empty spot
-					if (foundNonEmpty)
-						break;
-				}
+				probeInfo.flags = LightProbeFlags::Clean;
+				numProbeUpdates++;
 			}
-		}
 
-		if(freeAll)
-		{
-			// Add all remaining (non-removed) probes to the free list, and mark them as dirty so when/if those probes
-			// get used again, the systems knows they are out of date
-			for (UINT32 i = 0; i < (UINT32)mProbeInfos.size(); ++i)
-			{
-				LightProbeInfo& info = mProbeInfos[i];
+			if (maxProbes != 0 && numProbeUpdates >= maxProbes)
+				break;
+		}
 
-				if (info.flags != LightProbeFlags::Empty)
-				{
-					if (info.bufferIdx != -1)
-					{
-						freedEntries.push_back(info.bufferIdx);
-						info.bufferIdx = -1;
-					}
+		gRenderer()->notifyLightProbeVolumeUpdated(this);
 
-					info.flags = LightProbeFlags::Dirty;
-				}
-			}
-		}
+		return mFirstDirtyProbe == (UINT32)mProbeInfos.size();
 	}
 
 	void LightProbeVolume::syncToCore(const CoreSyncData& data)
@@ -290,27 +488,59 @@ namespace bs
 			auto iterFind = mProbeMap.find(handle);
 			if(iterFind != mProbeMap.end())
 			{
+				// Update existing probe information
 				UINT32 compactIdx = iterFind->second;
 				
 				mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
 				mProbePositions[compactIdx] = position;
+
+				mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
 			}
-			else
+			else // Add a new probe
 			{
-				UINT32 compactIdx = (UINT32)mProbeInfos.size();
+				// Empty slots always start at a specific index because we always move them to the back of the array
+				UINT32 emptyProbeStartIdx = (UINT32)mProbeMap.size();
+				UINT32 numProbes = (UINT32)mProbeInfos.size();
 
-				LightProbeInfo info;
-				info.flags = LightProbeFlags::Dirty;
-				info.bufferIdx = -1;
-				info.handle = handle;
+				// Find an empty slot to place the probe information at
+				UINT32 compactIdx = -1;
+				for(UINT32 j = emptyProbeStartIdx; j < numProbes; ++j)
+				{
+					if(mProbeInfos[j].flags == LightProbeFlags::Empty)
+					{
+						compactIdx = j;
+						break;
+					}
+				}
 
-				mProbeInfos.push_back(info);
-				mProbePositions.push_back(position);
+				// Found an empty slot
+				if (compactIdx == -1)
+				{
+					compactIdx = (UINT32)mProbeInfos.size();
+
+					LightProbeInfo info;
+					info.flags = LightProbeFlags::Dirty;
+					info.bufferIdx = compactIdx;
+					info.handle = handle;
+
+					mProbeInfos.push_back(info);
+					mProbePositions.push_back(position);
+				}
+				else // No empty slot, add a new one
+				{
+					LightProbeInfo& info = mProbeInfos[compactIdx];
+					info.flags = LightProbeFlags::Dirty;
+					info.handle = handle;
+
+					mProbePositions[compactIdx] = position;
+				}
 
 				mProbeMap[handle] = compactIdx;
+				mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
 			}
 		}
 
+		// Mark slots for removed probes as empty, and move them back to the end of the array
 		for (UINT32 i = 0; i < numRemovedProbes; ++i)
 		{
 			UINT32 idx;
@@ -322,7 +552,25 @@ namespace bs
 				UINT32 compactIdx = iterFind->second;
 				
 				LightProbeInfo& info = mProbeInfos[compactIdx];
-				info.flags = LightProbeFlags::Removed;
+				info.flags = LightProbeFlags::Empty;
+
+				// Move the empty info to the back of the array so all non-empty probes are contiguous
+				// Search from back to current index, and find first non-empty probe to switch switch
+				UINT32 lastSearchIdx = (UINT32)mProbeInfos.size() - 1;
+				while (lastSearchIdx >= (INT32)compactIdx)
+				{
+					LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
+					if (flags != LightProbeFlags::Empty)
+					{
+						std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
+						std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
+
+						mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
+						break;
+					}
+
+					lastSearchIdx--;
+				}
 				
 				mProbeMap.erase(iterFind);
 			}
@@ -335,10 +583,43 @@ namespace bs
 			else
 				gRenderer()->notifyLightProbeVolumeRemoved(this);
 		}
-		else
+	}
+
+	void LightProbeVolume::getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const
+	{
+		UINT32 numActiveProbes = (UINT32)mProbeMap.size();
+		if (numActiveProbes == 0)
+			return;
+
+		output.resize(numActiveProbes);
+
+		LightProbeSHCoefficients* coefficients = bs_stack_alloc<LightProbeSHCoefficients>(numActiveProbes);
+		mCoefficients->readData(0, sizeof(LightProbeSHCoefficients) * numActiveProbes, coefficients);
+
+		for(UINT32 i = 0; i < numActiveProbes; ++i)
 		{
-			if(mIsActive)
-				gRenderer()->notifyLightProbeVolumeUpdated(this);
+			output[i].coefficients = coefficients[mProbeInfos[i].bufferIdx];
+			output[i].handle = mProbeInfos[i].handle;
 		}
+
+		bs_stack_free(coefficients);
+	}
+
+	void LightProbeVolume::resizeCoefficientBuffer(UINT32 count)
+	{
+		GPU_BUFFER_DESC desc;
+		desc.type = GBT_STRUCTURED;
+		desc.elementSize = sizeof(LightProbeSHCoefficients);
+		desc.elementCount = count;
+		desc.usage = GBU_STATIC;
+		desc.format = BF_UNKNOWN;
+		desc.randomGpuWrite = true;
+
+		SPtr<GpuBuffer> newBuffer = GpuBuffer::create(desc);
+		if (mCoefficients)
+			newBuffer->copyData(*mCoefficients, 0, 0, mCoefficients->getSize(), true);
+
+		mCoefficients = newBuffer;
+		mCoeffBufferSize = count;
 	}
 }}

+ 5 - 2
Source/BansheeCore/Source/BsMaterialRTTI.cpp

@@ -16,8 +16,11 @@ namespace bs
 
 		material->initializeTechniques();
 
-		SPtr<MaterialParams> matParams = any_cast<SPtr<MaterialParams>>(material->mRTTIData);
-		material->setParams(matParams);
+		if (material->getNumTechniques() > 0)
+		{
+			SPtr<MaterialParams> matParams = any_cast<SPtr<MaterialParams>>(material->mRTTIData);
+			material->setParams(matParams);
+		}
 
 		material->mRTTIData = nullptr; // Delete temporary data
 	}

Plik diff jest za duży
+ 671 - 256
Source/BansheeCore/Source/BsPixelUtil.cpp


+ 0 - 17
Source/BansheeCore/Source/BsPostProcessSettings.cpp

@@ -1,17 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsPostProcessSettings.h"
-#include "BsPostProcessSettingsRTTI.h"
-
-namespace bs
-{
-	RTTITypeBase* PostProcessSettings::getRTTIStatic()
-	{
-		return PostProcessSettingsRTTI::instance();
-	}
-
-	RTTITypeBase* PostProcessSettings::getRTTI() const
-	{
-		return PostProcessSettings::getRTTIStatic();
-	}
-}

+ 1 - 1
Source/BansheeCore/Source/BsProfilerGPU.cpp

@@ -95,7 +95,7 @@ namespace bs
 		if (mReportCount == 0)
 			BS_EXCEPT(InvalidStateException, "No reports are available.")
 
-			GPUProfilerReport report = mReadyReports[mReportHeadPos];
+		GPUProfilerReport report = mReadyReports[mReportHeadPos];
 
 		mReportHeadPos = (mReportHeadPos + 1) % MAX_QUEUE_ELEMENTS;
 		mReportCount--;

+ 95 - 39
Source/BansheeCore/Source/BsReflectionProbe.cpp

@@ -7,6 +7,7 @@
 #include "BsTexture.h"
 #include "BsRenderer.h"
 #include "BsUUID.h"
+#include "BsIBLUtility.h"
 
 namespace bs
 {
@@ -33,37 +34,100 @@ namespace bs
 		}
 	}
 
-	template <bool Core>
-	TReflectionProbe<Core>::TReflectionProbe()
-		:ReflectionProbeBase()
-	{ }
+	ReflectionProbe::ReflectionProbe()
+		:mLastUpdateHash(0)
+	{
 
-	template <bool Core>
-	TReflectionProbe<Core>::TReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents)
-		: ReflectionProbeBase(type, radius, extents)
-	{ }
+	}
 
-	template <bool Core>
-	void TReflectionProbe<Core>::generate()
+	ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents)
+		: ReflectionProbeBase(type, radius, extents), mLastUpdateHash(0)
 	{
-		if (mCustomTexture != nullptr)
-			_markCoreDirty();
+		// Calling virtual method is okay here because this is the most derived type
+		updateBounds();
 	}
 
-	template class TReflectionProbe<true>;
-	template class TReflectionProbe<false>;
+	ReflectionProbe::~ReflectionProbe()
+	{
+		if (mRendererTask)
+			mRendererTask->cancel();
+	}
 
-	ReflectionProbe::ReflectionProbe()
-		:mLastUpdateHash(0)
+	void ReflectionProbe::capture()
 	{
+		if (mCustomTexture != nullptr)
+			return;
 
+		captureAndFilter();
 	}
 
-	ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents)
-		: TReflectionProbe(type, radius, extents), mLastUpdateHash(0)
+	void ReflectionProbe::filter()
 	{
-		// Calling virtual method is okay here because this is the most derived type
-		updateBounds();
+		if (mCustomTexture == nullptr)
+			return;
+
+		captureAndFilter();
+	}
+
+	void ReflectionProbe::captureAndFilter()
+	{
+		// If previous rendering task exists, cancel it
+		if (mRendererTask != nullptr)
+			mRendererTask->cancel();
+
+		TEXTURE_DESC cubemapDesc;
+		cubemapDesc.type = TEX_TYPE_CUBE_MAP;
+		cubemapDesc.format = PF_RG11B10F;
+		cubemapDesc.width = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
+		cubemapDesc.height = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
+		cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
+		cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+		mFilteredTexture = Texture::_createPtr(cubemapDesc);
+
+		auto renderComplete = [this]()
+		{
+			mRendererTask = nullptr;
+		};
+
+		SPtr<ct::ReflectionProbe> coreProbe = getCore();
+		SPtr<ct::Texture> coreTexture = mFilteredTexture->getCore();
+
+		if (mCustomTexture == nullptr)
+		{
+			auto renderReflProbe = [coreTexture, coreProbe]()
+			{
+				ct::gRenderer()->captureSceneCubeMap(coreTexture, coreProbe->getPosition(), true);
+				ct::gIBLUtility().filterCubemapForSpecular(coreTexture, nullptr);
+
+				coreProbe->mFilteredTexture = coreTexture;
+				ct::gRenderer()->notifyReflectionProbeUpdated(coreProbe.get(), true);
+
+				return true;
+			};
+
+			mRendererTask = ct::RendererTask::create("ReflProbeRender", renderReflProbe);
+		}
+		else
+		{
+			SPtr<ct::Texture> coreCustomTex = mCustomTexture->getCore();
+			auto filterReflProbe = [coreCustomTex, coreTexture, coreProbe]()
+			{
+				ct::gRenderer()->captureSceneCubeMap(coreTexture, coreProbe->getPosition(), true);
+				ct::gIBLUtility().scaleCubemap(coreCustomTex, 0, coreTexture, 0);
+				ct::gIBLUtility().filterCubemapForSpecular(coreTexture, nullptr);
+
+				coreProbe->mFilteredTexture = coreTexture;
+				ct::gRenderer()->notifyReflectionProbeUpdated(coreProbe.get(), true);
+
+				return true;
+			};
+
+			mRendererTask = ct::RendererTask::create("ReflProbeRender", filterReflProbe);
+		}
+
+		mRendererTask->onComplete.connect(renderComplete);
+		ct::gRenderer()->addTask(mRendererTask);
 	}
 
 	SPtr<ct::ReflectionProbe> ReflectionProbe::getCore() const
@@ -104,7 +168,12 @@ namespace bs
 
 	SPtr<ct::CoreObject> ReflectionProbe::createCore() const
 	{
-		ct::ReflectionProbe* probe = new (bs_alloc<ct::ReflectionProbe>()) ct::ReflectionProbe(mType, mRadius, mExtents);
+		SPtr<ct::Texture> filteredTexture;
+		if (mFilteredTexture != nullptr)
+			filteredTexture = mFilteredTexture->getCore();
+
+		ct::ReflectionProbe* probe = new (bs_alloc<ct::ReflectionProbe>()) ct::ReflectionProbe(mType, mRadius, mExtents, 
+			filteredTexture);
 		SPtr<ct::ReflectionProbe> probePtr = bs_shared_ptr<ct::ReflectionProbe>(probe);
 		probePtr->mUUID = mUUID;
 		probePtr->_setThisPtr(probePtr);
@@ -143,14 +212,6 @@ namespace bs
 		dataPtr = rttiWriteElem(mBounds, dataPtr);
 		dataPtr = rttiWriteElem(mUUID, dataPtr);
 
-		SPtr<ct::Texture>* customTexture = new (dataPtr)SPtr<ct::Texture>();
-		if (mCustomTexture.isLoaded(false))
-			*customTexture = mCustomTexture->getCore();
-		else
-			*customTexture = nullptr;
-
-		dataPtr += sizeof(SPtr<ct::Texture>);
-
 		return CoreSyncData(buffer, size);
 	}
 
@@ -184,8 +245,9 @@ namespace bs
 
 	namespace ct
 	{
-	ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents)
-			: TReflectionProbe(type, radius, extents), mRendererId(0)
+	ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents, 
+		const SPtr<Texture>& filteredTexture)
+		: ReflectionProbeBase(type, radius, extents), mRendererId(0), mFilteredTexture(filteredTexture)
 	{
 
 	}
@@ -223,18 +285,12 @@ namespace bs
 		dataPtr = rttiReadElem(mBounds, dataPtr);
 		dataPtr = rttiReadElem(mUUID, dataPtr);
 
-		SPtr<Texture>* texture = (SPtr<Texture>*)dataPtr;
-
-		mCustomTexture = *texture;
-		texture->~SPtr<Texture>();
-		dataPtr += sizeof(SPtr<Texture>);
-
 		updateBounds();
 
 		if (dirtyFlags == (UINT32)ReflectionProbeDirtyFlag::Transform)
 		{
 			if (mIsActive)
-				gRenderer()->notifyReflectionProbeUpdated(this);
+				gRenderer()->notifyReflectionProbeUpdated(this, false);
 		}
 		else
 		{
@@ -262,4 +318,4 @@ namespace bs
 		}
 	}
 
-}}
+}}

+ 26 - 11
Source/BansheeEngine/Source/BsStandardPostProcessSettings.cpp → Source/BansheeCore/Source/BsRenderSettings.cpp

@@ -1,8 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsStandardPostProcessSettings.h"
-#include "BsBinarySerializer.h"
-#include "BsStandardPostProcessSettingsRTTI.h"
+#include "BsRenderSettings.h"
+#include "BsRenderSettingsRTTI.h"
 
 namespace bs
 {
@@ -109,21 +108,22 @@ namespace bs
 		return ScreenSpaceReflectionsSettings::getRTTIStatic();
 	}
 
-	StandardPostProcessSettings::StandardPostProcessSettings()
+	RenderSettings::RenderSettings()
 		: enableAutoExposure(true), enableTonemapping(true), enableFXAA(false), exposureScale(0.0f), gamma(2.2f)
+		, enableHDR(true), enableLighting(true), enableShadows(true), enableIndirectLighting(true), overlayOnly(false)
 	{ }
 
-	RTTITypeBase* StandardPostProcessSettings::getRTTIStatic()
+	RTTITypeBase* RenderSettings::getRTTIStatic()
 	{
-		return StandardPostProcessSettingsRTTI::instance();
+		return RenderSettingsRTTI::instance();
 	}
 
-	RTTITypeBase* StandardPostProcessSettings::getRTTI() const
+	RTTITypeBase* RenderSettings::getRTTI() const
 	{
-		return StandardPostProcessSettings::getRTTIStatic();
+		return RenderSettings::getRTTIStatic();
 	}
 
-	void StandardPostProcessSettings::_getSyncData(UINT8* buffer, UINT32& size)
+	void RenderSettings::_getSyncData(UINT8* buffer, UINT32& size)
 	{
 		UINT32 bufferSize = 0;
 		bufferSize += rttiGetElemSize(enableAutoExposure);
@@ -131,6 +131,11 @@ namespace bs
 		bufferSize += rttiGetElemSize(exposureScale);
 		bufferSize += rttiGetElemSize(gamma);
 		bufferSize += rttiGetElemSize(enableFXAA);
+		bufferSize += rttiGetElemSize(enableHDR);
+		bufferSize += rttiGetElemSize(enableLighting);
+		bufferSize += rttiGetElemSize(enableShadows);
+		bufferSize += rttiGetElemSize(enableIndirectLighting);
+		bufferSize += rttiGetElemSize(overlayOnly);
 
 		bufferSize += rttiGetElemSize(autoExposure.histogramLog2Min);
 		bufferSize += rttiGetElemSize(autoExposure.histogramLog2Max);
@@ -197,6 +202,11 @@ namespace bs
 		writeDst = rttiWriteElem(exposureScale, writeDst);
 		writeDst = rttiWriteElem(gamma, writeDst);
 		writeDst = rttiWriteElem(enableFXAA, writeDst);
+		writeDst = rttiWriteElem(enableHDR, writeDst);
+		writeDst = rttiWriteElem(enableLighting, writeDst);
+		writeDst = rttiWriteElem(enableShadows, writeDst);
+		writeDst = rttiWriteElem(enableIndirectLighting, writeDst);
+		writeDst = rttiWriteElem(overlayOnly, writeDst);
 
 		writeDst = rttiWriteElem(autoExposure.histogramLog2Min, writeDst);
 		writeDst = rttiWriteElem(autoExposure.histogramLog2Max, writeDst);
@@ -246,7 +256,7 @@ namespace bs
 		writeDst = rttiWriteElem(screenSpaceReflections.quality, writeDst);
 	}
 
-	void StandardPostProcessSettings::_setSyncData(UINT8* buffer, UINT32 size)
+	void RenderSettings::_setSyncData(UINT8* buffer, UINT32 size)
 	{
 		char* readSource = (char*)buffer;
 
@@ -255,6 +265,11 @@ namespace bs
 		readSource = rttiReadElem(exposureScale, readSource);
 		readSource = rttiReadElem(gamma, readSource);
 		readSource = rttiReadElem(enableFXAA, readSource);
+		readSource = rttiReadElem(enableHDR, readSource);
+		readSource = rttiReadElem(enableLighting, readSource);
+		readSource = rttiReadElem(enableShadows, readSource);
+		readSource = rttiReadElem(enableIndirectLighting, readSource);
+		readSource = rttiReadElem(overlayOnly, readSource);
 
 		readSource = rttiReadElem(autoExposure.histogramLog2Min, readSource);
 		readSource = rttiReadElem(autoExposure.histogramLog2Max, readSource);
@@ -303,4 +318,4 @@ namespace bs
 		readSource = rttiReadElem(screenSpaceReflections.maxRoughness, readSource);
 		readSource = rttiReadElem(screenSpaceReflections.quality, readSource);
 	}
-}
+}

+ 126 - 0
Source/BansheeCore/Source/BsRenderer.cpp

@@ -7,6 +7,8 @@
 #include "BsMaterial.h"
 #include "BsRendererExtension.h"
 #include "BsRendererManager.h"
+#include "BsCoreObjectManager.h"
+#include "BsSceneManager.h"
 
 namespace bs { namespace ct
 {
@@ -40,8 +42,132 @@ namespace bs { namespace ct
 			return (UINT32)a->getLocation() < (UINT32)b->getLocation();
 	}
 
+	void Renderer::update()
+	{
+		for(auto& entry : mUnresolvedTasks)
+		{
+			if (entry->isComplete())
+				entry->onComplete();
+			else if (!entry->isCanceled())
+				mRemainingUnresolvedTasks.push_back(entry);
+		}
+
+		mUnresolvedTasks.clear();
+		std::swap(mRemainingUnresolvedTasks, mUnresolvedTasks);
+	}
+
+	void Renderer::addTask(const SPtr<RendererTask>& task)
+	{
+		Lock lock(mTaskMutex);
+
+		assert(task->mState != 1 && "Task is already executing, it cannot be executed again until it finishes.");
+		task->mState.store(0); // Reset state in case the task is getting re-queued
+
+		mQueuedTasks.push_back(task);
+		mUnresolvedTasks.push_back(task);
+	}
+
+	void Renderer::processTasks(bool forceAll)
+	{
+		// Move all tasks to the core thread queue
+		{
+			Lock lock(mTaskMutex);
+
+			mRunningTasks.insert(mRunningTasks.end(), mQueuedTasks.begin(), mQueuedTasks.end());
+			mQueuedTasks.clear();
+		}
+
+		do
+		{
+			for (auto& entry : mRunningTasks)
+			{
+				if (entry->isCanceled() || entry->isComplete())
+					continue;
+
+				entry->mState.store(1);
+
+				bool complete = entry->mTaskWorker();
+				if (!complete)
+					mRemainingTasks.push_back(entry);
+				else
+					entry->mState.store(2);
+			}
+
+			mRunningTasks.clear();
+			std::swap(mRemainingTasks, mRunningTasks);
+		} while (forceAll && !mRunningTasks.empty());
+	}
+
+	void Renderer::processTask(RendererTask& task, bool forceAll)
+	{
+		// Move all tasks to the core thread queue
+		{
+			Lock lock(mTaskMutex);
+
+			mRunningTasks.insert(mRunningTasks.end(), mQueuedTasks.begin(), mQueuedTasks.end());
+			mQueuedTasks.clear();
+		}
+
+		bool complete = task.isCanceled() || task.isComplete();
+		while (!complete)
+		{
+			task.mState.store(1);
+
+			complete = task.mTaskWorker();
+			if (complete)
+				task.mState.store(2);
+
+			if (!forceAll)
+				break;
+		}
+	}
+
 	SPtr<Renderer> gRenderer()
 	{
 		return std::static_pointer_cast<Renderer>(RendererManager::instance().getActive());
 	}
+
+	RendererTask::RendererTask(const PrivatelyConstruct& dummy, const String& name, std::function<bool()> taskWorker) 
+		:mName(name), mTaskWorker(taskWorker)
+	{ }
+
+	SPtr<RendererTask> RendererTask::create(const String& name, std::function<bool()> taskWorker)
+	{
+		return bs_shared_ptr_new<RendererTask>(PrivatelyConstruct(), name, taskWorker);
+	}
+
+	bool RendererTask::isComplete() const
+	{
+		return mState.load() == 2;
+	}
+
+	bool RendererTask::isCanceled() const
+	{
+		return mState.load() == 3;
+	}
+
+	void RendererTask::wait()
+	{
+		// Task is about to be executed outside of normal rendering workflow. Make sure to manually sync all changes to
+		// the core thread first.
+		// Note: wait() might only get called during serialization, in which case we might call these methods just once
+		// before a level save, instead for every individual component
+		gSceneManager()._updateCoreObjectTransforms();
+		CoreObjectManager::instance().syncToCore();
+
+		auto worker = [this]()
+		{
+			gRenderer()->processTask(*this, true);
+		};
+
+		gCoreThread().queueCommand(worker);
+		gCoreThread().submit(true);
+
+		// Note: Tigger on complete callback and clear it from Renderer?
+	}
+
+	void RendererTask::cancel()
+	{
+		mState.store(3);
+	}
 }}

+ 3 - 4
Source/BansheeCore/Source/BsRendererMeshData.cpp

@@ -118,7 +118,7 @@ namespace bs
 		Color* colorDst = buffer;
 		for (UINT32 i = 0; i < numElements; i++)
 		{
-			PixelUtil::unpackColor(colorDst, PF_R8G8B8A8, (void*)colorSrc);
+			PixelUtil::unpackColor(colorDst, PF_RGBA8, (void*)colorSrc);
 
 			colorSrc += stride;
 			colorDst++;
@@ -139,7 +139,7 @@ namespace bs
 		Color* colorSrc = buffer;
 		for (UINT32 i = 0; i < numElements; i++)
 		{
-			PixelUtil::packColor(*colorSrc, PF_R8G8B8A8, (void*)colorDst);
+			PixelUtil::packColor(*colorSrc, PF_RGBA8, (void*)colorDst);
 
 			colorSrc++;
 			colorDst += stride;
@@ -154,8 +154,7 @@ namespace bs
 		UINT32 numElements = mMeshData->getNumVertices();
 		assert(numElements * sizeof(UINT32) == size);
 
-		UINT8* colorDst = mMeshData->getElementData(VES_COLOR);
-		memcpy(colorDst, buffer, size);
+		mMeshData->setVertexData(VES_COLOR, (UINT8*)buffer, size);
 	}
 
 	void RendererMeshData::getUV0(Vector2* buffer, UINT32 size)

+ 2 - 2
Source/BansheeCore/Source/BsResources.cpp

@@ -43,7 +43,7 @@ namespace bs
 	{
 		if (!FileSystem::isFile(filePath))
 		{
-			LOGWRN_VERBOSE("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
+			LOGWRN("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
 
 			return HResource();
 		}
@@ -70,7 +70,7 @@ namespace bs
 	{
 		if (!FileSystem::isFile(filePath))
 		{
-			LOGWRN_VERBOSE("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
+			LOGWRN("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
 
 			return HResource();
 		}

+ 23 - 1
Source/BansheeCore/Source/BsSceneManager.cpp

@@ -10,6 +10,7 @@
 #include "BsViewport.h"
 #include "BsGameObjectManager.h"
 #include "BsRenderTarget.h"
+#include "BsLightProbeVolume.h"
 
 namespace bs
 {
@@ -133,6 +134,16 @@ namespace bs
 		mReflectionProbes.erase(probe.get());
 	}
 
+	void SceneManager::_registerLightProbeVolume(const SPtr<LightProbeVolume>& volume, const HSceneObject& so)
+	{
+		mLightProbeVolumes[volume.get()] = SceneLightProbeVolumeData(volume, so);
+	}
+
+	void SceneManager::_unregisterLightProbeVolume(const SPtr<LightProbeVolume>& volume)
+	{
+		mLightProbeVolumes.erase(volume.get());
+	}
+
 	void SceneManager::_notifyMainCameraStateChanged(const SPtr<Camera>& camera)
 	{
 		auto iterFind = std::find_if(mMainCameras.begin(), mMainCameras.end(),
@@ -237,6 +248,17 @@ namespace bs
 				probe->setIsActive(so->getActive());
 			}
 		}
+
+		for (auto& volumePair : mLightProbeVolumes)
+		{
+			SPtr<LightProbeVolume> volume = volumePair.second.volume;
+			HSceneObject so = volumePair.second.sceneObject;
+
+			volume->_updateTransform(so);
+
+			if (so->getActive() != volume->getIsActive())
+				volume->setIsActive(so->getActive());
+		}
 	}
 
 	SceneCameraData SceneManager::getMainCamera() const
@@ -582,4 +604,4 @@ namespace bs
 	{
 		return SceneManager::instance();
 	}
-}
+}

+ 91 - 6
Source/BansheeCore/Source/BsSkybox.cpp

@@ -7,6 +7,7 @@
 #include "BsTexture.h"
 #include "BsRenderer.h"
 #include "BsUUID.h"
+#include "BsIBLUtility.h"
 
 namespace bs
 {
@@ -23,7 +24,81 @@ namespace bs
 	template class TSkybox<false>;
 
 	Skybox::Skybox()
-	{ }
+	{
+		// This shouldn't normally happen, as filtered textures are generated when a radiance texture is assigned, but
+		// we check for it anyway (something could have gone wrong).
+		if(mTexture.isLoaded())
+		{
+			if (mFilteredRadiance == nullptr || mIrradiance == nullptr)
+				filterTexture();
+		}
+	}
+
+	Skybox::~Skybox()
+	{
+		if (mRendererTask != nullptr)
+			mRendererTask->cancel();
+	}
+
+	void Skybox::filterTexture()
+	{
+		// If previous rendering task exists, cancel it
+		if (mRendererTask != nullptr)
+			mRendererTask->cancel();
+
+		{
+			TEXTURE_DESC cubemapDesc;
+			cubemapDesc.type = TEX_TYPE_CUBE_MAP;
+			cubemapDesc.format = PF_RG11B10F;
+			cubemapDesc.width = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
+			cubemapDesc.height = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
+			cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
+			cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+			mFilteredRadiance = Texture::_createPtr(cubemapDesc);
+		}
+
+		{
+			TEXTURE_DESC irradianceCubemapDesc;
+			irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP;
+			irradianceCubemapDesc.format = PF_RG11B10F;
+			irradianceCubemapDesc.width = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
+			irradianceCubemapDesc.height = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
+			irradianceCubemapDesc.numMips = 0;
+			irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+			mIrradiance = Texture::_createPtr(irradianceCubemapDesc);
+		}
+
+		auto renderComplete = [this]()
+		{
+			mRendererTask = nullptr;
+		};
+
+		SPtr<ct::Skybox> coreSkybox = getCore();
+		SPtr<ct::Texture> coreFilteredRadiance = mFilteredRadiance->getCore();
+		SPtr<ct::Texture> coreIrradiance = mIrradiance->getCore();
+
+		auto filterSkybox = [coreFilteredRadiance, coreIrradiance, coreSkybox]()
+		{
+			// Filter radiance
+			ct::gIBLUtility().scaleCubemap(coreSkybox->getTexture(), 0, coreFilteredRadiance, 0);
+			ct::gIBLUtility().filterCubemapForSpecular(coreFilteredRadiance, nullptr);
+
+			coreSkybox->mFilteredRadiance = coreFilteredRadiance;
+
+			// Generate irradiance
+			ct::gIBLUtility().filterCubemapForIrradiance(coreFilteredRadiance, coreIrradiance);
+			coreSkybox->mIrradiance = coreIrradiance;
+
+			return true;
+		};
+
+		mRendererTask = ct::RendererTask::create("SkyboxFilter", filterSkybox);
+
+		mRendererTask->onComplete.connect(renderComplete);
+		ct::gRenderer()->addTask(mRendererTask);
+	}
 
 	SPtr<ct::Skybox> Skybox::getCore() const
 	{
@@ -43,7 +118,15 @@ namespace bs
 
 	SPtr<ct::CoreObject> Skybox::createCore() const
 	{
-		ct::Skybox* skybox = new (bs_alloc<ct::Skybox>()) ct::Skybox();
+		SPtr<ct::Texture> filteredRadiance;
+		if (mFilteredRadiance)
+			filteredRadiance = mFilteredRadiance->getCore();
+
+		SPtr<ct::Texture> irradiance;
+		if (mIrradiance)
+			irradiance = mIrradiance->getCore();
+
+		ct::Skybox* skybox = new (bs_alloc<ct::Skybox>()) ct::Skybox(filteredRadiance, irradiance);
 		SPtr<ct::Skybox> skyboxPtr = bs_shared_ptr<ct::Skybox>(skybox);
 		skyboxPtr->mUUID = mUUID;
 		skyboxPtr->_setThisPtr(skyboxPtr);
@@ -81,6 +164,9 @@ namespace bs
 
 	void Skybox::_markCoreDirty(SkyboxDirtyFlag flags)
 	{
+		if(flags == SkyboxDirtyFlag::Texture)
+			filterTexture();
+
 		markCoreDirty((UINT32)flags);
 	}
 
@@ -96,7 +182,8 @@ namespace bs
 
 	namespace ct
 	{
-		Skybox::Skybox()
+		Skybox::Skybox(const SPtr<Texture>& filteredRadiance, const SPtr<Texture>& irradiance)
+			:mFilteredRadiance(filteredRadiance), mIrradiance(irradiance)
 		{ }
 
 		Skybox::~Skybox()
@@ -138,9 +225,7 @@ namespace bs
 			}
 			else
 			{
-				if (dirtyFlags == SkyboxDirtyFlag::Texture)
-					gRenderer()->notifySkyboxTextureChanged(this);
-				else
+				if (dirtyFlags != SkyboxDirtyFlag::Texture)
 				{
 					gRenderer()->notifySkyboxRemoved(this);
 					gRenderer()->notifySkyboxAdded(this);

+ 1 - 1
Source/BansheeCore/Source/BsTextureImportOptions.cpp

@@ -6,7 +6,7 @@
 namespace bs
 {
 	TextureImportOptions::TextureImportOptions()
-		: mFormat(PF_R8G8B8A8), mGenerateMips(false), mMaxMip(0), mCPUCached(false), mSRGB(false), mCubemap(false)
+		: mFormat(PF_RGBA8), mGenerateMips(false), mMaxMip(0), mCPUCached(false), mSRGB(false), mCubemap(false)
 		, mCubemapSourceType(CubemapSourceType::Faces)
 	{ }
 

+ 4 - 4
Source/BansheeCore/Source/BsTextureManager.cpp

@@ -90,13 +90,13 @@ namespace bs
 		desc.type = TEX_TYPE_2D;
 		desc.width = 2;
 		desc.height = 2;
-		desc.format = PF_R8G8B8A8;
+		desc.format = PF_RGBA8;
 		desc.usage = TU_STATIC;
 
 		// White built-in texture
 		SPtr<Texture> whiteTexture = createTexture(desc);
 
-		SPtr<PixelData> whitePixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> whitePixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 		whitePixelData->setColorAt(Color::White, 0, 0);
 		whitePixelData->setColorAt(Color::White, 0, 1);
 		whitePixelData->setColorAt(Color::White, 1, 0);
@@ -108,7 +108,7 @@ namespace bs
 		// Black built-in texture
 		SPtr<Texture> blackTexture = createTexture(desc);
 
-		SPtr<PixelData> blackPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> blackPixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 		blackPixelData->setColorAt(Color::Black, 0, 0);
 		blackPixelData->setColorAt(Color::Black, 0, 1);
 		blackPixelData->setColorAt(Color::Black, 1, 0);
@@ -119,7 +119,7 @@ namespace bs
 
 		// Normal (Y = Up) built-in texture
 		SPtr<Texture> normalTexture = createTexture(desc);
-		SPtr<PixelData> normalPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> normalPixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 
 		Color encodedNormal(0.5f, 0.5f, 1.0f);
 		normalPixelData->setColorAt(encodedNormal, 0, 0);

+ 1 - 1
Source/BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -192,7 +192,7 @@ namespace bs
 
 	void Platform::setIcon(const PixelData& pixelData)
 	{
-		SPtr<PixelData> resizedData = PixelData::create(32, 32, 1, PF_R8G8B8A8);
+		SPtr<PixelData> resizedData = PixelData::create(32, 32, 1, PF_RGBA8);
 		PixelUtil::scale(pixelData, *resizedData);
 
 		Vector<Color> pixels = pixelData.getColors();

+ 121 - 68
Source/BansheeD3D11RenderAPI/Source/BsD3D11Mappings.cpp

@@ -413,39 +413,39 @@ namespace bs { namespace ct
 		case DXGI_FORMAT_R32G32B32A32_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R32G32B32A32_FLOAT:
-			return PF_FLOAT32_RGBA;
+			return PF_RGBA32F;
 		case DXGI_FORMAT_R32G32B32A32_UINT:
-			return PF_UNKNOWN;
+			return PF_RGBA32U;
 		case DXGI_FORMAT_R32G32B32A32_SINT:
-			return PF_UNKNOWN;
+			return PF_RGBA32I;
 		case DXGI_FORMAT_R32G32B32_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R32G32B32_FLOAT:
-			return PF_FLOAT32_RGB;
+			return PF_RGB32F;
 		case DXGI_FORMAT_R32G32B32_UINT:
-			return PF_UNKNOWN;
+			return PF_RGB32U;
 		case DXGI_FORMAT_R32G32B32_SINT:
-			return PF_UNKNOWN;
+			return PF_RGB32I;
 		case DXGI_FORMAT_R16G16B16A16_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R16G16B16A16_FLOAT:
-			return PF_FLOAT16_RGBA;
+			return PF_RGBA16F;
 		case DXGI_FORMAT_R16G16B16A16_UNORM:
-			return PF_UNKNOWN;
+			return PF_RGBA16;
 		case DXGI_FORMAT_R16G16B16A16_UINT:
-			return PF_UNKNOWN;
+			return PF_RGBA16U;
 		case DXGI_FORMAT_R16G16B16A16_SNORM:
-			return PF_UNKNOWN;
+			return PF_RGBA16S;
 		case DXGI_FORMAT_R16G16B16A16_SINT:
-			return PF_UNKNOWN;
+			return PF_RGBA16I;
 		case DXGI_FORMAT_R32G32_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R32G32_FLOAT:
-			return PF_FLOAT32_RG;
+			return PF_RG32F;
 		case DXGI_FORMAT_R32G32_UINT:
-			return PF_UNKNOWN;
+			return PF_RG32U;
 		case DXGI_FORMAT_R32G32_SINT:
-			return PF_UNKNOWN;
+			return PF_RG32I;
 		case DXGI_FORMAT_R32G8X24_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
@@ -457,44 +457,44 @@ namespace bs { namespace ct
 		case DXGI_FORMAT_R10G10B10A2_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R10G10B10A2_UNORM:
-			return PF_UNORM_R10G10B10A2;
+			return PF_RGB10A2;
 		case DXGI_FORMAT_R10G10B10A2_UINT:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R11G11B10_FLOAT:
-			return PF_FLOAT_R11G11B10;
+			return PF_RG11B10F;
 		case DXGI_FORMAT_R8G8B8A8_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R8G8B8A8_UNORM:
 		case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
-			return PF_R8G8B8A8;
+			return PF_RGBA8;
 		case DXGI_FORMAT_R8G8B8A8_UINT:
-			return PF_UNKNOWN;
+			return PF_RGBA8U;
 		case DXGI_FORMAT_R8G8B8A8_SNORM:
-			return PF_UNKNOWN;
+			return PF_RGBA8S;
 		case DXGI_FORMAT_R8G8B8A8_SINT:
-			return PF_UNKNOWN;
+			return PF_RGBA8I;
 		case DXGI_FORMAT_R16G16_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R16G16_FLOAT:
-			return PF_FLOAT16_RG;
+			return PF_RG16F;
 		case DXGI_FORMAT_R16G16_UNORM:
-			return PF_UNKNOWN;
+			return PF_RG16;
 		case DXGI_FORMAT_R16G16_UINT:
-			return PF_UNKNOWN;
+			return PF_RG16U;
 		case DXGI_FORMAT_R16G16_SNORM:
-			return PF_UNKNOWN;
+			return PF_RG16S;
 		case DXGI_FORMAT_R16G16_SINT:
-			return PF_UNKNOWN;
+			return PF_RG16I;
 		case DXGI_FORMAT_R32_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_D32_FLOAT:
 			return PF_D32;
 		case DXGI_FORMAT_R32_FLOAT:
-			return PF_FLOAT32_R;
+			return PF_R32F;
 		case DXGI_FORMAT_R32_UINT:
-			return PF_UNKNOWN;
+			return PF_R32U;
 		case DXGI_FORMAT_R32_SINT:
-			return PF_UNKNOWN;
+			return PF_R32I;
 		case DXGI_FORMAT_R24G8_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_D24_UNORM_S8_UINT:
@@ -506,37 +506,37 @@ namespace bs { namespace ct
 		case DXGI_FORMAT_R8G8_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R8G8_UNORM:
-			return PF_R8G8;
+			return PF_RG8;
 		case DXGI_FORMAT_R8G8_UINT:
-			return PF_UNKNOWN;
+			return PF_RG8U;
 		case DXGI_FORMAT_R8G8_SNORM:
-			return PF_UNKNOWN;
+			return PF_RG8S;
 		case DXGI_FORMAT_R8G8_SINT:
-			return PF_UNKNOWN;
+			return PF_RG8I;
 		case DXGI_FORMAT_R16_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R16_FLOAT:
-			return PF_FLOAT16_R;
+			return PF_R16F;
 		case DXGI_FORMAT_D16_UNORM:
 			return PF_D16;
 		case DXGI_FORMAT_R16_UNORM:
-			return PF_UNKNOWN;
+			return PF_R16;
 		case DXGI_FORMAT_R16_UINT:
-			return PF_UNKNOWN;
+			return PF_R16U;
 		case DXGI_FORMAT_R16_SNORM:
-			return PF_UNKNOWN;
+			return PF_R16S;
 		case DXGI_FORMAT_R16_SINT:
-			return PF_UNKNOWN;
+			return PF_R16I;
 		case DXGI_FORMAT_R8_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R8_UNORM:
 			return PF_R8;
 		case DXGI_FORMAT_R8_UINT:
-			return PF_UNKNOWN;
+			return PF_R8U;
 		case DXGI_FORMAT_R8_SNORM:
-			return PF_UNKNOWN;
+			return PF_R8S;
 		case DXGI_FORMAT_R8_SINT:
-			return PF_UNKNOWN;
+			return PF_R8I;
 		case DXGI_FORMAT_A8_UNORM:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R1_UNORM:
@@ -589,9 +589,12 @@ namespace bs { namespace ct
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_B5G5R5A1_UNORM:
 			return PF_UNKNOWN;
+		case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
+		case DXGI_FORMAT_B8G8R8X8_UNORM:
+			return PF_BGR8;
 		case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
 		case DXGI_FORMAT_B8G8R8A8_UNORM:
-			return PF_B8G8R8A8;
+			return PF_BGRA8;
 		default:
 			return PF_UNKNOWN;
 		}
@@ -603,35 +606,91 @@ namespace bs { namespace ct
 		{
 		case PF_R8:
 			return DXGI_FORMAT_R8_UNORM;
-		case PF_R8G8:
+		case PF_R8S:
+			return DXGI_FORMAT_R8_SNORM;
+		case PF_R8I:
+			return DXGI_FORMAT_R8_SINT;
+		case PF_R8U:
+			return DXGI_FORMAT_R8_UINT;
+		case PF_RG8:
 			return DXGI_FORMAT_R8G8_UNORM; 
-		case PF_R8G8B8:
-		case PF_B8G8R8:
-			return DXGI_FORMAT_UNKNOWN;
-		case PF_R8G8B8A8:
+		case PF_RG8S:
+			return DXGI_FORMAT_R8G8_SNORM; 
+		case PF_RG8I:
+			return DXGI_FORMAT_R8G8_SINT; 
+		case PF_RG8U:
+			return DXGI_FORMAT_R8G8_UINT; 
+		case PF_BGR8:
+			if (gamma)
+				return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
+			return DXGI_FORMAT_B8G8R8X8_UNORM;
+		case PF_RGB8:
+		case PF_RGBA8:
 			if (gamma)
 				return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
 			return DXGI_FORMAT_R8G8B8A8_UNORM;
-		case PF_B8G8R8A8:
+		case PF_RGBA8I:
+			return DXGI_FORMAT_R8G8B8A8_SINT;
+		case PF_RGBA8U:
+			return DXGI_FORMAT_R8G8B8A8_UINT;
+		case PF_RGBA8S:
+			return DXGI_FORMAT_R8G8B8A8_SNORM;
+		case PF_BGRA8:
 			if (gamma)
 				return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
 			return DXGI_FORMAT_B8G8R8A8_UNORM;
-		case PF_FLOAT16_R:
+		case PF_R16F:
 			return DXGI_FORMAT_R16_FLOAT;
-		case PF_FLOAT16_RG:
+		case PF_RG16F:
 			return DXGI_FORMAT_R16G16_FLOAT;
-		case PF_FLOAT16_RGB:
-			return DXGI_FORMAT_UNKNOWN;
-		case PF_FLOAT16_RGBA:
+		case PF_RGBA16F:
 			return DXGI_FORMAT_R16G16B16A16_FLOAT;
-		case PF_FLOAT32_R:
+		case PF_R32F:
 			return DXGI_FORMAT_R32_FLOAT;
-		case PF_FLOAT32_RG:
+		case PF_RG32F:
 			return DXGI_FORMAT_R32G32_FLOAT;
-		case PF_FLOAT32_RGB:
+		case PF_RGB32F:
 			return DXGI_FORMAT_R32G32B32_FLOAT;
-		case PF_FLOAT32_RGBA:
+		case PF_RGBA32F:
 			return DXGI_FORMAT_R32G32B32A32_FLOAT;
+		case PF_R16I:
+			return DXGI_FORMAT_R16_SINT;
+		case PF_RG16I:
+			return DXGI_FORMAT_R16G16_SINT;
+		case PF_RGBA16I:
+			return DXGI_FORMAT_R16G16B16A16_SINT;
+		case PF_R16U:
+			return DXGI_FORMAT_R16_UINT;
+		case PF_RG16U:
+			return DXGI_FORMAT_R16G16_UINT;
+		case PF_RGBA16U:
+			return DXGI_FORMAT_R16G16B16A16_UINT;
+		case PF_R32I:
+			return DXGI_FORMAT_R32_SINT;
+		case PF_RG32I:
+			return DXGI_FORMAT_R32G32_SINT;
+		case PF_RGB32I:
+			return DXGI_FORMAT_R32G32B32_SINT;
+		case PF_R32U:
+			return DXGI_FORMAT_R32_UINT;
+		case PF_RG32U:
+			return DXGI_FORMAT_R32G32_UINT;
+		case PF_RGB32U:
+			return DXGI_FORMAT_R32G32B32_UINT;
+		case PF_RGBA32U:
+			return DXGI_FORMAT_R32G32B32A32_UINT;
+		case PF_R16S:
+			return DXGI_FORMAT_R16_SNORM;
+		case PF_RG16S:
+			return DXGI_FORMAT_R16G16_SNORM;
+		case PF_RGBA16S:
+			return DXGI_FORMAT_R16G16B16A16_SNORM;
+		case PF_R16:
+			return DXGI_FORMAT_R16_UNORM;
+		case PF_RG16:
+			return DXGI_FORMAT_R16G16_UNORM;
+		case PF_RGBA16:
+			return DXGI_FORMAT_R16G16B16A16_UNORM;
 		case PF_BC1:
 		case PF_BC1a:
 			if(gamma)
@@ -663,9 +722,9 @@ namespace bs { namespace ct
 			return DXGI_FORMAT_D32_FLOAT;
 		case PF_D16:
 			return DXGI_FORMAT_D16_UNORM;
-		case PF_FLOAT_R11G11B10:
+		case PF_RG11B10F:
 			return DXGI_FORMAT_R11G11B10_FLOAT;
-		case PF_UNORM_R10G10B10A2:
+		case PF_RGB10A2:
 			return DXGI_FORMAT_R10G10B10A2_UNORM;
 		case PF_UNKNOWN:
 		default:
@@ -765,14 +824,8 @@ namespace bs { namespace ct
 		// Check for formats that are not supported at all by DX11
 		switch(pf)
 		{
-		case PF_R8G8B8:
-			pf = PF_R8G8B8A8;
-			break;
-		case PF_B8G8R8:
-			pf = PF_B8G8R8A8;
-			break;
-		case PF_FLOAT16_RGB:
-			pf = PF_FLOAT16_RGBA;
+		case PF_RGB8:
+			pf = PF_RGBA8;
 			break;
 		default:
 			break;
@@ -783,8 +836,8 @@ namespace bs { namespace ct
 		{
 			switch (pf)
 			{
-			case PF_B8G8R8A8:
-				pf = PF_R8G8B8A8;
+			case PF_BGRA8:
+				pf = PF_RGBA8;
 				break;
 			default:
 				break;

+ 1 - 1
Source/BansheeD3D11RenderAPI/Source/BsD3D11RenderWindow.cpp

@@ -592,7 +592,7 @@ namespace bs
 		mDevice.getImmediateContext()->Map(tempTexture, 0,D3D11_MAP_READ, 0, &mappedTex2D);
 
 		// Copy the the texture to the dest
-		PixelData src(getProperties().getWidth(), getProperties().getHeight(), 1, PF_R8G8B8A8);
+		PixelData src(getProperties().getWidth(), getProperties().getHeight(), 1, PF_RGBA8);
 		src.setExternalBuffer((UINT8*)mappedTex2D.pData);
 		PixelUtil::bulkPixelConversion(src, dst);
 

+ 3 - 1
Source/BansheeEditor/Source/BsEditorWindowBase.cpp

@@ -88,7 +88,9 @@ namespace bs
 		mCamera->setNearClipDistance(5);
 		mCamera->setAspectRatio(1.0f);
 		mCamera->setLayers(0);
-		mCamera->setFlag(CameraFlag::Overlay, true);
+
+		SPtr<RenderSettings> settings = mCamera->getRenderSettings();
+		settings->overlayOnly = true;
 
 		mGUI = mSceneObject->addComponent<CGUIWidget>(mCamera);
 		mGUI->setDepth(128);

+ 0 - 7
Source/BansheeEngine/CMakeSources.cmake

@@ -87,9 +87,6 @@ set(BS_BANSHEEENGINE_INC_RENDERER
 	"Include/BsRendererMaterialManager.h"
 	"Include/BsRenderQueue.h"
 	"Include/BsRendererUtility.h"
-	"Include/BsStandardPostProcessSettings.h"	
-	"Include/BsLightProbeCache.h"
-	"Include/BsIBLUtility.h"
 )
 
 set(BS_BANSHEEENGINE_SRC_RTTI
@@ -136,7 +133,6 @@ set(BS_BANSHEEENGINE_INC_RTTI
 	"Include/BsCGUIWidgetRTTI.h"
 	"Include/BsGameSettingsRTTI.h"
 	"Include/BsResourceMappingRTTI.h"
-	"Include/BsStandardPostProcessSettingsRTTI.h"
 )
 
 set(BS_BANSHEEENGINE_INC_NOFILTER
@@ -176,9 +172,6 @@ set(BS_BANSHEEENGINE_SRC_RENDERER
 	"Source/BsRendererMaterialManager.cpp"
 	"Source/BsRenderQueue.cpp"
 	"Source/BsRendererUtility.cpp"
-	"Source/BsStandardPostProcessSettings.cpp"
-	"Source/BsLightProbeCache.cpp"
-	"Source/BsIBLUtility.cpp"
 )
 
 set(BS_BANSHEEENGINE_SRC_INPUT

+ 0 - 94
Source/BansheeEngine/Include/BsLightProbeCache.h

@@ -1,94 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "BsParamBlocks.h"
-#include "BsRendererMaterial.h"
-#include "BsModule.h"
-
-namespace bs { namespace ct
-{
-	/** @addtogroup Renderer-Engine-Internal
-	 *  @{
-	 */
-
-	/** 
-	 * Keeps a cache of all textures used by light probes so they may be generated during development time, and then just
-	 * loaded during runtime.
-	 */
-	class BS_EXPORT LightProbeCache : public Module<LightProbeCache>
-	{
-	public:
-		~LightProbeCache();
-
-		/** Initializes the cache with a path at which it can find the saved textures. */
-		void initCache(const Path& path);
-
-		/** Notifies the manager that the probe with the provided UUID is dirty and needs to be re-created. */
-		void notifyDirty(const String& uuid);
-
-		/** 
-		 * Checks if the radiance texture for the probe with the specified UUID is marked as dirty, or if it hasn't been
-		 * cached yet at all. 
-		 */
-		bool isRadianceDirty(const String& uuid) const;
-
-		/** 
-		 * Checks if the radiance texture for the probe with the specified UUID is marked as dirty, or if it hasn't been
-		 * cached yet at all. 
-		 */
-		bool isIrradianceDirty(const String& uuid) const;
-
-		/** Sets a cached radiance texture and associates it with the provided UUID. */
-		void setCachedRadianceTexture(const String& uuid, const SPtr<Texture>& texture);
-
-		/** Sets a cached irradiance texture and associates it with the provided UUID. */
-		void setCachedIrradianceTexture(const String& uuid, const SPtr<Texture>& texture);
-
-		/** 
-		 * Returns an radiance texture that was assigned to the specified UUID with setCachedRadianceTexture() was
-		 * called. 
-		 */
-		SPtr<Texture> getCachedRadianceTexture(const String& uuid) const;
-
-		/** 
-		 * Returns an irradiance texture that was assigned to the specified UUID with setCachedIrradianceTexture() was
-		 * called. 
-		 */
-		SPtr<Texture> getCachedIrradianceTexture(const String& uuid) const;
-
-		/** Unloads in-memory data for a probe with the specified UUID. */
-		void unloadCachedTexture(const String& uuid);
-
-		/** Saves all cached textures in a folder at the provided path. */
-		void saveCache(const Path& path);
-
-	private:
-		/** Information about a single light probe texture. */
-		struct TextureInfo
-		{
-			SPtr<Texture> texture = nullptr;
-			bool dirty = false;
-			bool needsSaving = false;
-		};
-
-		/** Information about textures for a single light probe. */
-		struct ProbeInfo
-		{
-			TextureInfo radiance;
-			TextureInfo irradiance;
-		};
-
-		/** Loads a saved cached texture at the specified path. */
-		SPtr<Texture> loadCachedTexture(const Path& path) const;
-
-		/** Saves cached texture data at the specified path. */
-		void saveCachedTexture(const TextureInfo& texInfo, const Path& path);
-
-		Path mDataPath;
-		mutable UnorderedMap<String, ProbeInfo> mProbeInfos;
-	};
-
-	/** @} */
-}}

+ 7 - 8
Source/BansheeEngine/Include/BsPrerequisites.h

@@ -227,13 +227,12 @@ namespace bs
 		/* TID_CLight = 30012, */
 		TID_GameSettings = 30013,
 		TID_ResourceMapping = 30014,
-		TID_StandardPostProcessSettings = 30015,
-		TID_AutoExposureSettings = 30016,
-		TID_TonemappingSettings = 30017,
-		TID_WhiteBalanceSettings = 30018,
-		TID_ColorGradingSettings = 30019,
-		TID_DepthOfFieldSettings = 30020,
-		TID_AmbientOcclusionSettings = 30021,
-		TID_ScreenSpaceReflectionsSettings = 30022
+		//TID_AutoExposureSettings = 30016,
+		//TID_TonemappingSettings = 30017,
+		//TID_WhiteBalanceSettings = 30018,
+		//TID_ColorGradingSettings = 30019,
+		//TID_DepthOfFieldSettings = 30020,
+		//TID_AmbientOcclusionSettings = 30021,
+		//TID_ScreenSpaceReflectionsSettings = 30022
 	};
 }

+ 166 - 8
Source/BansheeEngine/Include/BsRendererMaterial.h

@@ -16,10 +16,10 @@
 	public:																	\
 	static void _initMetaData()												\
 	{																		\
-		_initDefines(mMetaData.defines);									\
+		_initVariations(mMetaData.variations);								\
 		RendererMaterialManager::_registerMaterial(&mMetaData, path);		\
 	};																		\
-	static void _initDefines(ShaderDefines& defines);
+	static void _initVariations(ShaderVariations& variations);
 
 /** @} */
 
@@ -29,17 +29,131 @@ namespace bs { namespace ct
 	 *  @{
 	 */
 
+	/** 
+	 * Contains information about a single variation of a RendererMaterial. Each variation can have a separate set of
+	 * \#defines that control shader compilation.
+	 */
+	class BS_EXPORT ShaderVariation
+	{
+	public:
+		/** Possible types of a variation parameter. */
+		enum ParamType
+		{
+			Int,
+			UInt,
+			Float,
+			Bool
+		};
+
+		/** Name, type and value of a variation parameter. */
+		struct Param
+		{
+			Param()
+				:i(0), type(Int)
+			{ }
+
+			Param(const String& name, INT32 val)
+				:i(val), name(name), type(Int)
+			{ }
+
+			Param(const String& name, UINT32 val)
+				:ui(val), name(name), type(Int)
+			{ }
+
+			Param(const String& name, float val)
+				:f(val), name(name), type(Float)
+			{ }
+
+			Param(const String& name, bool val)
+				:i(val ? 1 : 0), name(name), type(Bool)
+			{ }
+
+			union
+			{
+				INT32 i;
+				UINT32 ui;
+				float f;
+			};
+
+			String name;
+			ParamType type;
+		};
+
+		ShaderVariation() { }
+		
+		/** Creates a new shader variation with the specified parameters. */
+		ShaderVariation(const SmallVector<Param, 4>& params);
+
+		/** 
+		 * Returns the value of a signed integer parameter with the specified name. Returns 0 if the parameter cannot be 
+		 * found.
+		 */
+		INT32 getInt(const String& name);
+
+		/** 
+		 * Returns the value of a unsigned integer parameter with the specified name. Returns 0 if the parameter cannot be
+		 * found.
+		 */
+		UINT32 getUInt(const String& name);
+
+		/** Returns the value of a float parameter with the specified name. Returns 0 if the parameter cannot be found.  */
+		float getFloat(const String& name);
+
+		/** 
+		 * Returns the value of a boolean parameter with the specified name. Returns false if the parameter cannot be 
+		 * found.
+		 */
+		bool getBool(const String& name);
+
+		/** Converts all the variation parameters in a ShaderDefines object, that may be consumed by the shader compiler. */
+		ShaderDefines getDefines() const;
+
+		/** 
+		 * Returns a unique index of this variation, relative to all other variations registered in ShaderVariations object.
+		 */
+		UINT32 getIdx() const { return mIdx;  }
+
+		/** Returns all the variation parameters. */
+		const UnorderedMap<String, Param>& getParams() const { return mParams; }
+
+	private:
+		friend class ShaderVariations;
+
+		UnorderedMap<String, Param> mParams;
+		UINT32 mIdx = -1;
+	};
+
+	/** A container for all variations of a single RendererMaterial. */
+	class BS_EXPORT ShaderVariations
+	{
+	public:
+		/** Registers a new variation. */
+		void add(ShaderVariation& variation);
+
+		/** Returns a variation at the specified index. Variations are indexed sequentially as they are added. */
+		const ShaderVariation& get(UINT32 idx) { return mVariations[idx]; }
+
+		/** Returns a list of all variations. */
+		const SmallVector<ShaderVariation, 4>& getVariations() const { return mVariations; }
+
+	private:
+		SmallVector<ShaderVariation, 4> mVariations;
+		UINT32 mNextIdx = 0;
+	};
+
 	/**	Contains data common to all render material instances of a specific type. */
 	struct RendererMaterialMetaData
 	{
-		SPtr<Shader> shader;
-		ShaderDefines defines;
+		SmallVector<SPtr<Shader>, 4> shaders;
+		SmallVector<RendererMaterialBase*, 4> instances;
+		ShaderVariations variations;
 	};
 
 	/**	Base class for all RendererMaterial instances, containing common data and methods. */
 	class BS_EXPORT RendererMaterialBase
 	{
 	public:
+		RendererMaterialBase() { }
 		virtual ~RendererMaterialBase() { }
 
 		/**	Returns the internal material. */
@@ -48,11 +162,17 @@ namespace bs { namespace ct
 		/** Returns the internal parameter set containing GPU bindable parameters. */
 		SPtr<GpuParamsSet> getParamsSet() const { return mParamsSet; }
 
+		/** 
+		 * Helper field to be set before construction. Identifiers the variation of the material to initialize this 
+		 * object with. 
+		 */
+		UINT32 _varIdx;
 	protected:
 		friend class RendererMaterialManager;
 
 		SPtr<Material> mMaterial;
 		SPtr<GpuParamsSet> mParamsSet;
+		ShaderVariation mVariation;
 	};
 
 	/**	Helper class to initialize all renderer materials as soon as the library is loaded. */
@@ -74,16 +194,54 @@ namespace bs { namespace ct
 	class RendererMaterial : public RendererMaterialBase
 	{
 	public:
+		virtual ~RendererMaterial() { }
+
+		/** 
+		 * Retrieves an instance of this renderer material. If material has multiple variations the first available
+		 * variation will be returned. 
+		 */
+		static T* get()
+		{
+			if(mMetaData.instances[0] == nullptr)
+			{
+				RendererMaterialBase* mat = bs_alloc<T>();
+				mat->_varIdx = 0;
+				new (mat) T();
+				
+				mMetaData.instances[0] = mat;
+			}
+
+			return (T*)mMetaData.instances[0];
+		}
+
+		/** Retrieves an instance of a particular variation of this renderer material. */
+		static T* get(const ShaderVariation& variation)
+		{
+			UINT32 varIdx = variation.getIdx();
+
+			if(mMetaData.instances[varIdx] == nullptr)
+			{
+				RendererMaterialBase* mat = bs_alloc<T>();
+				mat->_varIdx = varIdx;
+				new (mat) T();
+				
+				mMetaData.instances[varIdx] = mat;
+			}
+
+			return (T*)mMetaData.instances[varIdx];
+		}
+
+	protected:
 		RendererMaterial()
 		{
 			mInitOnStart.instantiate();
-			mMaterial = Material::create(mMetaData.shader);
+			mMaterial = Material::create(mMetaData.shaders[_varIdx]);
 			mParamsSet = mMaterial->createParamsSet();
-		}
 
-		virtual ~RendererMaterial() { }
+			if(!mMetaData.variations.getVariations().empty())
+				mVariation = mMetaData.variations.get(_varIdx);
+		}
 
-	protected:
 		friend class RendererMaterialManager;
 
 		static RendererMaterialMetaData mMetaData;

+ 1 - 0
Source/BansheeEngine/Include/BsRendererMaterialManager.h

@@ -26,6 +26,7 @@ namespace bs
 			ct::RendererMaterialMetaData* metaData;
 			Path shaderPath;
 			Path resourcePath;
+			UINT32 variationIdx;
 		};
 
 	public:

+ 52 - 21
Source/BansheeEngine/Include/BsRendererUtility.h

@@ -8,6 +8,7 @@
 #include "BsVector2I.h"
 #include "BsRect2I.h"
 #include "BsRendererMaterial.h"
+#include "BsParamBlocks.h"
 
 namespace bs { namespace ct
 {
@@ -15,18 +16,8 @@ namespace bs { namespace ct
 	 *  @{
 	 */
 
-	/** 
-	 * Shader that copies a source texture into a render target, and optionally resolves it. 
-	 * 
-	 * @tparam	MSAA_COUNT		Number of MSAA samples in the input texture. If larger than 1 the texture will be resolved
-	 *							before written to the destination.
-	 * @tparam	IS_COLOR		If true the input is assumed to be a 4-component color texture. If false it is assumed
-	 *							the input is a 1-component depth texture. This controls how is the texture resolve and is
-	 *							only relevant if MSAA_COUNT > 1. Color texture MSAA samples will be averaged, while for
-	 *							depth textures the minimum of all samples will be used.
-	 */
-	template<int MSAA_COUNT, bool IS_COLOR = true>
-	class BlitMat : public RendererMaterial<BlitMat<MSAA_COUNT, IS_COLOR>>
+	/** Shader that copies a source texture into a render target, and optionally resolves it. */
+	class BlitMat : public RendererMaterial<BlitMat>
 	{
 		RMAT_DEF("Blit.bsl");
 
@@ -35,8 +26,50 @@ namespace bs { namespace ct
 
 		/** Updates the parameter buffers used by the material. */
 		void setParameters(const SPtr<Texture>& source);
+
+		/** 
+		 * Returns the material variation matching the provided parameters.
+		 *
+		 * @param	msaaCount		Number of MSAA samples in the input texture. If larger than 1 the texture will be resolved
+		 *							before written to the destination.
+		 * @param	isColor			If true the input is assumed to be a 4-component color texture. If false it is assumed
+		 *							the input is a 1-component depth texture. This controls how is the texture resolve and is
+		 *							only relevant if @p msaaCount > 1. Color texture MSAA samples will be averaged, while for
+		 *							depth textures the minimum of all samples will be used.
+		 */
+		static BlitMat* getVariation(UINT32 msaaCount, bool isColor);
 	private:
 		MaterialParamTexture mSource;
+
+		static ShaderVariation VAR_1MSAA_Color;
+		static ShaderVariation VAR_2MSAA_Color;
+		static ShaderVariation VAR_4MSAA_Color;
+		static ShaderVariation VAR_8MSAA_Color;
+
+		static ShaderVariation VAR_1MSAA_Depth;
+		static ShaderVariation VAR_2MSAA_Depth;
+		static ShaderVariation VAR_4MSAA_Depth;
+		static ShaderVariation VAR_8MSAA_Depth;
+	};
+
+	BS_PARAM_BLOCK_BEGIN(ClearParamDef)
+		BS_PARAM_BLOCK_ENTRY(INT32, gClearValue)
+	BS_PARAM_BLOCK_END
+
+	extern ClearParamDef gClearParamDef;
+
+	/** Shader that clears the currently bound render target to an integer value. */
+	class ClearMat : public RendererMaterial<ClearMat>
+	{
+		RMAT_DEF("Clear.bsl");
+
+	public:
+		ClearMat();
+
+		/** Executes the material on the currently bound render target, clearing to to @p value. */
+		void execute(UINT32 value);
+	private:
+		SPtr<GpuParamBlockBuffer> mParamBuffer;
 	};
 
 	/**
@@ -165,6 +198,12 @@ namespace bs { namespace ct
 			drawScreenQuad(uv, textureSize, numInstances);
 		}
 
+		/** 
+		 * Clears the currently bound render target to the provided integer value. This is similar to 
+		 * RenderAPI::clearRenderTarget(), except it supports integer clears.
+		 */
+		void clear(UINT32 value);
+
 		/** Returns a stencil mesh used for a radial light (a unit sphere). */
 		SPtr<Mesh> getRadialLightStencil() const { return mPointLightStencilMesh; }
 
@@ -182,18 +221,10 @@ namespace bs { namespace ct
 		SPtr<Mesh> mPointLightStencilMesh;
 		SPtr<Mesh> mSpotLightStencilMesh;
 		SPtr<Mesh> mSkyBoxMesh;
-
-		BlitMat<1, true> mBlitMat_Color_NoMSAA;
-		BlitMat<2, true> mBlitMat_Color_MSAA2x;
-		BlitMat<4, true> mBlitMat_Color_MSAA4x;
-		BlitMat<8, true> mBlitMat_Color_MSAA8x;
-		BlitMat<2, false> mBlitMat_Depth_MSAA2x;
-		BlitMat<4, false> mBlitMat_Depth_MSAA4x;
-		BlitMat<8, false> mBlitMat_Depth_MSAA8x;
 	};
 
 	/** Provides easy access to RendererUtility. */
 	BS_EXPORT RendererUtility& gRendererUtility();
 
 	/** @} */
-}}
+}}

+ 0 - 3
Source/BansheeEngine/Source/BsApplication.cpp

@@ -21,7 +21,6 @@
 #include "BsPlatform.h"
 #include "BsEngineShaderIncludeHandler.h"
 #include "BsEngineConfig.h"
-#include "BsLightProbeCache.h"
 
 namespace bs
 {
@@ -43,7 +42,6 @@ namespace bs
 		ShortcutManager::shutDown();
 		GUIManager::shutDown();
 		SpriteManager::shutDown();
-		ct::LightProbeCache::shutDown();
 		BuiltinResources::shutDown();
 		RendererMaterialManager::shutDown();
 		VirtualInput::shutDown();
@@ -58,7 +56,6 @@ namespace bs
 
 		VirtualInput::startUp();
 		BuiltinResources::startUp();
-		ct::LightProbeCache::startUp();
 		RendererMaterialManager::startUp();
 		RendererManager::instance().initialize();
 		SpriteManager::startUp();

+ 4 - 4
Source/BansheeEngine/Source/BsBuiltinResources.cpp

@@ -286,7 +286,7 @@ namespace bs
 		mShaderDiffuse = getShader(ShaderDiffuseFile);
 		mShaderTransparent = getShader(ShaderTransparentFile);
 
-		SPtr<PixelData> dummyPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> dummyPixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 
 		dummyPixelData->setColorAt(Color::Red, 0, 0);
 		dummyPixelData->setColorAt(Color::Red, 0, 1);
@@ -1118,7 +1118,7 @@ namespace bs
 	{
 		StringStream ss;
 
-		SPtr<PixelData> blackPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> blackPixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 		blackPixelData->setColorAt(Color::Black, 0, 0);
 		blackPixelData->setColorAt(Color::Black, 0, 1);
 		blackPixelData->setColorAt(Color::Black, 1, 0);
@@ -1126,7 +1126,7 @@ namespace bs
 
 		SPtr<Texture> blackTexture = Texture::_createPtr(blackPixelData);
 
-		SPtr<PixelData> whitePixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> whitePixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 		whitePixelData->setColorAt(Color::White, 0, 0);
 		whitePixelData->setColorAt(Color::White, 0, 1);
 		whitePixelData->setColorAt(Color::White, 1, 0);
@@ -1134,7 +1134,7 @@ namespace bs
 
 		SPtr<Texture> whiteTexture = Texture::_createPtr(whitePixelData);
 
-		SPtr<PixelData> normalPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> normalPixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 
 		Color encodedNormal(0.5f, 0.5f, 1.0f);
 		normalPixelData->setColorAt(encodedNormal, 0, 0);

+ 0 - 277
Source/BansheeEngine/Source/BsLightProbeCache.cpp

@@ -1,277 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsLightProbeCache.h"
-#include "BsFileSystem.h"
-#include "BsIReflectable.h"
-#include "BsRTTIType.h"
-#include "BsFileSerializer.h"
-
-namespace bs { namespace ct
-{
-	struct CachedTextureData : IReflectable
-	{
-		TextureType type;
-		UINT32 numFaces;
-		UINT32 numMips;
-		Vector<SPtr<PixelData>> pixelData;
-
-		/************************************************************************/
-		/* 								RTTI		                     		*/
-		/************************************************************************/
-	public:
-		friend class CachedTextureDataRTTI;
-		static RTTITypeBase* getRTTIStatic();
-		RTTITypeBase* getRTTI() const override;
-	};
-
-	class CachedTextureDataRTTI : public RTTIType <CachedTextureData, IReflectable, CachedTextureDataRTTI>
-	{
-	private:
-		BS_BEGIN_RTTI_MEMBERS
-			BS_RTTI_MEMBER_PLAIN(type, 0)
-			BS_RTTI_MEMBER_PLAIN(numFaces, 1)
-			BS_RTTI_MEMBER_PLAIN(numMips, 2)
-			BS_RTTI_MEMBER_REFLPTR_ARRAY(pixelData, 3)
-		BS_END_RTTI_MEMBERS
-	public:
-		CachedTextureDataRTTI()
-			:mInitMembers(this)
-		{ }
-
-		const String& getRTTIName() override
-		{
-			static String name = "CachedTextureData";
-			return name;
-		}
-
-		UINT32 getRTTIId() override
-		{
-			return TID_CachedTextureData;
-		}
-
-		SPtr<IReflectable> newRTTIObject() override
-		{
-			return bs_shared_ptr_new<CachedTextureData>();
-		}
-	};
-
-	RTTITypeBase* CachedTextureData::getRTTIStatic()
-	{
-		return CachedTextureDataRTTI::instance();
-	}
-
-	RTTITypeBase* CachedTextureData::getRTTI() const
-	{
-		return getRTTIStatic();
-	}
-
-	LightProbeCache::~LightProbeCache()
-	{
-		for (auto& entry : mProbeInfos)
-		{
-			if(entry.second.radiance.needsSaving || entry.second.irradiance.needsSaving)
-			{
-				LOGWRN("Shutting down reflection cubemap cache manager but not all textures were saved on disk before "
-					   "shutdown.");
-				break;
-			}
-		}
-	}
-
-	void LightProbeCache::initCache(const Path& path)
-	{
-		mDataPath = path;
-
-		auto visitFile = [&](const Path& filePath) -> bool
-		{
-			String uuid = filePath.getFilename(false);
-			mProbeInfos.insert(std::make_pair(uuid, ProbeInfo()));
-
-			return true;
-		};
-
-		FileSystem::iterate(path, visitFile, nullptr, false);
-	}
-
-	void LightProbeCache::notifyDirty(const String& uuid)
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind != mProbeInfos.end())
-		{
-			iterFind->second.radiance.dirty = true;
-			iterFind->second.irradiance.dirty = true;
-		}
-	}
-
-	bool LightProbeCache::isRadianceDirty(const String& uuid) const
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind != mProbeInfos.end())
-		{
-			if(iterFind->second.radiance.texture != nullptr)
-				return iterFind->second.radiance.dirty;
-		}
-
-		return true;
-	}
-
-	bool LightProbeCache::isIrradianceDirty(const String& uuid) const
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind != mProbeInfos.end())
-		{
-			if(iterFind->second.irradiance.texture != nullptr)
-				return iterFind->second.irradiance.dirty;
-		}
-
-		return true;
-	}
-
-	void LightProbeCache::setCachedRadianceTexture(const String& uuid, const SPtr<Texture>& texture)
-	{
-		ProbeInfo& probeInfo = mProbeInfos[uuid];
-		TextureInfo& texInfo = probeInfo.radiance;
-		texInfo.texture = texture;
-		texInfo.needsSaving = true;
-		texInfo.dirty = false;
-	}
-
-	void LightProbeCache::setCachedIrradianceTexture(const String& uuid, const SPtr<Texture>& texture)
-	{
-		ProbeInfo& probeInfo = mProbeInfos[uuid];
-		TextureInfo& texInfo = probeInfo.irradiance;
-		texInfo.texture = texture;
-		texInfo.needsSaving = true;
-		texInfo.dirty = false;
-	}
-
-	SPtr<Texture> LightProbeCache::getCachedRadianceTexture(const String& uuid) const
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind == mProbeInfos.end())
-			return nullptr;
-
-		TextureInfo& texInfo = iterFind->second.radiance;
-		if (texInfo.texture != nullptr)
-			return texInfo.texture;
-
-		Path filePath = mDataPath + "R_" + uuid + ".asset";
-		texInfo.texture = loadCachedTexture(filePath);
-		
-		return texInfo.texture;
-	}
-
-	SPtr<Texture> LightProbeCache::getCachedIrradianceTexture(const String& uuid) const
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind == mProbeInfos.end())
-			return nullptr;
-
-		TextureInfo& texInfo = iterFind->second.irradiance;
-		if (texInfo.texture != nullptr)
-			return texInfo.texture;
-
-		Path filePath = mDataPath + "IRR_" + uuid + ".asset";
-		texInfo.texture = loadCachedTexture(filePath);
-
-		return texInfo.texture;
-	}
-
-	void LightProbeCache::unloadCachedTexture(const String& uuid)
-	{
-		auto iterFind = mProbeInfos.find(uuid);
-		if (iterFind != mProbeInfos.end())
-		{
-			// Not allowed to unload if it requires saving (should only happen during development time)
-			if (!iterFind->second.radiance.needsSaving)
-				iterFind->second.radiance.texture = nullptr;
-
-			if (!iterFind->second.irradiance.needsSaving)
-				iterFind->second.irradiance.texture = nullptr;
-		}
-	}
-
-	void LightProbeCache::saveCache(const Path& path)
-	{
-		for(auto& entry : mProbeInfos)
-		{
-			ProbeInfo& probeInfo = entry.second;
-
-			Path radiancePath = path + "R_" + entry.first + ".asset";
-			saveCachedTexture(probeInfo.radiance, radiancePath);
-			probeInfo.radiance.needsSaving = false;
-
-			Path irradiancePath = path + "IRRR_" + entry.first + ".asset";
-			saveCachedTexture(probeInfo.irradiance, irradiancePath);
-			probeInfo.irradiance.needsSaving = false;
-		}
-	}
-
-	SPtr<Texture> LightProbeCache::loadCachedTexture(const Path& path) const
-	{
-		if (!FileSystem::exists(path))
-			return nullptr;
-
-		FileDecoder fd(path);
-		SPtr<CachedTextureData> textureData = std::static_pointer_cast<CachedTextureData>(fd.decode());
-
-		if (textureData == nullptr || textureData->pixelData.size() == 0 || textureData->pixelData[0] == nullptr)
-			return nullptr;
-
-		TEXTURE_DESC desc;
-		desc.type = textureData->type;
-		desc.format = textureData->pixelData[0]->getFormat();
-		desc.width = textureData->pixelData[0]->getWidth();
-		desc.height = textureData->pixelData[0]->getHeight();
-		desc.depth = textureData->pixelData[0]->getDepth();
-
-		if (desc.type == TEX_TYPE_CUBE_MAP)
-			desc.numArraySlices = textureData->numFaces / 6;
-		else
-			desc.numArraySlices = textureData->numFaces;
-
-		desc.numMips = textureData->numMips;
-
-		SPtr<Texture> texture = Texture::create(desc);
-		for (UINT32 face = 0; face < textureData->numFaces; face++)
-		{
-			for (UINT32 mip = 0; mip < textureData->numMips; mip++)
-			{
-				UINT32 srcIdx = face * textureData->numMips + mip;
-				if (srcIdx >= textureData->pixelData.size())
-					continue;
-
-				texture->writeData(*textureData->pixelData[srcIdx], mip, face, true);
-			}
-		}
-
-		return texture;
-	}
-
-	void LightProbeCache::saveCachedTexture(const TextureInfo& texInfo, const Path& path)
-	{
-		if (!texInfo.needsSaving)
-			return;
-
-		auto& texProps = texInfo.texture->getProperties();
-
-		SPtr<CachedTextureData> textureData = bs_shared_ptr_new<CachedTextureData>();
-		textureData->type = texProps.getTextureType();
-		textureData->numFaces = texProps.getNumFaces();
-		textureData->numMips = texProps.getNumMipmaps() + 1;
-
-		for (UINT32 face = 0; face < textureData->numFaces; face++)
-		{
-			for (UINT32 mip = 0; mip < textureData->numMips; mip++)
-			{
-				SPtr<PixelData> pixelData = texProps.allocBuffer(face, mip);
-				texInfo.texture->readData(*pixelData, mip, face);
-
-				textureData->pixelData.push_back(pixelData);
-			}
-		}
-;
-		FileEncoder fe(path);
-		fe.encode(textureData.get());
-	}
-}}

+ 70 - 0
Source/BansheeEngine/Source/BsRendererMaterial.cpp

@@ -4,5 +4,75 @@
 
 namespace bs { namespace ct
 {
+	ShaderVariation::ShaderVariation(const SmallVector<Param, 4>& params)
+	{
+		for (auto& entry : params)
+			mParams[entry.name] = entry;
+	}
 
+	INT32 ShaderVariation::getInt(const String& name)
+	{
+		auto iterFind = mParams.find(name);
+		if (iterFind == mParams.end())
+			return 0;
+		else
+			return iterFind->second.i;
+	}
+
+	UINT32 ShaderVariation::getUInt(const String& name)
+	{
+		auto iterFind = mParams.find(name);
+		if (iterFind == mParams.end())
+			return 0;
+		else
+			return iterFind->second.ui;
+	}
+
+	float ShaderVariation::getFloat(const String& name)
+	{
+		auto iterFind = mParams.find(name);
+		if (iterFind == mParams.end())
+			return 0.0f;
+		else
+			return iterFind->second.f;
+	}
+
+	bool ShaderVariation::getBool(const String& name)
+	{
+		auto iterFind = mParams.find(name);
+		if (iterFind == mParams.end())
+			return false;
+		else
+			return iterFind->second.i > 0 ? true : false;
+	}
+
+	ShaderDefines ShaderVariation::getDefines() const
+	{
+		ShaderDefines defines;
+		for (auto& entry : mParams)
+		{
+			switch (entry.second.type)
+			{
+			case Int:
+			case Bool:
+				defines.set(entry.first, entry.second.i);
+				break;
+			case UInt:
+				defines.set(entry.first, entry.second.ui);
+				break;
+			case Float:
+				defines.set(entry.first, entry.second.f);
+				break;
+			}
+		}
+
+		return defines;
+	}
+
+	void ShaderVariations::add(ShaderVariation& variation)
+	{
+		variation.mIdx = mNextIdx++;
+
+		mVariations.push_back(variation);
+	}
 }}

+ 50 - 10
Source/BansheeEngine/Source/BsRendererMaterialManager.cpp

@@ -12,6 +12,8 @@ namespace bs
 	{
 		BuiltinResources& br = BuiltinResources::instance();
 
+		// Note: Ideally I want to avoid loading all materials, and instead just load those that are used. This might be a
+		// problem on lower end systems that don't support all renderer features.
 		Vector<RendererMaterialData>& materials = getMaterials();
 		Vector<SPtr<ct::Shader>> shaders;
 		for (auto& material : materials)
@@ -36,15 +38,33 @@ namespace bs
 		Lock lock(getMutex());
 
 		Vector<RendererMaterialData>& materials = getMaterials();
-		UINT32 variationIdx = 0;
-		for (auto& entry : materials)
+
+		const SmallVector<ct::ShaderVariation, 4>& variations = metaData->variations.getVariations();
+
+		if(variations.empty())
 		{
-			if (entry.shaderPath == shaderPath)
-				variationIdx++;
+			metaData->shaders.resize(1);
+			metaData->instances.resize(1);
+
+			Path resourcePath = _getVariationPath(shaderPath, 0);
+			materials.push_back({ metaData, shaderPath, resourcePath, 0 });
 		}
+		else
+		{
+			metaData->shaders.resize(variations.size());
+			metaData->instances.resize(variations.size());
 
-		Path resourcePath = _getVariationPath(shaderPath, variationIdx);
-		materials.push_back({metaData, shaderPath, resourcePath });
+			UINT32 variationIdx = 0;
+			for (auto& variation : variations)
+			{
+				assert(variation.getIdx() == variationIdx);
+
+				Path resourcePath = _getVariationPath(shaderPath, variationIdx);
+				materials.push_back({ metaData, shaderPath, resourcePath, variationIdx });
+
+				variationIdx++;
+			}
+		}
 	}
 
 	Vector<ShaderDefines> RendererMaterialManager::_getVariations(const Path& shaderPath)
@@ -55,7 +75,17 @@ namespace bs
 		for (auto& entry : materials)
 		{
 			if (entry.shaderPath == shaderPath)
-				output.push_back(entry.metaData->defines);
+			{
+				if(entry.metaData->variations.getVariations().empty())
+				{
+					output.push_back(ShaderDefines());
+				}
+				else
+				{
+					const ct::ShaderVariation& variation = entry.metaData->variations.get(entry.variationIdx);
+					output.push_back(variation.getDefines());
+				}
+			}
 		}
 
 		return output;
@@ -83,7 +113,7 @@ namespace bs
 
 		Vector<RendererMaterialData>& materials = getMaterials();
 		for (UINT32 i = 0; i < materials.size(); i++)
-			materials[i].metaData->shader = shaders[i];
+			materials[i].metaData->shaders[materials[i].variationIdx] = shaders[i];
 	}
 
 	void RendererMaterialManager::destroyOnCore()
@@ -92,7 +122,17 @@ namespace bs
 
 		Vector<RendererMaterialData>& materials = getMaterials();
 		for (UINT32 i = 0; i < materials.size(); i++)
-			materials[i].metaData->shader = nullptr;
+		{
+			materials[i].metaData->shaders.clear();
+
+			for (auto& entry : materials[i].metaData->instances)
+			{
+				if(entry != nullptr)
+					bs_delete(entry);
+			}
+
+			materials[i].metaData->instances.clear();
+		}
 	}
 
 	Vector<RendererMaterialManager::RendererMaterialData>& RendererMaterialManager::getMaterials()
@@ -106,4 +146,4 @@ namespace bs
 		static Mutex mutex;
 		return mutex;
 	}
-}
+}

+ 118 - 73
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -122,14 +122,10 @@ namespace bs { namespace ct
 
 			mSkyBoxMesh = Mesh::create(meshData);
 		}
-
-		IBLUtility::startUp();
 	}
 
 	RendererUtility::~RendererUtility()
-	{
-		IBLUtility::shutDown();
-	}
+	{ }
 
 	void RendererUtility::setPass(const SPtr<Material>& material, UINT32 passIdx, UINT32 techniqueIdx)
 	{
@@ -253,61 +249,12 @@ namespace bs { namespace ct
 	void RendererUtility::blit(const SPtr<Texture>& texture, const Rect2I& area, bool flipUV, bool isDepth)
 	{
 		auto& texProps = texture->getProperties();
-		SPtr<Material> mat;
-		SPtr<GpuParamsSet> params;
-
-#define PICK_MATERIAL(Type)									\
-			mat = mBlitMat_##Type.getMaterial();			\
-			params = mBlitMat_##Type.getParamsSet();		\
-			mBlitMat_##Type.setParameters(texture);
-
-		if (texProps.getNumSamples() > 1)
-		{
-			if(texProps.getNumSamples() > 8)
-				LOGWRN("Unsupported sample count in an MSAA texture");
-
-			if(isDepth)
-			{
-				switch(texProps.getNumSamples())
-				{
-				case 2:
-					PICK_MATERIAL(Depth_MSAA2x)
-					break;
-				case 4:
-					PICK_MATERIAL(Depth_MSAA4x)
-					break;
-				default:
-				case 8:
-					PICK_MATERIAL(Depth_MSAA8x)
-					break;
-				}
-			}
-			else
-			{
-				switch(texProps.getNumSamples())
-				{
-				case 2:
-					PICK_MATERIAL(Color_MSAA2x)
-					break;
-				case 4:
-					PICK_MATERIAL(Color_MSAA4x)
-					break;
-				default:
-				case 8:
-					PICK_MATERIAL(Color_MSAA8x)
-					break;
-				}
-			}
-		}
-		else
-		{
-			PICK_MATERIAL(Color_NoMSAA)
-		}
 
-#undef PICK_MATERIAL
+		BlitMat* blitMat = BlitMat::getVariation(texProps.getNumSamples(), !isDepth);
+		blitMat->setParameters(texture);
 
-		setPass(mat);
-		setPassParams(params);
+		setPass(blitMat->getMaterial());
+		setPassParams(blitMat->getParamsSet());
 
 		Rect2 fArea((float)area.x, (float)area.y, (float)area.width, (float)area.height);
 		if (area.width == 0 || area.height == 0)
@@ -388,36 +335,134 @@ namespace bs { namespace ct
 		draw(mFullScreenQuadMesh, mFullScreenQuadMesh->getProperties().getSubMesh(), numInstances);
 	}
 
+	void RendererUtility::clear(UINT32 value)
+	{
+		ClearMat* clearMat = ClearMat::get();
+		clearMat->execute(value);
+	}
+
 	RendererUtility& gRendererUtility()
 	{
 		return RendererUtility::instance();
 	}
 
-	template<int MSAA_COUNT, bool IS_COLOR>
-	BlitMat<MSAA_COUNT, IS_COLOR>::BlitMat()
+	ShaderVariation BlitMat::VAR_1MSAA_Color = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 1),
+		ShaderVariation::Param("COLOR", true)
+	});
+	ShaderVariation BlitMat::VAR_2MSAA_Color = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("COLOR", true)
+	});
+
+	ShaderVariation BlitMat::VAR_4MSAA_Color = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 4),
+		ShaderVariation::Param("COLOR", true)
+	});
+
+	ShaderVariation BlitMat::VAR_8MSAA_Color = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 8),
+		ShaderVariation::Param("COLOR", true)
+	});
+
+	ShaderVariation BlitMat::VAR_1MSAA_Depth = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 1),
+		ShaderVariation::Param("COLOR", false)
+	});
+
+	ShaderVariation BlitMat::VAR_2MSAA_Depth = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 2),
+		ShaderVariation::Param("COLOR", false)
+	});
+
+	ShaderVariation BlitMat::VAR_4MSAA_Depth = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 4),
+		ShaderVariation::Param("COLOR", false)
+	});
+
+	ShaderVariation BlitMat::VAR_8MSAA_Depth = ShaderVariation({
+		ShaderVariation::Param("MSAA_COUNT", 8),
+		ShaderVariation::Param("COLOR", false)
+	});
+
+	BlitMat::BlitMat()
 	{
 		mSource = mMaterial->getParamTexture("gSource");
 	}
 
-	template<int MSAA_COUNT, bool IS_COLOR>
-	void BlitMat<MSAA_COUNT, IS_COLOR>::_initDefines(ShaderDefines& defines)
+	void BlitMat::_initVariations(ShaderVariations& variations)
 	{
-		defines.set("MSAA_COUNT", MSAA_COUNT);
-		defines.set("COLOR", IS_COLOR ? 1 : 0);
+		variations.add(VAR_1MSAA_Color);
+		variations.add(VAR_2MSAA_Color);
+		variations.add(VAR_4MSAA_Color);
+		variations.add(VAR_8MSAA_Color);
+
+		variations.add(VAR_1MSAA_Depth);
+		variations.add(VAR_2MSAA_Depth);
+		variations.add(VAR_4MSAA_Depth);
+		variations.add(VAR_8MSAA_Depth);
 	}
 
-	template<int MSAA_COUNT, bool IS_COLOR>
-	void BlitMat<MSAA_COUNT, IS_COLOR>::setParameters(const SPtr<Texture>& source)
+	void BlitMat::setParameters(const SPtr<Texture>& source)
 	{
 		mSource.set(source);
 		mMaterial->updateParamsSet(mParamsSet);
 	}
 
-	template class BlitMat<1, true>;
-	template class BlitMat<2, true>;
-	template class BlitMat<4, true>;
-	template class BlitMat<8, true>;
-	template class BlitMat<2, false>;
-	template class BlitMat<4, false>;
-	template class BlitMat<8, false>;
+	BlitMat* BlitMat::getVariation(UINT32 msaaCount, bool isColor)
+	{
+		if (msaaCount > 1)
+		{
+			if(isColor)
+			{
+				switch(msaaCount)
+				{
+				case 2:
+					return get(VAR_2MSAA_Color);
+				case 4:
+					return get(VAR_4MSAA_Color);
+				default:
+				case 8:
+					return get(VAR_8MSAA_Color);
+				}
+			}
+			else
+			{
+				switch(msaaCount)
+				{
+				case 2:
+					return get(VAR_2MSAA_Depth);
+				case 4:
+					return get(VAR_4MSAA_Depth);
+				default:
+				case 8:
+					return get(VAR_8MSAA_Depth);
+				}
+			}
+		}
+		else
+			return get(VAR_1MSAA_Color);
+	}
+
+	ClearParamDef gClearParamDef;
+
+	ClearMat::ClearMat()
+	{
+		mParamBuffer = gClearParamDef.createBuffer();
+		mParamsSet->setParamBlockBuffer("Params", mParamBuffer);
+	}
+
+	void ClearMat::_initVariations(ShaderVariations& variations)
+	{
+		// Do nothing
+	}
+
+	void ClearMat::execute(UINT32 value)
+	{
+		gClearParamDef.gClearValue.set(mParamBuffer, value);
+
+		gRendererUtility().setPass(mMaterial);
+		gRendererUtility().setPassParams(mParamsSet);
+		gRendererUtility().drawScreenQuad();
+	}
 }}

+ 2 - 1
Source/BansheeFBXImporter/Include/BsFBXImportData.h

@@ -38,10 +38,11 @@ namespace bs
 	{
 		~FBXImportNode();
 
-		Matrix4 localTransform;
+		Matrix4 geomTransform;
 		Matrix4 worldTransform;
 		String name;
 		FbxNode* fbxNode;
+		bool flipWinding;
 
 		Vector<FBXImportNode*> children;
 	};

+ 59 - 18
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -359,7 +359,7 @@ namespace bs
 
 			for (auto& node : mesh->referencedBy)
 			{
-				Matrix4 worldTransform = scene.globalScale * node->worldTransform;
+				Matrix4 worldTransform = scene.globalScale * node->worldTransform * node->geomTransform;
 				Matrix4 worldTransformIT = worldTransform.inverse();
 				worldTransformIT = worldTransformIT.transpose();
 
@@ -511,6 +511,7 @@ namespace bs
 
 	void FBXImporter::parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene)
 	{
+		// Scale from file units to engine units, and apply optional user scale
 		float importScale = 1.0f;
 		if (options.importScale > 0.0001f)
 			importScale = options.importScale;
@@ -589,27 +590,49 @@ namespace bs
 	{
 		FBXImportNode* node = bs_new<FBXImportNode>();
 
-		Vector3 translation = FBXToNativeType(fbxNode->LclTranslation.Get());
-		Vector3 rotationEuler = FBXToNativeType(fbxNode->LclRotation.Get());
-		Vector3 scale = FBXToNativeType(fbxNode->LclScaling.Get());
+		Vector3 translation = FBXToNativeType(fbxNode->EvaluateLocalTranslation(FbxTime(0)));
+		Vector3 rotationEuler = FBXToNativeType(fbxNode->EvaluateLocalRotation(FbxTime(0)));
+		Vector3 scale = FBXToNativeType(fbxNode->EvaluateLocalScaling(FbxTime(0)));
 
 		Quaternion rotation((Degree)rotationEuler.x, (Degree)rotationEuler.y, (Degree)rotationEuler.z);
 
-		node->localTransform.setTRS(translation, rotation, scale);
+		Matrix4 localTransform = Matrix4::TRS(translation, rotation, scale);
 		node->name = fbxNode->GetNameWithoutNameSpacePrefix().Buffer();
 		node->fbxNode = fbxNode;
 
 		if (parent != nullptr)
 		{
-			node->worldTransform = node->localTransform * parent->worldTransform;
+			node->worldTransform = parent->worldTransform * localTransform;
 
 			parent->children.push_back(node);
 		}
 		else
-			node->worldTransform = node->localTransform;
+			node->worldTransform = localTransform;
+
+		// Geometry transform is applied to geometry (mesh data) only, it is not inherited by children, so we store it
+		// separately
+		Vector3 geomTrans = FBXToNativeType(fbxNode->GeometricTranslation.Get());
+		Vector3 geomRotEuler = FBXToNativeType(fbxNode->GeometricRotation.Get());
+		Vector3 geomScale = FBXToNativeType(fbxNode->GeometricScaling.Get());
+
+		Quaternion geomRotation((Degree)geomRotEuler.x, (Degree)geomRotEuler.y, (Degree)geomRotEuler.z);
+		node->geomTransform = Matrix4::TRS(geomTrans, geomRotation, geomScale);
 
 		scene.nodeMap.insert(std::make_pair(fbxNode, node));
 
+		// Determine if geometry winding needs to be flipped to match the engine convention. This is true by default, but
+		// each negative scaling factor changes the winding.
+		if (parent != nullptr)
+			node->flipWinding = parent->flipWinding;
+		else
+			node->flipWinding = true;
+
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (scale[i] < 0.0f) node->flipWinding = !node->flipWinding;
+			if (geomScale[i] < 0.0f) node->flipWinding = !node->flipWinding;
+		}
+
 		return node;
 	}
 
@@ -625,7 +648,6 @@ namespace bs
 			splitMesh->bones = mesh->bones;
 
 			FBXUtility::splitVertices(*mesh, *splitMesh);
-			FBXUtility::flipWindingOrder(*splitMesh);
 			splitMeshes.push_back(splitMesh);
 
 			bs_delete(mesh);
@@ -891,14 +913,29 @@ namespace bs
 			UINT32 numIndices = (UINT32)mesh->indices.size();
 			for (auto& node : mesh->referencedBy)
 			{
-				Matrix4 worldTransform = scene.globalScale * node->worldTransform;
+				Matrix4 worldTransform = scene.globalScale * node->worldTransform * node->geomTransform;
 				Matrix4 worldTransformIT = worldTransform.inverse();
 				worldTransformIT = worldTransformIT.transpose();
 
 				SPtr<RendererMeshData> meshData = RendererMeshData::create((UINT32)numVertices, numIndices, (VertexLayout)vertexLayout);
 
 				// Copy indices
-				meshData->setIndices((UINT32*)mesh->indices.data(), numIndices * sizeof(UINT32));
+				if(!node->flipWinding)
+					meshData->setIndices(orderedIndices, numIndices * sizeof(UINT32));
+				else
+				{
+					UINT32* flippedIndices = bs_stack_alloc<UINT32>(numIndices);
+
+					for (UINT32 i = 0; i < numIndices; i += 3)
+					{
+						flippedIndices[i + 0] = orderedIndices[i + 0];
+						flippedIndices[i + 1] = orderedIndices[i + 2];
+						flippedIndices[i + 2] = orderedIndices[i + 1];
+					}
+
+					meshData->setIndices(flippedIndices, numIndices * sizeof(UINT32));
+					bs_stack_free(flippedIndices);
+				}
 
 				// Copy & transform positions
 				UINT32 positionsSize = sizeof(Vector3) * (UINT32)numVertices;
@@ -1014,6 +1051,8 @@ namespace bs
 				allSubMeshes.push_back(subMeshes);
 			}
 
+			bs_free(orderedIndices);
+
 			UINT32 numBones = (UINT32)mesh->bones.size();
 			boneIndexOffset += numBones;
 		}
@@ -1774,13 +1813,13 @@ namespace bs
 				boneAnim.scale = TAnimationCurve<Vector3>(keyframes);
 			}
 
-			TAnimationCurve<Vector3> eulerAnimation;
+			SPtr<TAnimationCurve<Vector3>> eulerAnimation = bs_shared_ptr_new<TAnimationCurve<Vector3>>();
 			if (hasCurveValues(rotation))
 			{
 				float defaultValues[3];
 				memcpy(defaultValues, &defaultRotation, sizeof(defaultValues));
 
-				eulerAnimation = importCurve<Vector3, 3>(rotation, defaultValues, importOptions, clip.start, clip.end);
+				*eulerAnimation = importCurve<Vector3, 3>(rotation, defaultValues, importOptions, clip.start, clip.end);
 			}
 			else
 			{
@@ -1789,18 +1828,18 @@ namespace bs
 				keyframes[0].inTangent = Vector3::ZERO;
 				keyframes[0].outTangent = Vector3::ZERO;
 
-				eulerAnimation = TAnimationCurve<Vector3>(keyframes);
+				*eulerAnimation = TAnimationCurve<Vector3>(keyframes);
 			}
 
 			if(importOptions.reduceKeyframes)
 			{
 				boneAnim.translation = reduceKeyframes(boneAnim.translation);
 				boneAnim.scale = reduceKeyframes(boneAnim.scale);
-				eulerAnimation = reduceKeyframes(eulerAnimation);
+				*eulerAnimation = reduceKeyframes(*eulerAnimation);
 			}
 
 			boneAnim.translation = AnimationUtility::scaleCurve(boneAnim.translation, importScene.scaleFactor);
-			boneAnim.rotation = AnimationUtility::eulerToQuaternionCurve(eulerAnimation);
+			boneAnim.rotation = *AnimationUtility::eulerToQuaternionCurve(eulerAnimation);
 		}
 
 		if (importOptions.importBlendShapes)
@@ -1882,9 +1921,11 @@ namespace bs
 				node->SetScalingOffset(FbxNode::eDestinationPivot, zero);
 				node->SetRotationPivot(FbxNode::eDestinationPivot, zero);
 				node->SetScalingPivot(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricTranslation(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricRotation(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricScaling(FbxNode::eDestinationPivot, one);
+
+				// We account for geometric properties separately during node traversal
+				node->SetGeometricTranslation(FbxNode::eDestinationPivot, node->GetGeometricTranslation(FbxNode::eSourcePivot));
+				node->SetGeometricRotation(FbxNode::eDestinationPivot, node->GetGeometricRotation(FbxNode::eSourcePivot));
+				node->SetGeometricScaling(FbxNode::eDestinationPivot, node->GetGeometricScaling(FbxNode::eSourcePivot));
 
 				// Banshee assumes euler angles are in YXZ order
 				node->SetRotationOrder(FbxNode::eDestinationPivot, FbxEuler::eOrderYXZ);

+ 1 - 1
Source/BansheeFBXImporter/Source/BsFBXUtility.cpp

@@ -229,7 +229,7 @@ namespace bs
 				if (splits.empty())
 				{
 					dstVertIdx = srcVertIdx;
-					copyVertexAttributes(source, i, dest, srcVertIdx);
+					copyVertexAttributes(source, i, dest, dstVertIdx);
 				}
 				else // Split occurred, add a brand new vertex
 				{

+ 2 - 2
Source/BansheeFontImporter/Source/BsFontImporter.cpp

@@ -184,7 +184,7 @@ namespace bs
 				UINT32 bufferSize = pageIter->width * pageIter->height * 2;
 
 				// TODO - I don't actually need a 2 channel texture
-				SPtr<PixelData> pixelData = bs_shared_ptr_new<PixelData>(pageIter->width, pageIter->height, 1, PF_R8G8);
+				SPtr<PixelData> pixelData = bs_shared_ptr_new<PixelData>(pageIter->width, pageIter->height, 1, PF_RG8);
 
 				pixelData->allocateInternalBuffer();
 				UINT8* pixelBuffer = pixelData->getData();
@@ -325,7 +325,7 @@ namespace bs
 				TEXTURE_DESC texDesc;
 				texDesc.width = pageIter->width;
 				texDesc.height = pageIter->height;
-				texDesc.format = PF_R8G8;
+				texDesc.format = PF_RG8;
 
 				HTexture newTex = Texture::create(texDesc);
 

+ 41 - 13
Source/BansheeFreeImgImporter/Source/BsFreeImgImporter.cpp

@@ -270,6 +270,7 @@ namespace bs
 		FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(fiBitmap);
 		FREE_IMAGE_COLOR_TYPE colourType = FreeImage_GetColorType(fiBitmap);
 		unsigned bpp = FreeImage_GetBPP(fiBitmap);
+		unsigned srcElemSize = 0;
 
 		switch(imageType)
 		{
@@ -312,6 +313,7 @@ namespace bs
 			{
 			case 8:
 				format = PF_R8;
+				srcElemSize = 1;
 				break;
 			case 16:
 				// Determine 555 or 565 from green mask
@@ -327,23 +329,26 @@ namespace bs
 					return nullptr;
 					// FreeImage doesn't support 4444 format so must be 1555
 				}
+				srcElemSize = 2;
 				break;
 			case 24:
 				// FreeImage differs per platform
 				//     PF_BYTE_BGR[A] for little endian (== PF_ARGB native)
 				//     PF_BYTE_RGB[A] for big endian (== PF_RGBA native)
 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
-				format = PF_R8G8B8;
+				format = PF_RGB8;
 #else
-				format = PF_B8G8R8;
+				format = PF_BGR8;
 #endif
+				srcElemSize = 3;
 				break;
 			case 32:
 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
-				format = PF_R8G8B8A8;
+				format = PF_RGBA8;
 #else
-				format = PF_B8G8R8A8;
+				format = PF_BGRA8;
 #endif
+				srcElemSize = 4;
 				break;
 
 
@@ -357,19 +362,24 @@ namespace bs
 			break;
 		case FIT_FLOAT:
 			// Single-component floating point data
-			format = PF_FLOAT32_R;
+			format = PF_R32F;
+			srcElemSize = 4;
 			break;
 		case FIT_RGB16:
-			format = PF_FLOAT16_RGB;
+			format = PF_RGBA16F;
+			srcElemSize = 2 * 3;
 			break;
 		case FIT_RGBA16:
-			format = PF_FLOAT16_RGBA;
+			format = PF_RGBA16F;
+			srcElemSize = 2 * 4;
 			break;
 		case FIT_RGBF:
-			format = PF_FLOAT32_RGB;
+			format = PF_RGB32F;
+			srcElemSize = 4 * 3;
 			break;
 		case FIT_RGBAF:
-			format = PF_FLOAT32_RGBA;
+			format = PF_RGBA32F;
+			srcElemSize = 4 * 4;
 			break;
 		};
 
@@ -377,6 +387,7 @@ namespace bs
 		unsigned srcPitch = FreeImage_GetPitch(fiBitmap);
 
 		// Final data - invert image and trim pitch at the same time
+		UINT32 dstElemSize = PixelUtil::getNumElemBytes(format);
 		UINT32 dstPitch = width * PixelUtil::getNumElemBytes(format);
 
 		// Bind output buffer
@@ -386,11 +397,28 @@ namespace bs
 
 		UINT8* pSrc;
 		UINT8* pDst = output;
-		for (UINT32 y = 0; y < height; ++y)
+
+		// Copy row by row, which is faster
+		if (srcElemSize == dstElemSize)
+		{
+			for (UINT32 y = 0; y < height; ++y)
+			{
+				pSrc = srcData + (height - y - 1) * srcPitch;
+				memcpy(pDst, pSrc, dstPitch);
+				pDst += dstPitch;
+			}
+		}
+		else
 		{
-			pSrc = srcData + (height - y - 1) * srcPitch;
-			memcpy(pDst, pSrc, dstPitch);
-			pDst += dstPitch;
+			for (UINT32 y = 0; y < height; ++y)
+			{
+				pSrc = srcData + (height - y - 1) * srcPitch;
+
+				for(UINT32 x = 0; x < width; ++x)
+					memcpy(pDst + x * dstElemSize, pSrc + x * srcElemSize, srcElemSize);
+
+				pDst += dstPitch;
+			}
 		}
 
 		FreeImage_Unload(fiBitmap);

+ 276 - 160
Source/BansheeGLRenderAPI/Source/BsGLPixelFormat.cpp

@@ -17,167 +17,283 @@ namespace bs { namespace ct
 		return pf;
 	}
 
-    GLenum GLPixelUtil::getGLOriginFormat(PixelFormat mFormat)
-    {
-        switch(mFormat)
-        {
-			case PF_R8:
-				return GL_RED;
-			case PF_R8G8:
-				return GL_RG;
-            case PF_R8G8B8:
-                return GL_RGB;
-            case PF_B8G8R8:
-                return GL_BGR;
-			case PF_R8G8B8A8:
-                return GL_RGBA;
-            case PF_B8G8R8A8:
-                return GL_BGRA;
-			case PF_FLOAT16_R:
-                return GL_RED;
-			case PF_FLOAT16_RG:
-				return GL_RG;
-            case PF_FLOAT16_RGB:
-                return GL_RGB;
-            case PF_FLOAT16_RGBA:
-                return GL_RGBA;
-			case PF_FLOAT32_R:
-                return GL_RED;
-			case PF_FLOAT32_RG:
-				return GL_RG;
-            case PF_FLOAT32_RGB:
-                return GL_RGB;
-            case PF_FLOAT32_RGBA:
-                return GL_RGBA;
-			case PF_FLOAT_R11G11B10:
-				return GL_RGB;
-			case PF_UNORM_R10G10B10A2:
-				return GL_RGBA;
-            case PF_BC1:
-            case PF_BC1a:
-            case PF_BC3:
-			case PF_BC7:
-				return GL_RGBA;
-			case PF_BC4:
-				return GL_RED;
-			case PF_BC5:
-				return GL_RG;
-			case PF_BC6H:
-				return GL_RGB;
-            default:
-                return 0;
-        }
-    }
+	GLenum GLPixelUtil::getGLOriginFormat(PixelFormat mFormat)
+	{
+		switch (mFormat)
+		{
+		case PF_R8:
+		case PF_R8I:
+		case PF_R8U:
+		case PF_R8S:
+			return GL_RED;
+		case PF_RG8:
+		case PF_RG8I:
+		case PF_RG8U:
+		case PF_RG8S:
+			return GL_RG;
+		case PF_RGB8:
+			return GL_RGB;
+		case PF_BGR8:
+			return GL_BGR;
+		case PF_RGBA8:
+		case PF_RGBA8I:
+		case PF_RGBA8U:
+		case PF_RGBA8S:
+			return GL_RGBA;
+		case PF_BGRA8:
+			return GL_BGRA;
+		case PF_R16F:
+		case PF_R16I:
+		case PF_R16U:
+		case PF_R16S:
+		case PF_R16:
+			return GL_RED;
+		case PF_RG16F:
+		case PF_RG16I:
+		case PF_RG16U:
+		case PF_RG16S:
+		case PF_RG16:
+			return GL_RG;
+		case PF_RGBA16F:
+		case PF_RGBA16I:
+		case PF_RGBA16U:
+		case PF_RGBA16S:
+		case PF_RGBA16:
+			return GL_RGBA;
+		case PF_R32F:
+		case PF_R32I:
+		case PF_R32U:
+			return GL_RED;
+		case PF_RG32F:
+		case PF_RG32I:
+		case PF_RG32U:
+			return GL_RG;
+		case PF_RGB32F:
+		case PF_RGB32I:
+		case PF_RGB32U:
+			return GL_RGB;
+		case PF_RGBA32F:
+		case PF_RGBA32I:
+		case PF_RGBA32U:
+			return GL_RGBA;
+		case PF_RG11B10F:
+			return GL_RGB;
+		case PF_RGB10A2:
+			return GL_RGBA;
+		case PF_BC1:
+		case PF_BC1a:
+		case PF_BC3:
+		case PF_BC7:
+			return GL_RGBA;
+		case PF_BC4:
+			return GL_RED;
+		case PF_BC5:
+			return GL_RG;
+		case PF_BC6H:
+			return GL_RGB;
+		default:
+			return 0;
+		}
+	}
 
-    GLenum GLPixelUtil::getGLOriginDataType(PixelFormat format)
-    {
-        switch(format)
-        {
-			case PF_R8:
-            case PF_R8G8B8:
-            case PF_B8G8R8:
-			case PF_R8G8:
-				return GL_UNSIGNED_BYTE;
-            case PF_B8G8R8A8:
-			case PF_R8G8B8A8:
-				return GL_UNSIGNED_INT_8_8_8_8_REV;
-			case PF_FLOAT16_R:
-			case PF_FLOAT16_RG:
-            case PF_FLOAT16_RGB:
-            case PF_FLOAT16_RGBA:
-                return GL_HALF_FLOAT;
-			case PF_FLOAT32_R:
-			case PF_FLOAT32_RG:
-            case PF_FLOAT32_RGB:
-            case PF_FLOAT32_RGBA:
-                return GL_FLOAT;
-			case PF_FLOAT_R11G11B10:
-				return GL_UNSIGNED_INT_10F_11F_11F_REV;
-			case PF_UNORM_R10G10B10A2:
-				return GL_UNSIGNED_INT_2_10_10_10_REV;
-            default:
-                return 0;
-        }
-    }
-    
-    GLenum GLPixelUtil::getGLInternalFormat(PixelFormat mFormat, bool hwGamma)
-    {
-        switch(mFormat) {
-            case PF_R8:
-                return GL_R8;
-			case PF_R8G8:
-				return GL_RG8;
-            case PF_R8G8B8:
-            case PF_B8G8R8:
-				if (hwGamma)
-					return GL_SRGB8;
-				else
-					return GL_RGB8;
-            case PF_B8G8R8A8:
-			case PF_R8G8B8A8:
-				if (hwGamma)
-					return GL_SRGB8_ALPHA8;
-				else
-					return GL_RGBA8;
-			case PF_FLOAT16_R:
-				return GL_R16F;
-            case PF_FLOAT16_RGB:
-                return GL_RGB16F;
-			case PF_FLOAT16_RG: 
-				return GL_RG16F;
-            case PF_FLOAT16_RGBA:
-                return GL_RGBA16F;
-			case PF_FLOAT32_R:
-				return GL_R32F;
-			case PF_FLOAT32_RG:
-				return GL_RG32F;
-            case PF_FLOAT32_RGB:
-                return GL_RGB32F;
-            case PF_FLOAT32_RGBA:
-                return GL_RGBA32F;
-			case PF_BC1a:
-			case PF_BC1:
-				if (hwGamma)
-					return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
-				else
-					return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
-            case PF_BC2:
-				if (hwGamma)
-					return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
-				else
-	                return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
-            case PF_BC3:
-				if (hwGamma)
-					return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
-				else
-	                return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
-			case PF_BC4:
-				return GL_COMPRESSED_RED_RGTC1;
-			case PF_BC5:
-				return GL_COMPRESSED_RG_RGTC2;
-			case PF_BC6H:
-				return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
-			case PF_BC7:
-				if (hwGamma)
-					return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
-				else
-					return GL_COMPRESSED_RGBA_BPTC_UNORM;
-			case PF_D16:
-				return GL_DEPTH_COMPONENT16;
-			case PF_D32:
-				return GL_DEPTH_COMPONENT32F;
-			case PF_D24S8:
-				return GL_DEPTH24_STENCIL8;
-			case PF_D32_S8X24:
-				return GL_DEPTH32F_STENCIL8;
-			case PF_FLOAT_R11G11B10:
-				return GL_R11F_G11F_B10F;
-			case PF_UNORM_R10G10B10A2:
-				return GL_RGB10_A2;
-            default:
-                return GL_NONE;
-        }
-    }
+	GLenum GLPixelUtil::getGLOriginDataType(PixelFormat format)
+	{
+		switch (format)
+		{
+		case PF_R8:
+		case PF_RG8:
+		case PF_RGB8:
+		case PF_BGR8:
+		case PF_R8U:
+		case PF_RG8U:
+		case PF_RGBA8U:
+			return GL_UNSIGNED_BYTE;
+		case PF_BGRA8:
+		case PF_RGBA8:
+			return GL_UNSIGNED_INT_8_8_8_8_REV;
+		case PF_R8I:
+		case PF_RG8I:
+		case PF_RGBA8I:
+		case PF_R8S:
+		case PF_RG8S:
+		case PF_RGBA8S:
+			return GL_BYTE;
+		case PF_R16I:
+		case PF_RG16I:
+		case PF_RGBA16I:
+		case PF_R16S:
+		case PF_RG16S:
+		case PF_RGBA16S:
+			return GL_SHORT;
+		case PF_R16:
+		case PF_RG16:
+		case PF_RGBA16:
+		case PF_R16U:
+		case PF_RG16U:
+		case PF_RGBA16U:
+			return GL_UNSIGNED_SHORT;
+		case PF_R16F:
+		case PF_RG16F:
+		case PF_RGBA16F:
+			return GL_HALF_FLOAT;
+		case PF_R32I:
+		case PF_RG32I:
+		case PF_RGB32I:
+		case PF_RGBA32I:
+			return GL_INT;
+		case PF_R32U:
+		case PF_RG32U:
+		case PF_RGB32U:
+		case PF_RGBA32U:
+			return GL_UNSIGNED_INT;
+		case PF_R32F:
+		case PF_RG32F:
+		case PF_RGB32F:
+		case PF_RGBA32F:
+			return GL_FLOAT;
+		case PF_RG11B10F:
+			return GL_UNSIGNED_INT_10F_11F_11F_REV;
+		case PF_RGB10A2:
+			return GL_UNSIGNED_INT_2_10_10_10_REV;
+		default:
+			return 0;
+		}
+	}
+
+	GLenum GLPixelUtil::getGLInternalFormat(PixelFormat mFormat, bool hwGamma)
+	{
+		switch (mFormat) {
+		case PF_R8:
+			return GL_R8;
+		case PF_R8I:
+			return GL_R8I;
+		case PF_R8U:
+			return GL_R8UI;
+		case PF_R8S:
+			return GL_R8_SNORM;
+		case PF_RG8:
+			return GL_RG8;
+		case PF_RG8I:
+			return GL_RG8I;
+		case PF_RG8U:
+			return GL_RG8UI;
+		case PF_RG8S:
+			return GL_RG8_SNORM;
+		case PF_RGB8:
+		case PF_BGR8:
+			if (hwGamma)
+				return GL_SRGB8;
+			else
+				return GL_RGB8;
+		case PF_BGRA8:
+		case PF_RGBA8:
+			if (hwGamma)
+				return GL_SRGB8_ALPHA8;
+			else
+				return GL_RGBA8;
+		case PF_RGBA8I:
+			return GL_RGBA8I;
+		case PF_RGBA8U:
+			return GL_RGBA8UI;
+		case PF_RGBA8S:
+			return GL_RGBA8_SNORM;
+		case PF_R16F:
+			return GL_R16F;
+		case PF_R16I:
+			return GL_R16I;
+		case PF_R16U:
+			return GL_R16UI;
+		case PF_R16S:
+			return GL_R16_SNORM;
+		case PF_R16:
+			return GL_R16;
+		case PF_RG16F:
+			return GL_RG16F;
+		case PF_RG16I:
+			return GL_RG16I;
+		case PF_RG16U:
+			return GL_RG16UI;
+		case PF_RG16S:
+			return GL_RG16_SNORM;
+		case PF_RG16:
+			return GL_RG16;
+		case PF_RGBA16F:
+			return GL_RGBA16F;
+		case PF_RGBA16I:
+			return GL_RGBA16I;
+		case PF_RGBA16U:
+			return GL_RGBA16UI;
+		case PF_RGBA16S:
+			return GL_RGBA16_SNORM;
+		case PF_RGBA16:
+			return GL_RGBA16;
+		case PF_R32F:
+			return GL_R32F;
+		case PF_R32I:
+			return GL_R32I;
+		case PF_R32U:
+			return GL_R32UI;
+		case PF_RG32F:
+			return GL_RG32F;
+		case PF_RG32I:
+			return GL_RG32I;
+		case PF_RG32U:
+			return GL_RG32UI;
+		case PF_RGB32F:
+			return GL_RGB32F;
+		case PF_RGB32I:
+			return GL_RGB32I;
+		case PF_RGB32U:
+			return GL_RGB32UI;
+		case PF_RGBA32F:
+			return GL_RGBA32F;
+		case PF_RGBA32I:
+			return GL_RGBA32I;
+		case PF_RGBA32U:
+			return GL_RGBA32UI;
+		case PF_BC1a:
+		case PF_BC1:
+			if (hwGamma)
+				return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
+			else
+				return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+		case PF_BC2:
+			if (hwGamma)
+				return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+			else
+				return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+		case PF_BC3:
+			if (hwGamma)
+				return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+			else
+				return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+		case PF_BC4:
+			return GL_COMPRESSED_RED_RGTC1;
+		case PF_BC5:
+			return GL_COMPRESSED_RG_RGTC2;
+		case PF_BC6H:
+			return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
+		case PF_BC7:
+			if (hwGamma)
+				return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
+			else
+				return GL_COMPRESSED_RGBA_BPTC_UNORM;
+		case PF_D16:
+			return GL_DEPTH_COMPONENT16;
+		case PF_D32:
+			return GL_DEPTH_COMPONENT32F;
+		case PF_D24S8:
+			return GL_DEPTH24_STENCIL8;
+		case PF_D32_S8X24:
+			return GL_DEPTH32F_STENCIL8;
+		case PF_RG11B10F:
+			return GL_R11F_G11F_B10F;
+		case PF_RGB10A2:
+			return GL_RGB10_A2;
+		default:
+			return GL_NONE;
+		}
+	}
 
 	GLenum GLPixelUtil::getDepthStencilTypeFromPF(PixelFormat mFormat)
 	{

+ 4 - 4
Source/BansheeGLRenderAPI/Source/BsGLRenderTexture.cpp

@@ -364,9 +364,9 @@ namespace bs
         PixelComponentType pct = PixelUtil::getElementType(format);
         switch(pct)
         {
-        case PCT_BYTE: format = PF_R8G8B8A8; break;
-        case PCT_FLOAT16: format = PF_FLOAT16_RGBA; break;
-        case PCT_FLOAT32: format = PF_FLOAT32_RGBA; break;
+        case PCT_BYTE: format = PF_RGBA8; break;
+        case PCT_FLOAT16: format = PF_RGBA16F; break;
+        case PCT_FLOAT32: format = PF_RGBA32F; break;
         default: break;
         }
 
@@ -374,7 +374,7 @@ namespace bs
             return format;
 
         // If none at all, return to default
-        return PF_R8G8B8A8;
+        return PF_RGBA8;
     }
 	}
 }

+ 1 - 1
Source/BansheeSL/Source/BsSLFXCompiler.cpp

@@ -1547,7 +1547,6 @@ namespace bs
 			return true;
 		};
 
-
 		// Actually parse techniques
 		for (auto& entry : techniqueData)
 		{
@@ -1559,6 +1558,7 @@ namespace bs
 			if (!parseInherited(metaData, entry.second))
 			{
 				parseStateDelete(parseState);
+				bs_stack_free(techniqueWasParsed);
 				return output;
 			}
 

+ 20 - 5
Source/BansheeUtility/Include/BsBitwise.h

@@ -168,22 +168,37 @@ namespace bs
 		}
 
 		/** 
-		 * Convert floating point color channel value between 0.0 and 1.0 (otherwise clamped) to integer of a certain 
-		 * number of bits. Works for any value of bits between 0 and 31.
+		 * Converts floating point value in range [0, 1] to an unsigned integer of a certain number of bits. Works for any
+		 * value of bits between 0 and 31.
 		 */
-		static unsigned int floatToFixed(const float value, const unsigned int bits)
+		static unsigned int unormToUint(float value, unsigned int bits)
 		{
 			if (value <= 0.0f) return 0;
 			else if (value >= 1.0f) return (1 << bits) - 1;
 			else return (unsigned int)(value * (1 << bits));
 		}
 
-		/** Fixed point to float. */
-		static float fixedToFloat(unsigned value, unsigned int bits)
+		/** 
+		 * Converts floating point value in range [-1, 1] to an unsigned integer of a certain number of bits. Works for any
+		 * value of bits between 0 and 31.
+		 */
+		static unsigned int snormToUint(float value, unsigned int bits)
+		{
+			return unormToUint((value + 1.0f) * 0.5f, bits);
+		}
+
+		/** Converts an unsigned integer to a floating point in range [0, 1]. */
+		static float uintToUnorm(unsigned value, unsigned int bits)
 		{
 			return (float)value / (float)((1 << bits) - 1);
 		}
 
+		/** Converts an unsigned integer to a floating point in range [-1, 1]. */
+		static float uintToSnorm(unsigned value, unsigned int bits)
+		{
+			return uintToUnorm(value, bits) * 2.0f - 1.0f;
+		}
+
 		/** Write a n*8 bits integer value to memory in native endian. */
 		static void intWrite(void *dest, const int n, const unsigned int value)
 		{

+ 25 - 1
Source/BansheeUtility/Include/BsStringID.h

@@ -101,6 +101,9 @@ namespace bs
 			return mData->chars;
 		}
 
+		/** Returns the unique identifier of the string. */
+		UINT32 id() const { return mData ? mData->id : -1; }
+
 		static const StringID NONE;
 
 	private:
@@ -193,4 +196,25 @@ namespace bs
 
 	/** @endcond */
 	/** @} */
-}
+}
+
+/** @cond STDLIB */
+/** @addtogroup String
+ *  @{
+ */
+
+namespace std
+{
+/**	Hash value generator for StringID. */
+template<>
+struct hash<bs::StringID>
+{
+	size_t operator()(const bs::StringID& value) const
+	{
+		return (size_t)value.id();
+	}
+};
+}
+
+/** @} */
+/** @endcond */

+ 9 - 0
Source/BansheeUtility/Include/BsVectorNI.h

@@ -27,6 +27,12 @@ namespace bs
 			memcpy(v, val, sizeof(v));
 		}
 
+		VectorNI(std::initializer_list<INT32> list)
+		{
+			assert(list.size() <= N);
+			std::copy(list.begin(), list.end(), v);
+		}
+
 		INT32 operator[] (size_t i) const
 		{
 			assert(i < N);
@@ -69,4 +75,7 @@ namespace bs
 
 	typedef VectorNI<3> Vector3I;
 	typedef VectorNI<4> Vector4I;
+
+	BS_ALLOW_MEMCPY_SERIALIZATION(Vector3I)
+	BS_ALLOW_MEMCPY_SERIALIZATION(Vector4I)
 }

+ 3 - 3
Source/BansheeVulkanRenderAPI/Source/BsVulkanTextureManager.cpp

@@ -40,7 +40,7 @@ namespace bs
 		PixelUtil::checkFormat(format, ttype, usage);
 
 		if (ct::VulkanUtility::getPixelFormat(format, hwGamma) == VK_FORMAT_UNDEFINED)
-			return PF_R8G8B8A8;
+			return PF_RGBA8;
 
 		return format;
 	}
@@ -54,7 +54,7 @@ namespace bs
 		int idx = 0;
 		for(auto& entry : DummyTexTypes)
 		{
-			SPtr<PixelData> pixelData = PixelData::create(entry.width, entry.height, entry.depth, PF_R8G8B8A8);
+			SPtr<PixelData> pixelData = PixelData::create(entry.width, entry.height, entry.depth, PF_RGBA8);
 
 			for(int depth = 0; depth < entry.depth; depth++)
 				for(int height = 0; height < entry.height; height++)
@@ -66,7 +66,7 @@ namespace bs
 			desc.width = entry.width;
 			desc.height = entry.height;
 			desc.depth = entry.depth;
-			desc.format = PF_R8G8B8A8;
+			desc.format = PF_RGBA8;
 			desc.usage = TU_STATIC;
 
 			mDummyReadTextures[idx] = std::static_pointer_cast<VulkanTexture>(createTexture(desc));

+ 74 - 18
Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

@@ -64,11 +64,11 @@ namespace bs { namespace ct
 				PixelUtil::getBitDepths(format, bitDepths);
 
 				if (bitDepths[0] == 16) // 16-bit format, fall back to 4-channel 16-bit, guaranteed to be supported
-					format = PF_FLOAT16_RGBA;
+					format = PF_RGBA16F;
 				else if(format == PF_BC6H) // Fall back to uncompressed alternative
-					format = PF_FLOAT16_RGBA;
+					format = PF_RGBA16F;
 				else // Must be 8-bit per channel format, compressed format or some uneven format
-					format = PF_R8G8B8A8;
+					format = PF_RGBA8;
 			}
 		}
 
@@ -84,42 +84,98 @@ namespace bs { namespace ct
 				return VK_FORMAT_R8_SRGB;
 
 			return VK_FORMAT_R8_UNORM;
-		case PF_R8G8:
+		case PF_RG8:
 			if (sRGB)
 				return VK_FORMAT_R8G8_SRGB;
 
 			return VK_FORMAT_R8G8_UNORM;
-		case PF_R8G8B8:
+		case PF_RGB8:
 			if (sRGB)
 				return VK_FORMAT_R8G8B8_SRGB;
 
 			return VK_FORMAT_R8G8B8_UNORM;
-		case PF_R8G8B8A8:
+		case PF_RGBA8:
 			if (sRGB)
 				return VK_FORMAT_R8G8B8A8_SRGB;
 
 			return VK_FORMAT_R8G8B8A8_UNORM;
-		case PF_B8G8R8A8:
+		case PF_BGRA8:
 			if (sRGB)
 				return VK_FORMAT_B8G8R8A8_SRGB;
 
 			return VK_FORMAT_B8G8R8A8_UNORM;
-		case PF_FLOAT16_R:
+		case PF_R8I:
+			return VK_FORMAT_R8_SINT;
+		case PF_RG8I:
+			return VK_FORMAT_R8G8_SINT;
+		case PF_RGBA8I:
+			return VK_FORMAT_R8G8B8A8_SINT;
+		case PF_R8U:
+			return VK_FORMAT_R8_UINT;
+		case PF_RG8U:
+			return VK_FORMAT_R8G8_UINT;
+		case PF_RGBA8U:
+			return VK_FORMAT_R8G8B8A8_UINT;
+		case PF_R8S:
+			return VK_FORMAT_R8_SNORM;
+		case PF_RG8S:
+			return VK_FORMAT_R8G8_SNORM;
+		case PF_RGBA8S:
+			return VK_FORMAT_R8G8B8A8_SNORM;
+		case PF_R16F:
 			return VK_FORMAT_R16_SFLOAT;
-		case PF_FLOAT16_RG:
+		case PF_RG16F:
 			return VK_FORMAT_R16G16_SFLOAT;
-		case PF_FLOAT16_RGB:
-			return VK_FORMAT_R16G16B16_SFLOAT;
-		case PF_FLOAT16_RGBA:
+		case PF_RGBA16F:
 			return VK_FORMAT_R16G16B16A16_SFLOAT;
-		case PF_FLOAT32_R:
+		case PF_R32F:
 			return VK_FORMAT_R32_SFLOAT;
-		case PF_FLOAT32_RG:
+		case PF_RG32F:
 			return VK_FORMAT_R32G32_SFLOAT;
-		case PF_FLOAT32_RGB:
+		case PF_RGB32F:
 			return VK_FORMAT_R32G32B32_SFLOAT;
-		case PF_FLOAT32_RGBA:
+		case PF_RGBA32F:
 			return VK_FORMAT_R32G32B32A32_SFLOAT;
+		case PF_R16I:
+			return VK_FORMAT_R16_SINT;
+		case PF_RG16I:
+			return VK_FORMAT_R16G16_SINT;
+		case PF_RGBA16I:
+			return VK_FORMAT_R16G16B16A16_SINT;
+		case PF_R16U:
+			return VK_FORMAT_R16_UINT;
+		case PF_RG16U:
+			return VK_FORMAT_R16G16_UINT;
+		case PF_RGBA16U:
+			return VK_FORMAT_R16G16B16A16_UINT;
+		case PF_R32I:
+			return VK_FORMAT_R32_SINT;
+		case PF_RG32I:
+			return VK_FORMAT_R32G32_SINT;
+		case PF_RGB32I:
+			return VK_FORMAT_R32G32B32_SINT;
+		case PF_RGBA32I:
+			return VK_FORMAT_R32G32B32A32_SINT;
+		case PF_R32U:
+			return VK_FORMAT_R32_UINT;
+		case PF_RG32U:
+			return VK_FORMAT_R32G32_UINT;
+		case PF_RGB32U:
+			return VK_FORMAT_R32G32B32_UINT;
+		case PF_RGBA32U:
+			return VK_FORMAT_R32G32B32A32_UINT;
+		case PF_R16S:
+			return VK_FORMAT_R16_SNORM;
+		case PF_RG16S:
+			return VK_FORMAT_R16G16_SNORM;
+		case PF_RGBA16S:
+			return VK_FORMAT_R16G16B16A16_SNORM;
+		case PF_R16:
+			return VK_FORMAT_R16_UNORM;
+		case PF_RG16:
+			return VK_FORMAT_R16G16_UNORM;
+		case PF_RGBA16:
+			return VK_FORMAT_R16G16B16A16_UNORM;
 		case PF_BC1:
 		case PF_BC1a:
 			if (sRGB)
@@ -155,9 +211,9 @@ namespace bs { namespace ct
 			return VK_FORMAT_D32_SFLOAT;
 		case PF_D16:
 			return VK_FORMAT_D16_UNORM;
-		case PF_FLOAT_R11G11B10:
+		case PF_RG11B10F:
 			return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
-		case PF_UNORM_R10G10B10A2:
+		case PF_RGB10A2:
 			return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
 		case PF_UNKNOWN:
 		default:

+ 1 - 0
Source/CMake/GenerateScriptBindings.cmake

@@ -73,6 +73,7 @@ if(BansheeSBGen_FOUND)
 			-output-cs-editor ${BS_GENERATED_CS_EDITOR_OUTPUT_DIR}
 			-- ${BS_INCLUDE_DIRS}
 			-DBS_STATIC_LIB
+			-DBS_SBGEN
 			-w)
 
 		message(STATUS "Generating script bindings, please wait...")

+ 10 - 3
Source/CMakeLists.txt

@@ -1,11 +1,11 @@
-cmake_minimum_required (VERSION 3.7.2)
+cmake_minimum_required (VERSION 3.9.0)
 project (Banshee)
 
 # Version
 set (BS_VERSION_MAJOR 0)
 set (BS_VERSION_MINOR 4)
 
-set (BS_PREBUILT_DEPENDENCIES_VERSION 4)
+set (BS_PREBUILT_DEPENDENCIES_VERSION 5)
 set (BS_SRC_DEPENDENCIES_VERSION 12)
 
 # Configuration types
@@ -300,6 +300,13 @@ if(MSVC)
 		include_external_msproject(MBansheeEngine ${PROJECT_SOURCE_DIR}/MBansheeEngine/MBansheeEngine.csproj)
 		include_external_msproject(MBansheeEditor ${PROJECT_SOURCE_DIR}/MBansheeEditor/MBansheeEditor.csproj)
 		
+		set_target_properties(MBansheeEngine PROPERTIES
+		  MAP_IMPORTED_CONFIG_RELEASE OptimizedDebug
+		)
+		set_target_properties(MBansheeEditor PROPERTIES
+		  MAP_IMPORTED_CONFIG_RELEASE OptimizedDebug
+		)
+		
 		set_property(TARGET MBansheeEngine PROPERTY FOLDER Script)
 		set_property(TARGET MBansheeEditor PROPERTY FOLDER Script)
 		
@@ -310,4 +317,4 @@ if(MSVC)
 	endif()
 else()
 # TODO - Use Mono compiler to build the managed code as a pre-build step
-endif()
+endif()

+ 2 - 1
Source/Examples/ExampleGettingStarted/Source/Main.cpp

@@ -390,7 +390,8 @@ namespace bs
 		HCamera guiCamera = guiSO->addComponent<CCamera>(window);
 
 		// Notify the renderer that the camera will only be used for overlays (e.g. GUI) so it can optimize its usage
-		guiCamera->setFlag(CameraFlag::Overlay, true);
+		SPtr<RenderSettings> settings = guiCamera->getRenderSettings();
+		settings->overlayOnly = true;
 
 		// Set up GUI camera properties. 
 		// We don't care about aspect ratio for GUI camera.

+ 2 - 2
Source/Examples/ExampleLowLevelRendering/Source/Main.cpp

@@ -247,7 +247,7 @@ namespace bs { namespace ct
 		gIndexBuffer->unlock();
 
 		// Create a simple 2x2 checkerboard texture to map to the object we're about to render
-		SPtr<PixelData> pixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		SPtr<PixelData> pixelData = PixelData::create(2, 2, 1, PF_RGBA8);
 		pixelData->setColorAt(Color::White, 0, 0);
 		pixelData->setColorAt(Color::Black, 1, 0);
 		pixelData->setColorAt(Color::White, 1, 1);
@@ -266,7 +266,7 @@ namespace bs { namespace ct
 		TEXTURE_DESC colorAttDesc;
 		colorAttDesc.width = windowResWidth;
 		colorAttDesc.height = windowResHeight;
-		colorAttDesc.format = PF_R8G8B8A8;
+		colorAttDesc.format = PF_RGBA8;
 		colorAttDesc.usage = TU_RENDERTARGET;
 
 		SPtr<Texture> colorAtt = Texture::create(colorAttDesc);

+ 1 - 1
Source/Examples/ExamplePhysicallyBasedShading/Source/Main.cpp

@@ -240,7 +240,7 @@ namespace bs
 
                 // Importing using a HDR format if requested
                 if (isHDR)
-                    importOptions->setFormat(PF_FLOAT_R11G11B10);
+                    importOptions->setFormat(PF_RG11B10F);
 			}
 
 			// Import texture with specified import options

+ 15 - 25
Source/MBansheeEditor/Inspectors/CameraInspector.cs

@@ -31,12 +31,11 @@ namespace BansheeEditor
         private GUIColorField clearColorField = new GUIColorField(new LocEdString("Clear color"));
         private GUIIntField priorityField = new GUIIntField(new LocEdString("Render priority"));
         private GUIListBoxField layersField = new GUIListBoxField(Layers.Names, true, new LocEdString("Layers"));
-        private GUIToggleField hdrField = new GUIToggleField(new LocEdString("HDR"));
         private GUIToggleField mainField = new GUIToggleField(new LocEdString("Main"));
 
-        private GUIToggle postProcessFoldout = new GUIToggle(new LocEdString("Post processing"), EditorStyles.Foldout);
-        private PostProcessSettingsGUI postProcessGUI;
-        private GUILayout postProcessLayout;
+        private GUIToggle renderSettingsFoldout = new GUIToggle(new LocEdString("Render settings"), EditorStyles.Foldout);
+        private RenderSettingsGUI renderSettingsGUI;
+        private GUILayout renderSettingsLayout;
 
         private ulong layersValue = 0;
         private InspectableState modifyState;
@@ -76,8 +75,7 @@ namespace BansheeEditor
             clearColorField.Value = camera.ClearColor;
             priorityField.Value = camera.Priority;
             mainField.Value = camera.Main;
-            hdrField.Value = camera.HDR;
-            postProcessGUI.Settings = camera.PostProcess;
+            renderSettingsGUI.Settings = camera.RenderSettings;
 
             if (layersValue != camera.Layers)
             {
@@ -223,13 +221,6 @@ namespace BansheeEditor
                     ConfirmModify();
                 };
 
-                hdrField.OnChanged += x =>
-                {
-                    camera.HDR = x;
-                    MarkAsModified();
-                    ConfirmModify();
-                };
-
                 Layout.AddElement(projectionTypeField);
                 Layout.AddElement(fieldOfView);
                 Layout.AddElement(orthoHeight);
@@ -255,27 +246,26 @@ namespace BansheeEditor
                 Layout.AddElement(priorityField);
                 Layout.AddElement(layersField);
                 Layout.AddElement(mainField);
-                Layout.AddElement(hdrField);
 
-                postProcessFoldout.OnToggled += x =>
+                renderSettingsFoldout.OnToggled += x =>
                 {
-                    Persistent.SetBool("postProcess_Expanded", x);
-                    postProcessLayout.Active = x;
+                    Persistent.SetBool("renderSettings_Expanded", x);
+                    renderSettingsLayout.Active = x;
                 };
-                Layout.AddElement(postProcessFoldout);
+                Layout.AddElement(renderSettingsFoldout);
 
-                postProcessLayout = Layout.AddLayoutX();
+                renderSettingsLayout = Layout.AddLayoutX();
                 {
-                    postProcessLayout.AddSpace(10);
+                    renderSettingsLayout.AddSpace(10);
 
-                    GUILayoutY contentsLayout = postProcessLayout.AddLayoutY();
-                    postProcessGUI = new PostProcessSettingsGUI(camera.PostProcess, contentsLayout, Persistent);
-                    postProcessGUI.OnChanged += x => { camera.PostProcess = x; MarkAsModified(); };
-                    postProcessGUI.OnConfirmed += ConfirmModify;
+                    GUILayoutY contentsLayout = renderSettingsLayout.AddLayoutY();
+                    renderSettingsGUI = new RenderSettingsGUI(camera.RenderSettings, contentsLayout, Persistent);
+                    renderSettingsGUI.OnChanged += x => { camera.RenderSettings = x; MarkAsModified(); };
+                    renderSettingsGUI.OnConfirmed += ConfirmModify;
                 }
 
                 ToggleTypeSpecificFields(camera.ProjectionType);
-                postProcessLayout.Active = Persistent.GetBool("postProcess_Expanded");
+                renderSettingsLayout.Active = Persistent.GetBool("renderSettings_Expanded");
             }
         }
 

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików