Sfoglia il codice sorgente

Merge branch 'master' of https://github.com/bearishsun/bansheeengine into shader-refactor

# Conflicts:
#	Data/Raw/Engine/Includes/NormalVertexInput.bslinc
#	Data/Raw/Engine/Includes/SkinnedVertexInput.bslinc
BearishSun 8 anni fa
parent
commit
7c1afc814f
71 ha cambiato i file con 2970 aggiunte e 1654 eliminazioni
  1. 6 1
      .editorconfig
  2. 12 0
      Data/Raw/Engine/DataList.json
  3. 21 0
      Data/Raw/Engine/Includes/NormalVertexInput.bslinc
  4. 114 0
      Data/Raw/Engine/Includes/ShadowDepthBase.bslinc
  5. 33 8
      Data/Raw/Engine/Includes/SkinnedVertexInput.bslinc
  6. 65 0
      Data/Raw/Engine/Shaders/ShadowDepthCube.bsl
  7. 4 0
      Data/Raw/Engine/Shaders/ShadowDepthNormal.bsl
  8. 0 0
      Dependencies/.gitkeep
  9. 10 1
      Documentation/GitHub/dependencies.md
  10. 3 0
      Source/BansheeCore/Include/BsAudioClip.h
  11. 0 7
      Source/BansheeCore/Include/BsCamera.h
  12. 24 0
      Source/BansheeCore/Include/BsCommonTypes.h
  13. 2 1
      Source/BansheeCore/Include/BsGameObject.h
  14. 16 1
      Source/BansheeCore/Include/BsLight.h
  15. 16 1
      Source/BansheeCore/Include/BsRenderable.h
  16. 8 0
      Source/BansheeCore/Include/BsResource.h
  17. 7 3
      Source/BansheeCore/Include/BsResources.h
  18. 6 2
      Source/BansheeCore/Include/BsSavedResourceData.h
  19. 7 13
      Source/BansheeCore/Include/BsSavedResourceDataRTTI.h
  20. 15 1
      Source/BansheeCore/Include/BsSceneObject.h
  21. 4 0
      Source/BansheeCore/Include/BsSceneObjectRTTI.h
  22. 23 9
      Source/BansheeCore/Source/BsLight.cpp
  23. 23 7
      Source/BansheeCore/Source/BsRenderable.cpp
  24. 94 13
      Source/BansheeCore/Source/BsResources.cpp
  25. 4 6
      Source/BansheeCore/Source/BsSavedResourceData.cpp
  26. 6 0
      Source/BansheeCore/Source/BsSceneManager.cpp
  27. 66 16
      Source/BansheeCore/Source/BsSceneObject.cpp
  28. 12 12
      Source/BansheeFontImporter/Source/BsFontImporter.cpp
  29. 8 1
      Source/BansheeUtility/CMakeLists.txt
  30. 4 2
      Source/BansheeUtility/CMakeSources.cmake
  31. 190 190
      Source/BansheeUtility/Include/BsBitwise.h
  32. 25 0
      Source/BansheeUtility/Include/BsCompression.h
  33. 12 1
      Source/BansheeUtility/Include/BsConvexVolume.h
  34. 8 1
      Source/BansheeUtility/Include/BsDataStream.h
  35. 3 2
      Source/BansheeUtility/Include/BsDebug.h
  36. 2 1
      Source/BansheeUtility/Include/BsFileSerializer.h
  37. 17 13
      Source/BansheeUtility/Include/BsMatrix4.h
  38. 0 111
      Source/BansheeUtility/Include/BsTexAtlasGenerator.h
  39. 135 0
      Source/BansheeUtility/Include/BsTextureAtlasLayout.h
  40. 194 0
      Source/BansheeUtility/Source/BsCompression.cpp
  41. 70 44
      Source/BansheeUtility/Source/BsConvexVolume.cpp
  42. 112 102
      Source/BansheeUtility/Source/BsDataStream.cpp
  43. 2 1
      Source/BansheeUtility/Source/BsDebug.cpp
  44. 1 1
      Source/BansheeUtility/Source/BsFileSerializer.cpp
  45. 11 3
      Source/BansheeUtility/Source/BsMatrix4.cpp
  46. 0 332
      Source/BansheeUtility/Source/BsTexAtlasGenerator.cpp
  47. 187 0
      Source/BansheeUtility/Source/BsTextureAtlasLayout.cpp
  48. 44 0
      Source/CMake/Modules/FindSnappy.cmake
  49. 3 3
      Source/CMakeLists.txt
  50. 12 1
      Source/MBansheeEditor/Windows/Inspector/InspectorWindow.cs
  51. 7 3
      Source/MBansheeEngine/Scene/GameObject.cs
  52. 18 0
      Source/MBansheeEngine/Scene/SceneObject.cs
  53. 4 2
      Source/RenderBeast/CMakeSources.cmake
  54. 1 1
      Source/RenderBeast/Include/BsImageBasedLighting.h
  55. 3 3
      Source/RenderBeast/Include/BsLightGrid.h
  56. 1 1
      Source/RenderBeast/Include/BsPostProcessing.h
  57. 8 50
      Source/RenderBeast/Include/BsRenderBeast.h
  58. 1 1
      Source/RenderBeast/Include/BsRenderBeastPrerequisites.h
  59. 1 1
      Source/RenderBeast/Include/BsRenderTargets.h
  60. 126 0
      Source/RenderBeast/Include/BsRendererScene.h
  61. 65 72
      Source/RenderBeast/Include/BsRendererView.h
  62. 52 0
      Source/RenderBeast/Include/BsShadowRendering.h
  63. 4 4
      Source/RenderBeast/Source/BsLightGrid.cpp
  64. 7 5
      Source/RenderBeast/Source/BsPostProcessing.cpp
  65. 91 551
      Source/RenderBeast/Source/BsRenderBeast.cpp
  66. 668 0
      Source/RenderBeast/Source/BsRendererScene.cpp
  67. 66 48
      Source/RenderBeast/Source/BsRendererView.cpp
  68. 183 0
      Source/RenderBeast/Source/BsShadowRendering.cpp
  69. 3 0
      Source/SBansheeEngine/Include/BsScriptSceneObject.h
  70. 2 1
      Source/SBansheeEngine/Source/BsScriptComponent.cpp
  71. 18 0
      Source/SBansheeEngine/Source/BsScriptSceneObject.cpp

+ 6 - 1
.editorconfig

@@ -1,3 +1,8 @@
+root = true;
+
 [*]
 indent_style = tab
-indent_size = 4
+indent_size = 4
+
+[*.cs]
+indent_style = space

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

@@ -127,6 +127,10 @@
         {
             "Path": "GBufferOutput.bslinc",
             "UUID": "46df32e8-6400-46d5-9ac2-beefdc9016ad"
+        },
+        {
+            "Path": "ShadowDepthBase.bslinc",
+            "UUID": "ff6f2a9d-6766-4f80-984b-c159a5fd3c5e"
         }
     ],
     "Shaders": [
@@ -245,6 +249,14 @@
         {
             "Path": "TiledDeferredImageBasedLighting.bsl",
             "UUID": "6029db14-107f-43df-9a33-7105c56aa0fd"
+        },
+        {
+            "Path": "ShadowDepthCube.bsl",
+            "UUID": "285c7b78-053a-4763-899e-8e11b5d4dac7"
+        },
+        {
+            "Path": "ShadowDepthNormal.bsl",
+            "UUID": "c9b64475-375d-410e-8cd4-e1b1181318d4"
         }
     ],
     "Skin": [

+ 21 - 0
Data/Raw/Engine/Includes/NormalVertexInput.bslinc

@@ -29,6 +29,16 @@ mixin NormalVertexInput
 			#endif				
 		};
 		
+		// Vertex input containing only position data
+		struct VertexInput_PO
+		{
+			float3 position : POSITION;
+			
+			#ifdef USE_BLEND_SHAPES
+				float3 deltaPosition : POSITION1;
+			#endif	
+		};			
+		
 		struct VertexIntermediate
 		{
 			float3 worldNormal; // Note: Half-precision could be used
@@ -84,6 +94,17 @@ mixin NormalVertexInput
 			return mul(gMatWorld, position);
 		}
 		
+		float4 getVertexWorldPosition(VertexInput_PO input)
+		{
+			#ifdef USE_BLEND_SHAPES
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			return mul(gMatWorld, position);
+		}		
+		
 		void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result)
 		{
 			result.uv0 = input.uv0;

+ 114 - 0
Data/Raw/Engine/Includes/ShadowDepthBase.bslinc

@@ -0,0 +1,114 @@
+#include "$ENGINE$\GBufferOutput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\PerObjectData.bslinc"
+
+#include "$ENGINE$\SkinnedVertexInput.bslinc"
+#include "$ENGINE$\NormalVertexInput.bslinc"
+#define USE_BLEND_SHAPES
+#include "$ENGINE$\SkinnedVertexInput.bslinc"
+#include "$ENGINE$\NormalVertexInput.bslinc"
+#undef USE_BLEND_SHAPES
+
+Technique : base("ShadowDepthBase") =
+{
+	Pass =
+	{
+		Common =
+		{
+			struct ShadowVStoFS
+			{
+				float4 position : SV_Position;
+				
+				#ifdef USES_GS
+				float4 worldPos : TEXCOORD0;
+				#endif
+			};
+			
+			cbuffer ShadowParams
+			{
+				float gDepthBias;
+				float gDepthRange;
+			};
+		};
+	
+		Vertex =
+		{
+			void linearizeDepth(inout float4 clipPos)
+			{
+				// Clamp to near plane if behind it
+				if (clipPos.z < 0)
+				{
+					clipPos.z = 0.000001f;
+					clipPos.w = 1.0f;
+				}
+
+				// Output linear depth in range [0, 1]
+				// TODO - Handle case for backends using [-1, 1] depth range
+				float linearDepth = clipPos.z * gDepthRange + gDepthBias;
+				clipPos.z = linearDepth * clipPos.w;
+			}		
+		
+			ShadowVStoFS main(VertexInput_PO input)
+			{
+				ShadowVStoFS output;
+			
+				float4 worldPosition = getVertexWorldPosition(input);
+				
+				#ifdef USES_GS
+				output.worldPos = worldPosition;
+				output.position = worldPosition;
+				#else
+				float4 clipPos = mul(gMatViewProj, worldPosition);
+				linearizeDepth(clipPos);
+				
+				output.position = clipPos;
+				#endif
+				
+				return output;
+			}
+		};
+	};
+};
+
+Technique 
+ : inherits("GBufferOutput")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("NormalVertexInput")
+ : inherits("ShadowDepthBase")
+ : inherits("ShadowDepth") =
+{
+};
+
+Technique 
+ : inherits("GBufferOutput")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("SkinnedVertexInput")
+ : inherits("ShadowDepthBase")
+ : inherits("ShadowDepth") =
+{
+	Tags = { "Skinned" };
+};
+
+Technique 
+ : inherits("GBufferOutput")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("MorphVertexInput")
+ : inherits("ShadowDepthBase")
+ : inherits("ShadowDepth") =
+{
+	Tags = { "Morph" };
+};
+
+Technique 
+ : inherits("GBufferOutput")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("SkinnedMorphVertexInput")
+ : inherits("ShadowDepthBase")
+ : inherits("ShadowDepth") =
+{
+	Tags = { "SkinnedMorph" };
+};

+ 33 - 8
Data/Raw/Engine/Includes/SkinnedVertexInput.bslinc

@@ -33,6 +33,18 @@ mixin SkinnedVertexInput
 			#endif
 		};
 		
+		// Vertex input containing only position data
+		struct VertexInput_PO
+		{
+			float3 position : POSITION;
+			uint4 blendIndices : BLENDINDICES;
+			float4 blendWeights : BLENDWEIGHT;
+			
+			#ifdef USE_BLEND_SHAPES
+				float3 deltaPosition : POSITION1;
+			#endif
+		};		
+		
 		struct VertexIntermediate
 		{
 			float3x4 blendMatrix;
@@ -50,12 +62,12 @@ mixin SkinnedVertexInput
 			return float3x4(row0, row1, row2);
 		}
 		
-		float3x4 getBlendMatrix(VertexInput input)
+		float3x4 getBlendMatrix(float4 blendWeights, uint4 blendIndices)
 		{
-			float3x4 result = input.blendWeights.x * getBoneMatrix(input.blendIndices.x);
-			result += input.blendWeights.y * getBoneMatrix(input.blendIndices.y);
-			result += input.blendWeights.z * getBoneMatrix(input.blendIndices.z);
-			result += input.blendWeights.w * getBoneMatrix(input.blendIndices.w);
+			float3x4 result = blendWeights.x * getBoneMatrix(blendIndices.x);
+			result += blendWeights.y * getBoneMatrix(blendIndices.y);
+			result += blendWeights.z * getBoneMatrix(blendIndices.z);
+			result += blendWeights.w * getBoneMatrix(blendIndices.w);
 			
 			return result;
 		}
@@ -66,7 +78,7 @@ mixin SkinnedVertexInput
 		
 			float3 normal = input.normal * 2.0f - 1.0f;
 			float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
-			
+
 			#ifdef USE_BLEND_SHAPES
 				float3 deltaNormal = (input.deltaNormal.xyz * 2.0f - 1.0f) * 2.0f;
 				normal = normalize(normal + deltaNormal * input.deltaNormal.w);
@@ -89,12 +101,12 @@ mixin SkinnedVertexInput
 		{
 			VertexIntermediate result;
 			
-			result.blendMatrix = getBlendMatrix(input);
+			result.blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
 			
 			float tangentSign;
 			float3x3 tangentToLocal = getSkinnedTangentToLocal(input, result.blendMatrix, tangentSign);
 			float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
-			
+
 			// Note: Consider transposing these externally, for easier reads
 			result.worldNormal = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
 			result.worldTangent = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
@@ -114,6 +126,19 @@ mixin SkinnedVertexInput
 			return mul(gMatWorld, position);
 		}
 		
+		float4 getVertexWorldPosition(VertexInput_PO input)
+		{
+			#ifdef USE_BLEND_SHAPES
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif
+		
+			float3x4 blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
+			position = float4(mul(blendMatrix, position), 1.0f);
+			return mul(gMatWorld, position);
+		}		
+		
 		void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result)
 		{
 			result.uv0 = input.uv0;

+ 65 - 0
Data/Raw/Engine/Shaders/ShadowDepthCube.bsl

@@ -0,0 +1,65 @@
+#define USES_GS
+#include "$ENGINE$\ShadowDepthBase.bslinc"
+
+Technique : base("ShadowDepth") =
+{ 
+	Pass =
+	{
+		Geometry =
+		{
+			struct GSToPS
+			{
+				float4 position : SV_Position;
+				uint targetIdx : SV_RenderTargetArrayIndex;
+			};
+
+			cbuffer ShadowCubeParams
+			{
+				float4x4 gFaceVPMatrices[6];
+				uint gFaceMasks[6];
+				uint padding[2];
+			};
+			
+			[maxvertexcount(18)]
+			void main(triangle ShadowVStoFS inputs[3], inout TriangleStream<GSToPS> outStream)
+			{
+				// Output a triangle to all relevant faces
+				[unroll]
+				for (int faceIdx = 0; faceIdx < 6; faceIdx++)
+				{
+					// Check the per-object masks that were determined based on CPU frustum culling
+					[branch]
+					if (gFaceMasks[faceIdx] > 0)
+					{
+						float4 clipPos[3];
+						
+						[unroll]
+						for (int vertIdx = 0; vertIdx < 3; vertIdx++)
+							clipPos[vertIdx] = mul(gFaceVPMatrices[faceIdx], inputs[vertIdx].worldPos);
+
+						// Per-triangle frustum culling
+						// Note: Test if this helps or hurts performance
+						float4 testMask = saturate(clipPos[0].xyxy * float4(-1, -1, 1, 1) - clipPos[0].w);
+						testMask *= saturate(clipPos[1].xyxy * float4(-1, -1, 1, 1) - clipPos[1].w);
+						testMask *= saturate(clipPos[2].xyxy * float4(-1, -1, 1, 1) - clipPos[2].w);
+
+						[branch]	
+						if (all(testMask == 0))
+						{				
+							GSToPS output;
+							output.targetIdx = faceIdx;
+
+							[unroll]
+							for (int vertIdx = 0; vertIdx < 3; vertIdx++)
+							{
+								output.position = clipPos[vertIdx];
+								outStream.Append(output);
+							}
+							outStream.RestartStrip();
+						}
+					}
+				}
+			}
+		};
+	};
+};

+ 4 - 0
Data/Raw/Engine/Shaders/ShadowDepthNormal.bsl

@@ -0,0 +1,4 @@
+#include "$ENGINE$\ShadowDepthBase.bslinc"
+
+Technique : base("ShadowDepth") =
+{ };

+ 0 - 0
Dependencies/.gitkeep


+ 10 - 1
Documentation/GitHub/dependencies.md

@@ -15,7 +15,7 @@ Each library below lists a set of outputs required by Banshee. After you compile
    
 Legend:
 - (BansheeSource) - root directory of Banshee's source code
-- (LibName) - name of the library (title of the each library shown below)
+- (LibName) - name of the library (title of each library shown below)
 - (Platform) - x86 for 32-bit builds, x64 for 64-bit builds
 - (Configuration) - Debug, OptimizedDebug or Release
    
@@ -23,6 +23,15 @@ If the library structure still isn't clear, download one of the pre-compiled dep
       
 ## Library list 
 	  
+**snappy**
+- Google's Snappy compressor/decompressor
+- https://github.com/BearishSun/snappy
+- Required by BansheeUtility
+- Outputs:
+  - Windows (Static library):
+    - /Debug/Snappy.lib (Debug configuration)
+    - /Release/Snappy.lib (Release configuration)
+	  
 **nvtt**
 - NVIDIA Texture Tools 2.1.0 (commit e85d851cd9502d77c6e20769d8c21baa3a94ac18)
 - https://github.com/castano/nvidia-texture-tools

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

@@ -141,6 +141,9 @@ namespace bs
 		/** @copydoc Resource::initialize */
 		void initialize() override;
 
+		/** @copydoc Resource::isCompressible */
+		bool isCompressible() const override { return false; } // Compression handled on a case by case basis manually by the audio system
+
 		/** Returns original audio data. Only available if @p keepSourceData has been provided on creation. */
 		virtual SPtr<DataStream> getSourceStream(UINT32& size) = 0;
 

+ 0 - 7
Source/BansheeCore/Include/BsCamera.h

@@ -29,13 +29,6 @@ namespace bs
 		PostProcess = 1<<2
 	};
 
-	/**	Projection type to use by the camera. */
-    enum ProjectionType
-    {
-		PT_ORTHOGRAPHIC, /**< Projection type where object size remains constant and parallel lines remain parallel. */
-		PT_PERSPECTIVE /**< Projection type that emulates human vision. Objects farther away appear smaller. */
-    };
-
 	/**	Flags that describe a camera. */
 	enum class CameraFlag
 	{

+ 24 - 0
Source/BansheeCore/Include/BsCommonTypes.h

@@ -344,6 +344,13 @@ namespace bs
 		TEX_TYPE_CUBE_MAP	BS_SCRIPT_EXPORT(n:TextureCube) = 4 
 	};
 
+	/**	Projection type to use by the camera. */
+	enum ProjectionType
+	{
+		PT_ORTHOGRAPHIC, /**< Projection type where object size remains constant and parallel lines remain parallel. */
+		PT_PERSPECTIVE /**< Projection type that emulates human vision. Objects farther away appear smaller. */
+	};
+
 	/**	Contains data about a type used for GPU data parameters. */
 	struct GpuParamDataTypeInfo
 	{
@@ -539,6 +546,23 @@ namespace bs
 	typedef Flags<RenderSurfaceMaskBits> RenderSurfaceMask;
 	BS_FLAGS_OPERATORS(RenderSurfaceMaskBits);
 
+	/** 
+	 * Controls what kind of mobility restrictions a scene object has. This is used primarily as a performance hint to
+	 * other systems. Generally the more restricted the mobility the higher performance can be achieved.
+	 */
+	enum class BS_SCRIPT_EXPORT() ObjectMobility
+	{
+		/** Scene object can be moved and has no mobility restrictions. */
+		Movable,
+		/** 
+		 * Scene object isn't allowed to be moved but is allowed to be visually changed in other ways (e.g. changing the
+		 * displayed mesh or light intensity (depends on attached components).
+		 */
+		Immovable,
+		/** Scene object isn't allowed to be moved nor is it allowed to be visually changed. Object must be fully static. */
+		Static
+	};
+
 	/**	Texture addressing mode, per component. */
 	struct UVWAddressingMode
 	{

+ 2 - 1
Source/BansheeCore/Include/BsGameObject.h

@@ -16,7 +16,8 @@ namespace bs
 	{
 		TCF_None = 0x00, /**< Component will not be notified about any events relating to the transform. */
 		TCF_Transform = 0x01, /**< Component will be notified when the its position, rotation or scale has changed. */
-		TCF_Parent = 0x02 /**< Component will be notified when its parent changes. */
+		TCF_Parent = 0x02, /**< Component will be notified when its parent changes. */
+		TCF_Mobility = 0x04 /**< Component will be notified when mobility state changes. */
 	};
 
 	/** @} */

+ 16 - 1
Source/BansheeCore/Include/BsLight.h

@@ -28,7 +28,8 @@ namespace bs
 	enum class LightDirtyFlag
 	{
 		Transform = 0x01,
-		Everything = 0x02
+		Everything = 0x02,
+		Mobility = 0x04
 	};
 
 	/** @} */
@@ -161,6 +162,19 @@ namespace bs
 		/**	Sets whether the light should be rendered or not. */
 		void setIsActive(bool active) { mIsActive = active; _markCoreDirty(); }
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+
 		/** 
 		 * Marks the simulation thread object as dirty and notifies the system its data should be synced with its core 
 		 * thread counterpart. 
@@ -188,6 +202,7 @@ namespace bs
 		bool mIsActive; /**< Whether the light should be rendered or not. */
 		Sphere mBounds; /**< Sphere that bounds the light area of influence. */
 		bool mAutoAttenuation; /**< Determines is attenuation radius is automatically determined. */
+		ObjectMobility mMobility; /**< Determines if there are any restrictions placed on light movement. */
 	};
 
 	/** @} */

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

@@ -21,7 +21,8 @@ namespace bs
 	enum class RenderableDirtyFlag
 	{
 		Transform = 0x01,
-		Everything = 0x02
+		Everything = 0x02,
+		Mobility = 0x04
 	};
 
 	/** Type of animation that can be applied to a renderable object. */
@@ -93,6 +94,19 @@ namespace bs
 		/**	Sets whether the object should be rendered or not. */
 		void setIsActive(bool active);
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+	
 		/** 
 		 * Sets bounds that will be used when determining if object is visible. Only relevant if setUseOverrideBounds() is
 		 * set to true.
@@ -164,6 +178,7 @@ namespace bs
 		Matrix4 mTransformNoScale;
 		bool mIsActive;
 		RenderableAnimType mAnimType;
+		ObjectMobility mMobility;
 	};
 
 	/** @} */

+ 8 - 0
Source/BansheeCore/Include/BsResource.h

@@ -41,6 +41,14 @@ namespace bs
 		/**	Checks if all the resources this object is dependent on are fully loaded. */
 		bool areDependenciesLoaded() const;
 
+		/** 
+		 * Returns true if the resource can be compressed using a generic compression when saved on a storage device. 
+		 * Certain resources already have their contents compressed (like audio files) and will not benefit from further 
+		 * compression. Resources supporting streaming should never be compressed, instead such resources can handle
+		 * compression/decompression locally through their streams.
+		 */
+		virtual bool isCompressible() const { return true; }
+
 		UINT32 mSize;
 		SPtr<ResourceMetaData> mMetaData;
 

+ 7 - 3
Source/BansheeCore/Include/BsResources.h

@@ -167,7 +167,9 @@ namespace bs
 		 *
 		 * @param[in]	resource 	Handle to the resource.
 		 * @param[in]	filePath 	Full pathname of the file to save as.
-		 * @param[in]	overwrite	(optional) If true, any existing resource at the specified location will be overwritten.
+		 * @param[in]	overwrite	If true, any existing resource at the specified location will be overwritten.
+		 * @param[in]	compress	Should the resource be compressed before saving. Some resource have data that is already
+		 *							compressed and this option will be ignored for such resources.
 		 * 			
 		 * @note
 		 * If the resource is a GpuResource and you are in some way modifying it from the core thread, make sure all those
@@ -176,12 +178,14 @@ namespace bs
 		 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread 
 		 * and the GPU in order to read the resource.
 		 */
-		void save(const HResource& resource, const Path& filePath, bool overwrite);
+		void save(const HResource& resource, const Path& filePath, bool overwrite, bool compress = false);
 
 		/**
 		 * Saves an existing resource to its previous location.
 		 *
 		 * @param[in]	resource 	Handle to the resource.
+		 * @param[in]	compress	Should the resource be compressed before saving. Some resource have data that is already
+		 *							compressed and this option will be ignored for such resources.
 		 *
 		 * @note	
 		 * If the resource is a GpuResource and you are in some way modifying it from the Core thread, make sure all those
@@ -190,7 +194,7 @@ namespace bs
 		 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread 
 		 * and the GPU in order to read the resource.
 		 */
-		void save(const HResource& resource);
+		void save(const HResource& resource, bool compress = false);
 
 		/**
 		 * Updates an existing resource handle with a new resource. Caller must ensure that new resource type matches the 

+ 6 - 2
Source/BansheeCore/Include/BsSavedResourceData.h

@@ -20,7 +20,7 @@ namespace bs
 	{
 	public:
 		SavedResourceData();
-		SavedResourceData(const Vector<String>& dependencies, bool allowAsync);
+		SavedResourceData(const Vector<String>& dependencies, bool allowAsync, UINT32 compressionMethod);
 
 		/**	Returns a list of all resource dependencies. */
 		const Vector<String>& getDependencies() const { return mDependencies; }
@@ -28,9 +28,13 @@ namespace bs
 		/**	Returns true if this resource is allow to be asynchronously loaded. */
 		bool allowAsyncLoading() const { return mAllowAsync; }
 
+		/** Returns the method used for compressing the resource. 0 if none. */
+		UINT32 getCompressionMethod() const { return mCompressionMethod; }
+
 	private:
 		Vector<String> mDependencies;
 		bool mAllowAsync;
+		UINT32 mCompressionMethod;
 
 	/************************************************************************/
 	/* 								SERIALIZATION                      		*/
@@ -38,7 +42,7 @@ namespace bs
 	public:
 		friend class SavedResourceDataRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 7 - 13
Source/BansheeCore/Include/BsSavedResourceDataRTTI.h

@@ -16,22 +16,16 @@ namespace bs
 	class BS_CORE_EXPORT SavedResourceDataRTTI : public RTTIType <SavedResourceData, IReflectable, SavedResourceDataRTTI>
 	{
 	private:
-		String& getDependency(SavedResourceData* obj, UINT32 arrayIdx) { return obj->mDependencies[arrayIdx]; }
-		void setDependency(SavedResourceData* obj, UINT32 arrayIdx, String& val) { obj->mDependencies[arrayIdx] = val; }
-
-		UINT32 getNumDependencies(SavedResourceData* obj) { return (UINT32)obj->mDependencies.size(); }
-		void setNumDependencies(SavedResourceData* obj, UINT32 numEntries) { obj->mDependencies = Vector<String>(numEntries); }
-
-		bool& getAllowAsyncLoading(SavedResourceData* obj) { return obj->mAllowAsync; }
-		void setAllowAsyncLoading(SavedResourceData* obj, bool& value) { obj->mAllowAsync = value; }
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN_ARRAY(mDependencies, 0)
+			BS_RTTI_MEMBER_PLAIN(mAllowAsync, 1)
+			BS_RTTI_MEMBER_PLAIN(mCompressionMethod, 2)
+		BS_END_RTTI_MEMBERS
 
 	public:
 		SavedResourceDataRTTI()
-		{
-			addPlainArrayField("mDependencies", 0, &SavedResourceDataRTTI::getDependency, &SavedResourceDataRTTI::getNumDependencies,
-				&SavedResourceDataRTTI::setDependency, &SavedResourceDataRTTI::setNumDependencies);
-			addPlainField("mAllowAsync", 1, &SavedResourceDataRTTI::getAllowAsyncLoading, &SavedResourceDataRTTI::setAllowAsyncLoading);
-		}
+			:mInitMembers(this)
+		{ }
 
 		const String& getRTTIName() override
 		{

+ 15 - 1
Source/BansheeCore/Include/BsSceneObject.h

@@ -361,7 +361,7 @@ namespace bs
 		/** 
 		 * Notifies components and child scene object that a transform has been changed.  
 		 * 
-		 * @param	flags	Specifies in what way was the transform changed.
+		 * @param	flags		Specifies in what way was the transform changed.
 		 */
 		void notifyTransformChanged(TransformChangedFlags flags) const;
 
@@ -455,6 +455,19 @@ namespace bs
 		 */
 		bool getActive(bool self = false);
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+
 		/**
 		 * Makes a deep copy of this object.
 		 * 			
@@ -468,6 +481,7 @@ namespace bs
 		Vector<HSceneObject> mChildren;
 		bool mActiveSelf;
 		bool mActiveHierarchy;
+		ObjectMobility mMobility;
 
 		/**
 		 * Internal version of setParent() that allows you to set a null parent.

+ 4 - 0
Source/BansheeCore/Include/BsSceneObjectRTTI.h

@@ -78,6 +78,9 @@ namespace bs
 
 		UINT32& getPrefabHash(SceneObject* obj) { return obj->mPrefabHash; }
 		void setPrefabHash(SceneObject* obj, UINT32& value) { obj->mPrefabHash = value; }
+
+		ObjectMobility& getMobility(SceneObject* obj) { return obj->mMobility; }
+		void setMobility(SceneObject* obj, ObjectMobility& value) { obj->mMobility = value; }
 	public:
 		SceneObjectRTTI()
 		{
@@ -93,6 +96,7 @@ namespace bs
 			addPlainField("mRotation", 7, &SceneObjectRTTI::getRotation, &SceneObjectRTTI::setRotation);
 			addPlainField("mScale", 8, &SceneObjectRTTI::getScale, &SceneObjectRTTI::setScale);
 			addPlainField("mActiveSelf", 9, &SceneObjectRTTI::getActive, &SceneObjectRTTI::setActive);
+			addPlainField("mMobility", 10, &SceneObjectRTTI::getMobility, &SceneObjectRTTI::setMobility);
 		}
 
 		void onDeserializationStarted(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override

+ 23 - 9
Source/BansheeCore/Source/BsLight.cpp

@@ -12,7 +12,7 @@ namespace bs
 	LightBase::LightBase()
 		: mPosition(BsZero), mRotation(BsIdentity), mType(LightType::Radial), mCastsShadows(false), mColor(Color::White)
 		, mAttRadius(10.0f), mSourceRadius(0.0f), mIntensity(5.0f), mSpotAngle(45), mSpotFalloffAngle(35.0f)
-		, mIsActive(true), mAutoAttenuation(true)
+		, mIsActive(true), mAutoAttenuation(true), mMobility(ObjectMobility::Movable)
 	{
 		updateAttenuationRange();
 	}
@@ -21,7 +21,7 @@ namespace bs
 		Degree spotAngle, Degree spotFalloffAngle)
 		: mPosition(BsZero), mRotation(BsIdentity), mType(type), mCastsShadows(castsShadows), mColor(color)
 		, mAttRadius(attRadius), mSourceRadius(srcRadius), mIntensity(intensity), mSpotAngle(spotAngle)
-		, mSpotFalloffAngle(spotFalloffAngle), mIsActive(true), mAutoAttenuation(true)
+		, mSpotFalloffAngle(spotFalloffAngle), mIsActive(true), mAutoAttenuation(true), mMobility(ObjectMobility::Movable)
 	{
 		updateAttenuationRange();
 	}
@@ -165,6 +165,13 @@ namespace bs
 		}
 	}
 
+	void LightBase::setMobility(ObjectMobility mobility)
+	{
+		mMobility = mobility;
+
+		_markCoreDirty(LightDirtyFlag::Mobility);
+	}
+
 	Light::Light()
 		:mLastUpdateHash(0)
 	{
@@ -233,6 +240,7 @@ namespace bs
 		size += rttiGetElemSize(mIsActive);
 		size += rttiGetElemSize(getCoreDirtyFlags());
 		size += rttiGetElemSize(mBounds);
+		size += rttiGetElemSize(mMobility);
 
 		UINT8* buffer = allocator->alloc(size);
 
@@ -251,6 +259,7 @@ namespace bs
 		dataPtr = rttiWriteElem(mIsActive, dataPtr);
 		dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
 		dataPtr = rttiWriteElem(mBounds, dataPtr);
+		dataPtr = rttiWriteElem(mMobility, dataPtr);
 
 		return CoreSyncData(buffer, size);
 	}
@@ -328,15 +337,11 @@ namespace bs
 		dataPtr = rttiReadElem(mIsActive, dataPtr);
 		dataPtr = rttiReadElem(dirtyFlags, dataPtr);
 		dataPtr = rttiReadElem(mBounds, dataPtr);
+		dataPtr = rttiReadElem(mMobility, dataPtr);
 
 		updateBounds();
 
-		if (dirtyFlags == (UINT32)LightDirtyFlag::Transform)
-		{
-			if (mIsActive)
-				gRenderer()->notifyLightUpdated(this);
-		}
-		else
+		if((dirtyFlags & (UINT32)LightDirtyFlag::Everything) != 0)
 		{
 			if (oldIsActive != mIsActive)
 			{
@@ -360,6 +365,15 @@ namespace bs
 				gRenderer()->notifyLightAdded(this);
 			}
 		}
+		else if((dirtyFlags & (UINT32)LightDirtyFlag::Mobility) != 0)
+		{
+			gRenderer()->notifyLightRemoved(this);
+			gRenderer()->notifyLightAdded(this);
+		}
+		else if ((dirtyFlags & (UINT32)LightDirtyFlag::Transform) != 0)
+		{
+			if (mIsActive)
+				gRenderer()->notifyLightUpdated(this);
+		}
 	}
-
 }}

+ 23 - 7
Source/BansheeCore/Source/BsRenderable.cpp

@@ -27,7 +27,7 @@ namespace bs
 	template<bool Core>
 	TRenderable<Core>::TRenderable()
 		: mLayer(1), mUseOverrideBounds(false), mPosition(BsZero), mTransform(BsIdentity), mTransformNoScale(BsIdentity)
-		, mIsActive(true), mAnimType(RenderableAnimType::None)
+		, mIsActive(true), mAnimType(RenderableAnimType::None), mMobility(ObjectMobility::Movable)
 	{
 		mMaterials.resize(1);
 	}
@@ -124,6 +124,14 @@ namespace bs
 		_markCoreDirty();
 	}
 
+	template <bool Core>
+	void TRenderable<Core>::setMobility(ObjectMobility mobility)
+	{
+		mMobility = mobility;
+
+		_markCoreDirty(RenderableDirtyFlag::Mobility);
+	}
+
 	template<bool Core>
 	void TRenderable<Core>::setOverrideBounds(const AABox& bounds)
 	{
@@ -314,6 +322,7 @@ namespace bs
 			rttiGetElemSize(mIsActive) +
 			rttiGetElemSize(animationId) +
 			rttiGetElemSize(mAnimType) + 
+			rttiGetElemSize(mMobility) +
 			rttiGetElemSize(getCoreDirtyFlags()) +
 			sizeof(SPtr<ct::Mesh>) +
 			numMaterials * sizeof(SPtr<ct::Material>);
@@ -330,6 +339,7 @@ namespace bs
 		dataPtr = rttiWriteElem(mIsActive, dataPtr);
 		dataPtr = rttiWriteElem(animationId, dataPtr);
 		dataPtr = rttiWriteElem(mAnimType, dataPtr);
+		dataPtr = rttiWriteElem(mMobility, dataPtr);
 		dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
 
 		SPtr<ct::Mesh>* mesh = new (dataPtr) SPtr<ct::Mesh>();
@@ -600,6 +610,7 @@ namespace bs
 		dataPtr = rttiReadElem(mIsActive, dataPtr);
 		dataPtr = rttiReadElem(mAnimationId, dataPtr);
 		dataPtr = rttiReadElem(mAnimType, dataPtr);
+		dataPtr = rttiReadElem(mMobility, dataPtr);
 		dataPtr = rttiReadElem(dirtyFlags, dataPtr);
 
 		SPtr<Mesh>* mesh = (SPtr<Mesh>*)dataPtr;
@@ -615,12 +626,7 @@ namespace bs
 			dataPtr += sizeof(SPtr<Material>);
 		}
 
-		if (dirtyFlags == (UINT32)RenderableDirtyFlag::Transform)
-		{
-			if (mIsActive)
-				gRenderer()->notifyRenderableUpdated(this);
-		}
-		else
+		if((dirtyFlags & (UINT32)RenderableDirtyFlag::Everything) != 0)
 		{
 			createAnimationBuffers();
 
@@ -651,6 +657,16 @@ namespace bs
 				gRenderer()->notifyRenderableAdded(this);
 			}
 		}
+		else if((dirtyFlags & (UINT32)RenderableDirtyFlag::Mobility) != 0)
+		{
+				gRenderer()->notifyRenderableRemoved(this);
+				gRenderer()->notifyRenderableAdded(this);
+		}
+		else if ((dirtyFlags & (UINT32)RenderableDirtyFlag::Transform) != 0)
+		{
+			if (mIsActive)
+				gRenderer()->notifyRenderableUpdated(this);
+		}
 	}
 	}
 }

+ 94 - 13
Source/BansheeCore/Source/BsResources.cpp

@@ -12,6 +12,10 @@
 #include "BsUtility.h"
 #include "BsSavedResourceData.h"
 #include "BsResourceListenerManager.h"
+#include "BsMemorySerializer.h"
+#include "BsCompression.h"
+#include "BsDataStream.h"
+#include "BsBinarySerializer.h"
 
 namespace bs
 {
@@ -355,14 +359,52 @@ namespace bs
 
 	SPtr<Resource> Resources::loadFromDiskAndDeserialize(const Path& filePath, bool loadWithSaveData)
 	{
-		FileDecoder fs(filePath);
-		fs.skip(); // Skipped over saved resource data
+		// Note: Called asynchronously over multiple resources this will cause performance issues on hard drives as they
+		// work best when they are reading one thing at a time, and it won't have benefits on an SSD either. Think about
+		// executing all file reads on a single thread, while decompression and similar operations can execute on multiple.
 
-		UnorderedMap<String, UINT64> loadParams;
+		SPtr<DataStream> stream = FileSystem::openFile(filePath, true);
+		if (stream == nullptr)
+			return nullptr;
+
+		if (stream->size() > std::numeric_limits<UINT32>::max())
+		{
+			BS_EXCEPT(InternalErrorException,
+				"File size is larger that UINT32 can hold. Ask a programmer to use a bigger data type.");
+		}
+
+		UnorderedMap<String, UINT64> params;
 		if(loadWithSaveData)
-			loadParams["keepSourceData"] = 1;
+			params["keepSourceData"] = 1;
+
+		// Read meta-data
+		SPtr<SavedResourceData> metaData;
+		{
+			if (!stream->eof())
+			{
+				UINT32 objectSize = 0;
+				stream->read(&objectSize, sizeof(objectSize));
+
+				BinarySerializer bs;
+				metaData = std::static_pointer_cast<SavedResourceData>(bs.decode(stream, objectSize, params));
+			}
+		}
+
+		// Read resource data
+		SPtr<IReflectable> loadedData;
+		{
+			if(metaData && !stream->eof())
+			{
+				UINT32 objectSize = 0;
+				stream->read(&objectSize, sizeof(objectSize));
+
+				if (metaData->getCompressionMethod() != 0)
+					stream = Compression::decompress(stream);
 
-		SPtr<IReflectable> loadedData = fs.decode(loadParams);
+				BinarySerializer bs;
+				loadedData = std::static_pointer_cast<SavedResourceData>(bs.decode(stream, objectSize, params));
+			}
+		}
 
 		if (loadedData == nullptr)
 		{
@@ -488,7 +530,7 @@ namespace bs
 		resource.setHandleData(nullptr, uuid);
 	}
 
-	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite)
+	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite, bool compress)
 	{
 		if (resource == nullptr)
 			return;
@@ -534,21 +576,60 @@ namespace bs
 		for (UINT32 i = 0; i < (UINT32)dependencyList.size(); i++)
 			dependencyUUIDs[i] = dependencyList[i].resource.getUUID();
 
-		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, resource->allowAsyncLoading());
+		UINT32 compressionMethod = (compress && resource->isCompressible()) ? 1 : 0;
+		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, 
+			resource->allowAsyncLoading(), compressionMethod);
+
+		Path parentDir = filePath.getDirectory();
+		if (!FileSystem::exists(parentDir))
+			FileSystem::createDir(parentDir);
+		
+		std::ofstream stream;
+		stream.open(filePath.toPlatformString().c_str(), std::ios::out | std::ios::binary);
+		if (stream.fail())
+			LOGWRN("Failed to save file: \"" + filePath.toString() + "\". Error: " + strerror(errno) + ".");
+	
+		// Write meta-data
+		{
+			MemorySerializer ms;
+			UINT32 numBytes = 0;
+			UINT8* bytes = ms.encode(resourceData.get(), numBytes);
+			
+			stream.write((char*)&numBytes, sizeof(numBytes));
+			stream.write((char*)bytes, numBytes);
+			
+			bs_free(bytes);
+		}
+
+		// Write object data
+		{
+			MemorySerializer ms;
+			UINT32 numBytes = 0;
+			UINT8* bytes = ms.encode(resource.get(), numBytes);
+
+			SPtr<MemoryDataStream> objStream = bs_shared_ptr_new<MemoryDataStream>(bytes, numBytes);
+			if (compressionMethod != 0)
+			{
+				SPtr<DataStream> srcStream = std::static_pointer_cast<DataStream>(objStream);
+				objStream = Compression::compress(srcStream);
+			}
+
+			stream.write((char*)&numBytes, sizeof(numBytes));
+			stream.write((char*)objStream->getPtr(), objStream->size());
+		}
 
-		FileEncoder fs(filePath);
-		fs.encode(resourceData.get());
-		fs.encode(resource.get());
+		stream.close();
+		stream.clear();
 	}
 
-	void Resources::save(const HResource& resource)
+	void Resources::save(const HResource& resource, bool compress)
 	{
 		if (resource == nullptr)
 			return;
 
 		Path path;
 		if (getFilePathFromUUID(resource.getUUID(), path))
-			save(resource, path, true);
+			save(resource, path, true, compress);
 	}
 
 	void Resources::update(HResource& handle, const SPtr<Resource>& resource)
@@ -784,4 +865,4 @@ namespace bs
 	{
 		return Resources::instance();
 	}
-}
+}

+ 4 - 6
Source/BansheeCore/Source/BsSavedResourceData.cpp

@@ -6,14 +6,12 @@
 namespace bs
 {
 	SavedResourceData::SavedResourceData()
-		:mAllowAsync(true)
+		:mAllowAsync(true), mCompressionMethod(0)
 	{ }
 
-	SavedResourceData::SavedResourceData(const Vector<String>& dependencies, bool allowAsync)
-		:mDependencies(dependencies), mAllowAsync(allowAsync)
-	{
-
-	}
+	SavedResourceData::SavedResourceData(const Vector<String>& dependencies, bool allowAsync, UINT32 compressionMethod)
+		:mDependencies(dependencies), mAllowAsync(allowAsync), mCompressionMethod(compressionMethod)
+	{ }
 
 	RTTITypeBase* SavedResourceData::getRTTIStatic()
 	{

+ 6 - 0
Source/BansheeCore/Source/BsSceneManager.cpp

@@ -166,6 +166,9 @@ namespace bs
 			SPtr<Renderable> renderable = renderablePair.second.renderable;
 			HSceneObject so = renderablePair.second.sceneObject;
 
+			if (so->getMobility() != renderable->getMobility())
+				renderable->setMobility(so->getMobility());
+
 			renderable->_updateTransform(so);
 
 			if (so->getActive() != renderable->getIsActive())
@@ -197,6 +200,9 @@ namespace bs
 			SPtr<Light> handler = lightPair.second.light;
 			HSceneObject so = lightPair.second.sceneObject;
 
+			if (so->getMobility() != handler->getMobility())
+				handler->setMobility(so->getMobility());
+
 			UINT32 curHash = so->getTransformHash();
 			if (curHash != handler->_getLastModifiedHash())
 			{

+ 66 - 16
Source/BansheeCore/Source/BsSceneObject.cpp

@@ -19,6 +19,7 @@ namespace bs
 		, mScale(Vector3::ONE), mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY)
 		, mWorldScale(Vector3::ONE), mCachedLocalTfrm(Matrix4::IDENTITY), mCachedWorldTfrm(Matrix4::IDENTITY)
 		, mDirtyFlags(0xFFFFFFFF), mDirtyHash(0), mActiveSelf(true), mActiveHierarchy(true)
+		, mMobility(ObjectMobility::Movable)
 	{
 		setName(name);
 	}
@@ -235,24 +236,36 @@ namespace bs
 
 	void SceneObject::setPosition(const Vector3& position)
 	{
-		mPosition = position;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mPosition = position;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setRotation(const Quaternion& rotation)
 	{
-		mRotation = rotation;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mRotation = rotation;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setScale(const Vector3& scale)
 	{
-		mScale = scale;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mScale = scale;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setWorldPosition(const Vector3& position)
 	{
+		if(mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Vector3 invScale = mParent->getWorldScale();
@@ -272,6 +285,9 @@ namespace bs
 
 	void SceneObject::setWorldRotation(const Quaternion& rotation)
 	{
+		if (mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Quaternion invRotation = mParent->getWorldRotation().inverse();
@@ -286,6 +302,9 @@ namespace bs
 
 	void SceneObject::setWorldScale(const Vector3& scale)
 	{
+		if (mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Matrix3 rotScale;
@@ -433,26 +452,43 @@ namespace bs
 
 	void SceneObject::notifyTransformChanged(TransformChangedFlags flags) const
 	{
-		mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
-		mDirtyHash++;
+		// If object is immovable, don't send transform changed events nor mark the transform dirty
+		TransformChangedFlags componentFlags = flags;
+		if (mMobility != ObjectMobility::Movable)
+			componentFlags = (TransformChangedFlags)(componentFlags & ~TCF_Transform);
+		else
+		{
+			mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
+			mDirtyHash++;
+		}
 
-		for(auto& entry : mComponents)
+		// Only send component flags if we haven't removed them all
+		if (componentFlags != 0)
 		{
-			if (entry->supportsNotify(flags))
+			for (auto& entry : mComponents)
 			{
-				bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun);
-				if(alwaysRun || gSceneManager().isRunning())
-					entry->onTransformChanged(flags);
+				if (entry->supportsNotify(flags))
+				{
+					bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun);
+					if (alwaysRun || gSceneManager().isRunning())
+						entry->onTransformChanged(componentFlags);
+				}
 			}
 		}
 
-		for (auto& entry : mChildren)
-			entry->notifyTransformChanged(flags);
+		// Mobility flag is only relevant for this scene object
+		flags = (TransformChangedFlags)(flags & ~TCF_Mobility);
+		if (flags != 0)
+		{
+			for (auto& entry : mChildren)
+				entry->notifyTransformChanged(flags);
+		}
 	}
 
 	void SceneObject::updateWorldTfrm() const
 	{
-		if(mParent != nullptr)
+		// Don't allow movement from parent when not movable
+		if (mParent != nullptr && mMobility == ObjectMobility::Movable)
 		{
 			// Update orientation
 			const Quaternion& parentOrientation = mParent->getWorldRotation();
@@ -683,6 +719,20 @@ namespace bs
 			return mActiveHierarchy;
 	}
 
+	void SceneObject::setMobility(ObjectMobility mobility)
+	{
+		if(mMobility != mobility)
+		{
+			mMobility = mobility;
+
+			// If mobility changed to movable, update both the mobility flag and transform, otherwise just mobility
+			if (mMobility == ObjectMobility::Movable)
+				notifyTransformChanged((TransformChangedFlags)(TCF_Transform | TCF_Mobility));
+			else
+				notifyTransformChanged(TCF_Mobility);
+		}
+	}
+
 	HSceneObject SceneObject::clone(bool instantiate)
 	{
 		bool isInstantiated = !hasFlag(SOF_DontInstantiate);

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

@@ -4,9 +4,7 @@
 #include "BsFontImportOptions.h"
 #include "BsPixelData.h"
 #include "BsTexture.h"
-#include "BsResources.h"
-#include "BsDebug.h"
-#include "BsTexAtlasGenerator.h"
+#include "BsTextureAtlasLayout.h"
 #include "BsCoreApplication.h"
 #include "BsCoreThread.h"
 
@@ -124,7 +122,7 @@ namespace bs
 			SPtr<FontBitmap> fontData = bs_shared_ptr_new<FontBitmap>();
 
 			// Get all char sizes so we can generate texture layout
-			Vector<TexAtlasElementDesc> atlasElements;
+			Vector<TextureAtlasUtility::Element> atlasElements;
 			Map<UINT32, UINT32> seqIdxToCharIdx;
 			for(auto iter = charIndexRanges.begin(); iter != charIndexRanges.end(); ++iter)
 			{
@@ -142,7 +140,7 @@ namespace bs
 
 					FT_GlyphSlot slot = face->glyph;
 
-					TexAtlasElementDesc atlasElement;
+					TextureAtlasUtility::Element atlasElement;
 					atlasElement.input.width = slot->bitmap.width;
 					atlasElement.input.height = slot->bitmap.rows;
 
@@ -165,7 +163,7 @@ namespace bs
 
 				FT_GlyphSlot slot = face->glyph;
 
-				TexAtlasElementDesc atlasElement;
+				TextureAtlasUtility::Element atlasElement;
 				atlasElement.input.width = slot->bitmap.width;
 				atlasElement.input.height = slot->bitmap.rows;
 
@@ -173,8 +171,8 @@ namespace bs
 			}
 
 			// Create an optimal layout for character bitmaps
-			TexAtlasGenerator texAtlasGen(false, MAXIMUM_TEXTURE_SIZE, MAXIMUM_TEXTURE_SIZE);
-			Vector<TexAtlasPageDesc> pages = texAtlasGen.createAtlasLayout(atlasElements);
+			Vector<TextureAtlasUtility::Page> pages = TextureAtlasUtility::createAtlasLayout(atlasElements, 64, 64,
+				MAXIMUM_TEXTURE_SIZE, MAXIMUM_TEXTURE_SIZE, true);
 
 			INT32 baselineOffset = 0;
 			UINT32 lineHeight = 0;
@@ -192,13 +190,15 @@ namespace bs
 				UINT8* pixelBuffer = pixelData->getData();
 				memset(pixelBuffer, 0, bufferSize);
 
-				for(size_t elementIdx = 0; elementIdx < atlasElements.size(); elementIdx++)
+				for(size_t i = 0; i < atlasElements.size(); i++)
 				{
 					// Copy character bitmap
-					if(atlasElements[elementIdx].output.page != pageIdx)
+					if(atlasElements[i].output.page != pageIdx)
 						continue;
 
-					TexAtlasElementDesc curElement = atlasElements[elementIdx];
+					TextureAtlasUtility::Element curElement = atlasElements[i];
+					UINT32 elementIdx = curElement.output.idx;
+					
 					bool isMissingGlypth = elementIdx == (atlasElements.size() - 1); // It's always the last element
 
 					UINT32 charIdx = 0;
@@ -343,8 +343,8 @@ namespace bs
 				}
 
 				newTex->setName(L"FontPage" + toWString((UINT32)fontData->texturePages.size()));
-				fontData->texturePages.push_back(newTex);
 
+				fontData->texturePages.push_back(newTex);
 				pageIdx++;
 			}
 

+ 8 - 1
Source/BansheeUtility/CMakeLists.txt

@@ -1,10 +1,14 @@
 # Source files and their filters
 include(CMakeSources.cmake)
 
+# Packages
+find_package(Snappy REQUIRED)
+
 # Includes
 set(BansheeUtility_INC 
 	"Include" 
-	"Include/ThirdParty")
+	"Include/ThirdParty"
+	${Snappy_INCLUDE_DIRS})
 
 if(WIN32)
 	set(BansheeUtility_INC ${BansheeUtility_INC} "Include/Win32")
@@ -24,6 +28,9 @@ target_link_libraries(BansheeUtilityTest BansheeUtility)
 target_compile_definitions(BansheeUtility PRIVATE -DBS_UTILITY_EXPORTS)
 
 # Libraries
+## External lib: Snappy
+target_link_libraries(BansheeUtility ${Snappy_LIBRARIES})	
+
 ## OS libs
 if(WIN32)
 target_link_libraries(BansheeUtility 

+ 4 - 2
Source/BansheeUtility/CMakeSources.cmake

@@ -34,7 +34,7 @@ set(BS_BANSHEEUTILITY_SRC_UNIX
 
 set(BS_BANSHEEUTILITY_INC_IMAGE
 	"Include/BsColor.h"
-	"Include/BsTexAtlasGenerator.h"
+	"Include/BsTextureAtlasLayout.h"
 )
 
 set(BS_BANSHEEUTILITY_INC_STRING
@@ -45,7 +45,7 @@ set(BS_BANSHEEUTILITY_INC_STRING
 
 set(BS_BANSHEEUTILITY_SRC_IMAGE
 	"Source/BsColor.cpp"
-	"Source/BsTexAtlasGenerator.cpp"
+	"Source/BsTextureAtlasLayout.cpp"
 )
 
 set(BS_BANSHEEUTILITY_SRC_GENERAL
@@ -55,6 +55,7 @@ set(BS_BANSHEEUTILITY_SRC_GENERAL
 	"Source/BsTimer.cpp"
 	"Source/BsTime.cpp"
 	"Source/BsUtil.cpp"
+	"Source/BsCompression.cpp"
 )
 
 set(BS_BANSHEEUTILITY_INC_DEBUG
@@ -96,6 +97,7 @@ set(BS_BANSHEEUTILITY_INC_GENERAL
 	"Include/BsTimer.h"
 	"Include/BsUtil.h"
 	"Include/BsFlags.h"
+	"Include/BsCompression.h"
 )
 
 set(BS_BANSHEEUTILITY_SRC_ALLOCATORS

+ 190 - 190
Source/BansheeUtility/Include/BsBitwise.h

@@ -58,33 +58,33 @@ namespace bs
 		} field;
 	};
 
-    /** Class for manipulating bit patterns. */
-    class Bitwise 
+	/** Class for manipulating bit patterns. */
+	class Bitwise
 	{
-    public:
+	public:
 		/** Returns the most significant bit set in a value. */
 		static UINT32 mostSignificantBitSet(unsigned int value)
-        {
+		{
 			UINT32 result = 0;
-            while (value != 0) {
-                ++result;
-                value >>= 1;
-            }
-            return result-1;
-        }
+			while (value != 0) {
+				++result;
+				value >>= 1;
+			}
+			return result - 1;
+		}
 
 		/** Returns the power-of-two number greater or equal to the provided value. */
-        static UINT32 nextPow2(UINT32 n)
-        {
-            --n;            
-            n |= n >> 16;
-            n |= n >> 8;
-            n |= n >> 4;
-            n |= n >> 2;
-            n |= n >> 1;
-            ++n;
-            return n;
-        }
+		static UINT32 nextPow2(UINT32 n)
+		{
+			--n;
+			n |= n >> 16;
+			n |= n >> 8;
+			n |= n >> 4;
+			n |= n >> 2;
+			n |= n >> 1;
+			++n;
+			return n;
+		}
 
 		/** Returns the power-of-two number closest to the provided value. */
 		static UINT32 closestPow2(UINT32 n)
@@ -99,15 +99,15 @@ namespace bs
 		}
 
 		/** Determines whether the number is power-of-two or not. */
-        template<typename T>
-        static bool isPow2(T n)
-        {
-            return (n & (n-1)) == 0;
-        }
+		template<typename T>
+		static bool isPow2(T n)
+		{
+			return (n & (n - 1)) == 0;
+		}
 
 		/** Returns the number of bits a pattern must be shifted right by to remove right-hand zeros. */
 		template<typename T>
-        static unsigned int getBitShift(T mask)
+		static unsigned int getBitShift(T mask)
 		{
 			if (mask == 0)
 				return 0;
@@ -122,7 +122,7 @@ namespace bs
 
 		/** Takes a value with a given src bit mask, and produces another value with a desired bit mask. */
 		template<typename SrcT, typename DestT>
-        static DestT convertBitPattern(SrcT srcValue, SrcT srcBitMask, DestT destBitMask)
+		static DestT convertBitPattern(SrcT srcValue, SrcT srcBitMask, DestT destBitMask)
 		{
 			// Mask off irrelevant source value bits (if any)
 			srcValue = srcValue & srcBitMask;
@@ -147,191 +147,191 @@ namespace bs
 		 * Convert N bit colour channel value to P bits. It fills P bits with the bit pattern repeated. 
 		 * (this is /((1<<n)-1) in fixed point).
 		 */
-        static unsigned int fixedToFixed(UINT32 value, unsigned int n, unsigned int p) 
-        {
-            if(n > p) 
-            {
-                // Less bits required than available; this is easy
-                value >>= n-p;
-            } 
-            else if(n < p)
-            {
-                // More bits required than are there, do the fill
-                // Use old fashioned division, probably better than a loop
-                if(value == 0)
-                        value = 0;
-                else if(value == (static_cast<unsigned int>(1)<<n)-1)
-                        value = (1<<p)-1;
-                else    value = value*(1<<p)/((1<<n)-1);
-            }
-            return value;    
-        }
+		static unsigned int fixedToFixed(UINT32 value, unsigned int n, unsigned int p)
+		{
+			if (n > p)
+			{
+				// Less bits required than available; this is easy
+				value >>= n - p;
+			}
+			else if (n < p)
+			{
+				// More bits required than are there, do the fill
+				// Use old fashioned division, probably better than a loop
+				if (value == 0)
+					value = 0;
+				else if (value == (static_cast<unsigned int>(1) << n) - 1)
+					value = (1 << p) - 1;
+				else    value = value*(1 << p) / ((1 << n) - 1);
+			}
+			return value;
+		}
 
 		/** 
 		 * 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.
 		 */
-        static unsigned int floatToFixed(const float value, const 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));     
-        }
+		static unsigned int floatToFixed(const float value, const 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)
-        {
-            return (float)value/(float)((1<<bits)-1);
-        }
+		static float fixedToFloat(unsigned value, unsigned int bits)
+		{
+			return (float)value / (float)((1 << bits) - 1);
+		}
 
 		/** Write a n*8 bits integer value to memory in native endian. */
-        static void intWrite(void *dest, const int n, const unsigned int value)
-        {
-            switch(n) {
-                case 1:
-                    ((UINT8*)dest)[0] = (UINT8)value;
-                    break;
-                case 2:
-                    ((UINT16*)dest)[0] = (UINT16)value;
-                    break;
-                case 3:
+		static void intWrite(void *dest, const int n, const unsigned int value)
+		{
+			switch(n) {
+				case 1:
+					((UINT8*)dest)[0] = (UINT8)value;
+					break;
+				case 2:
+					((UINT16*)dest)[0] = (UINT16)value;
+					break;
+				case 3:
 #if BS_ENDIAN == BS_ENDIAN_BIG      
-                    ((UINT8*)dest)[0] = (UINT8)((value >> 16) & 0xFF);
-                    ((UINT8*)dest)[1] = (UINT8)((value >> 8) & 0xFF);
-                    ((UINT8*)dest)[2] = (UINT8)(value & 0xFF);
+					((UINT8*)dest)[0] = (UINT8)((value >> 16) & 0xFF);
+					((UINT8*)dest)[1] = (UINT8)((value >> 8) & 0xFF);
+					((UINT8*)dest)[2] = (UINT8)(value & 0xFF);
 #else
-                    ((UINT8*)dest)[2] = (UINT8)((value >> 16) & 0xFF);
-                    ((UINT8*)dest)[1] = (UINT8)((value >> 8) & 0xFF);
-                    ((UINT8*)dest)[0] = (UINT8)(value & 0xFF);
+					((UINT8*)dest)[2] = (UINT8)((value >> 16) & 0xFF);
+					((UINT8*)dest)[1] = (UINT8)((value >> 8) & 0xFF);
+					((UINT8*)dest)[0] = (UINT8)(value & 0xFF);
 #endif
-                    break;
-                case 4:
-                    ((UINT32*)dest)[0] = (UINT32)value;                
-                    break;                
-            }        
-        }
+					break;
+				case 4:
+					((UINT32*)dest)[0] = (UINT32)value;                
+					break;                
+			}        
+		}
 
 		/** Read a n*8 bits integer value to memory in native endian. */
-        static unsigned int intRead(const void *src, int n) {
-            switch(n) {
-                case 1:
-                    return ((UINT8*)src)[0];
-                case 2:
-                    return ((UINT16*)src)[0];
-                case 3:
+		static unsigned int intRead(const void *src, int n) {
+			switch(n) {
+				case 1:
+					return ((UINT8*)src)[0];
+				case 2:
+					return ((UINT16*)src)[0];
+				case 3:
 #if BS_ENDIAN == BS_ENDIAN_BIG      
-                    return ((UINT32)((UINT8*)src)[0]<<16)|
-                            ((UINT32)((UINT8*)src)[1]<<8)|
-                            ((UINT32)((UINT8*)src)[2]);
+					return ((UINT32)((UINT8*)src)[0]<<16)|
+							((UINT32)((UINT8*)src)[1]<<8)|
+							((UINT32)((UINT8*)src)[2]);
 #else
-                    return ((UINT32)((UINT8*)src)[0])|
-                            ((UINT32)((UINT8*)src)[1]<<8)|
-                            ((UINT32)((UINT8*)src)[2]<<16);
+					return ((UINT32)((UINT8*)src)[0])|
+							((UINT32)((UINT8*)src)[1]<<8)|
+							((UINT32)((UINT8*)src)[2]<<16);
 #endif
-                case 4:
-                    return ((UINT32*)src)[0];
-            } 
-            return 0; // ?
-        }
+				case 4:
+					return ((UINT32*)src)[0];
+			} 
+			return 0; // ?
+		}
 
 		/** Convert a float32 to a float16 (NV_half_float). */
-        static UINT16 floatToHalf(float i)
-        {
-            union { float f; UINT32 i; } v;
-            v.f = i;
-            return floatToHalfI(v.i);
-        }
+		static UINT16 floatToHalf(float i)
+		{
+			union { float f; UINT32 i; } v;
+			v.f = i;
+			return floatToHalfI(v.i);
+		}
 
 		/** Converts float in UINT32 format to a a half in UINT16 format. */
-        static UINT16 floatToHalfI(UINT32 i)
-        {
-            int s =  (i >> 16) & 0x00008000;
-            int e = ((i >> 23) & 0x000000ff) - (127 - 15);
-            int m =   i        & 0x007fffff;
-        
-            if (e <= 0)
-            {
-                if (e < -10)
-                {
-                    return 0;
-                }
-                m = (m | 0x00800000) >> (1 - e);
-        
-                return static_cast<UINT16>(s | (m >> 13));
-            }
-            else if (e == 0xff - (127 - 15))
-            {
-                if (m == 0) // Inf
-                {
-                    return static_cast<UINT16>(s | 0x7c00);
-                } 
-                else    // NAN
-                {
-                    m >>= 13;
-                    return static_cast<UINT16>(s | 0x7c00 | m | (m == 0));
-                }
-            }
-            else
-            {
-                if (e > 30) // Overflow
-                {
-                    return static_cast<UINT16>(s | 0x7c00);
-                }
-        
-                return static_cast<UINT16>(s | (e << 10) | (m >> 13));
-            }
-        }
-        
+		static UINT16 floatToHalfI(UINT32 i)
+		{
+			int s =  (i >> 16) & 0x00008000;
+			int e = ((i >> 23) & 0x000000ff) - (127 - 15);
+			int m =   i        & 0x007fffff;
+		
+			if (e <= 0)
+			{
+				if (e < -10)
+				{
+					return 0;
+				}
+				m = (m | 0x00800000) >> (1 - e);
+		
+				return static_cast<UINT16>(s | (m >> 13));
+			}
+			else if (e == 0xff - (127 - 15))
+			{
+				if (m == 0) // Inf
+				{
+					return static_cast<UINT16>(s | 0x7c00);
+				} 
+				else    // NAN
+				{
+					m >>= 13;
+					return static_cast<UINT16>(s | 0x7c00 | m | (m == 0));
+				}
+			}
+			else
+			{
+				if (e > 30) // Overflow
+				{
+					return static_cast<UINT16>(s | 0x7c00);
+				}
+		
+				return static_cast<UINT16>(s | (e << 10) | (m >> 13));
+			}
+		}
+		
 		/** Convert a float16 (NV_half_float) to a float32. */
-        static float halfToFloat(UINT16 y)
-        {
-            union { float f; UINT32 i; } v;
-            v.i = halfToFloatI(y);
-            return v.f;
-        }
+		static float halfToFloat(UINT16 y)
+		{
+			union { float f; UINT32 i; } v;
+			v.i = halfToFloatI(y);
+			return v.f;
+		}
 
 		/** Converts a half in UINT16 format to a float in UINT32 format. */
-        static UINT32 halfToFloatI(UINT16 y)
-        {
-            int s = (y >> 15) & 0x00000001;
-            int e = (y >> 10) & 0x0000001f;
-            int m =  y        & 0x000003ff;
-        
-            if (e == 0)
-            {
-                if (m == 0) // Plus or minus zero
-                {
-                    return s << 31;
-                }
-                else // Denormalized number -- renormalize it
-                {
-                    while (!(m & 0x00000400))
-                    {
-                        m <<= 1;
-                        e -=  1;
-                    }
-        
-                    e += 1;
-                    m &= ~0x00000400;
-                }
-            }
-            else if (e == 31)
-            {
-                if (m == 0) // Inf
-                {
-                    return (s << 31) | 0x7f800000;
-                }
-                else // NaN
-                {
-                    return (s << 31) | 0x7f800000 | (m << 13);
-                }
-            }
-        
-            e = e + (127 - 15);
-            m = m << 13;
-        
-            return (s << 31) | (e << 23) | m;
-        }
+		static UINT32 halfToFloatI(UINT16 y)
+		{
+			int s = (y >> 15) & 0x00000001;
+			int e = (y >> 10) & 0x0000001f;
+			int m =  y        & 0x000003ff;
+		
+			if (e == 0)
+			{
+				if (m == 0) // Plus or minus zero
+				{
+					return s << 31;
+				}
+				else // Denormalized number -- renormalize it
+				{
+					while (!(m & 0x00000400))
+					{
+						m <<= 1;
+						e -=  1;
+					}
+		
+					e += 1;
+					m &= ~0x00000400;
+				}
+			}
+			else if (e == 31)
+			{
+				if (m == 0) // Inf
+				{
+					return (s << 31) | 0x7f800000;
+				}
+				else // NaN
+				{
+					return (s << 31) | 0x7f800000 | (m << 13);
+				}
+			}
+		
+			e = e + (127 - 15);
+			m = m << 13;
+		
+			return (s << 31) | (e << 23) | m;
+		}
 
 		/** Converts a 32-bit float to a 10-bit float according to OpenGL packed_float extension. */
 		static UINT32 floatToFloat10(float v)
@@ -492,7 +492,7 @@ namespace bs
 
 			return *(float*)&output;
 		}
-    };
+	};
 
 	/** @} */
 }

+ 25 - 0
Source/BansheeUtility/Include/BsCompression.h

@@ -0,0 +1,25 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+
+namespace bs
+{
+	/** @addtogroup Utility-Core
+	 *  @{
+	 */
+
+	/** Performs generic compression and decompression on raw data. */
+	class BS_UTILITY_EXPORT Compression
+	{
+	public:
+		/** Compresses the data from the provided data stream and outputs the new stream with compressed data. */
+		static SPtr<MemoryDataStream> compress(SPtr<DataStream>& input);
+
+		/** Decompresses the data from the provided data stream and outputs the new stream with decompressed data. */
+		static SPtr<MemoryDataStream> decompress(SPtr<DataStream>& input);
+	};
+
+	/** @} */
+}

+ 12 - 1
Source/BansheeUtility/Include/BsConvexVolume.h

@@ -11,6 +11,17 @@ namespace bs
 	 *  @{
 	 */
 
+	/**	Clip planes that form the camera frustum (visible area). */
+	enum FrustumPlane
+	{
+		FRUSTUM_PLANE_NEAR = 0,
+		FRUSTUM_PLANE_FAR = 1,
+		FRUSTUM_PLANE_LEFT = 2,
+		FRUSTUM_PLANE_RIGHT = 3,
+		FRUSTUM_PLANE_TOP = 4,
+		FRUSTUM_PLANE_BOTTOM = 5
+	};
+
 	/** Represents a convex volume defined by planes representing the volume border. */
 	class BS_UTILITY_EXPORT ConvexVolume
 	{
@@ -19,7 +30,7 @@ namespace bs
 		ConvexVolume(const Vector<Plane>& planes);
 
 		/** Creates frustum planes from the provided projection matrix. */
-		ConvexVolume(const Matrix4& projectionMatrix);
+		ConvexVolume(const Matrix4& projectionMatrix, bool useNearPlane = true);
 
 		/**
 		 * Checks does the volume intersects the provided axis aligned box.

+ 8 - 1
Source/BansheeUtility/Include/BsDataStream.h

@@ -154,6 +154,13 @@ namespace bs
 	class BS_UTILITY_EXPORT MemoryDataStream : public DataStream
 	{		
 	public:
+		/**
+		 * Allocates a new chunk of memory and wraps it in a stream.
+		 *
+		 * @param[in]	size		Size of the memory chunk in bytes.
+		 */
+		MemoryDataStream(size_t size);
+
 		/**
 		 * Wrap an existing memory chunk in a stream.
 		 *
@@ -162,7 +169,7 @@ namespace bs
 		 * @param[in]	freeOnClose	Should the memory buffer be freed when the data stream goes out of scope.
 		 */
 		MemoryDataStream(void* memory, size_t size, bool freeOnClose = true);
-		
+
 		/**
 		 * Create a stream which pre-buffers the contents of another stream. Data from the other buffer will be entirely 
 		 * read and stored in an internal buffer.

+ 3 - 2
Source/BansheeUtility/Include/BsDebug.h

@@ -44,8 +44,9 @@ namespace bs
 		/** Retrieves the Log used by the Debug instance. */
 		Log& getLog() { return mLog; }
 
-		/** Converts raw pixels into a BMP image. See BitmapWriter for more information. */
-		void writeAsBMP(UINT8* rawPixels, UINT32 bytesPerPixel, UINT32 width, UINT32 height, const Path& filePath, bool overwrite = true) const;
+		/** Converts raw pixels into a BMP image and saves it as a file */
+		void writeAsBMP(UINT8* rawPixels, UINT32 bytesPerPixel, UINT32 width, UINT32 height, const Path& filePath, 
+			bool overwrite = true) const;
 
 		/**
 		 * Saves a log about the current state of the application to the specified location.

+ 2 - 1
Source/BansheeUtility/Include/BsFileSerializer.h

@@ -14,9 +14,10 @@ namespace bs
 	// a generic Serializer interface so it may write both binary, plain-text or some other form of data.
 
 	/** Encodes the provided object to the specified file using the RTTI system. */
+	
 	class BS_UTILITY_EXPORT FileEncoder
 	{
-	public:
+		public:
 		FileEncoder(const Path& fileLocation);
 		~FileEncoder();
 

+ 17 - 13
Source/BansheeUtility/Include/BsMatrix4.h

@@ -97,22 +97,23 @@ namespace bs
 		}
 
 		/** Returns a row of the matrix. */
-		float* operator[] (UINT32 row)
-        {
-            assert(row < 4);
+		Vector4& operator[] (UINT32 row)
+		{
+			assert(row < 4);
 
-            return m[row];
-        }
+			return *(Vector4*)m[row];
+		}
 
-        const float *operator[] (UINT32 row) const
-        {
-            assert(row < 4);
+		/** Returns a row of the matrix. */
+		const Vector4& operator[] (UINT32 row) const
+		{
+			assert(row < 4);
 
-            return m[row];
-        }
+			return *(Vector4*)m[row];
+		}
 
-        Matrix4 operator* (const Matrix4 &rhs) const
-        {
+		Matrix4 operator* (const Matrix4 &rhs) const
+		{
 			Matrix4 r;
 
 			r.m[0][0] = m[0][0] * rhs.m[0][0] + m[0][1] * rhs.m[1][0] + m[0][2] * rhs.m[2][0] + m[0][3] * rhs.m[3][0];
@@ -456,7 +457,10 @@ namespace bs
 		/** Creates a 4x4 perspective projection matrix. */
 		static Matrix4 projectionPerspective(const Degree& horzFOV, float aspect, float near, float far);
 
-		/** Creates a view matrix and applies optional reflection. */
+		/** Creates a 4x4 ortographic projection matrix. */
+		static Matrix4 projectionOrthographic(float left, float right, float top, float bottom, float near, float far);
+
+		/** Creates a view matrix. */
 		static Matrix4 view(const Vector3& position, const Quaternion& orientation);
 
         /**

+ 0 - 111
Source/BansheeUtility/Include/BsTexAtlasGenerator.h

@@ -1,111 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisitesUtil.h"
-#include "BsVector2.h"
-
-namespace bs
-{
-	/** @addtogroup Image
-	 *  @{
-	 */
-
-	/**
-	 * Represents a single element used as in input to TexAtlasGenerator. Usually represents a single texture.
-	 * 			
-	 * @note	input is required to be filled in before passing it to TexAtlasGenerator.
-	 * @note	output will be filled in by TexAtlasGenerator after a call to TexAtlasGenerator::createAtlasLayout().
-	 */
-	struct TexAtlasElementDesc
-	{
-		struct
-		{
-			UINT32 width, height;
-		} input;
-		
-		struct
-		{
-			UINT32 x, y;
-			INT32 page;
-		} output;
-	};
-
-	/** A single page of the texture atlas. */
-	struct TexAtlasPageDesc
-	{
-		UINT32 width, height;
-	};
-
-	class TexAtlasNode;
-
-	/** Organizes a set of textures into a single larger texture (an atlas) by minimizing empty space. */
-	class BS_UTILITY_EXPORT TexAtlasGenerator
-	{
-	public:
-		/**
-		 * Constructs a new texture atlas generator with the provided parameters.
-		 *
-		 * @param[in]	square			(optional) Should the returned texture always be square. (width == height)
-		 * 								This option is only used if @p fixedSize parameter is set to false.
-		 * @param[in]	maxTexWidth 	(optional) Maximum width of the texture. 
-		 * @param[in]	maxTexHeight	(optional) Maximum height of the texture. 
-		 * @param[in]	fixedSize   	(optional) If this field is false, algorithm will try to reduce the size of the texture
-		 * 								if possible. If it is true, the algorithm will always produce textures of the specified
-		 * 								@p maxTexWidth, @p maxTexHeight size.
-		 */
-		TexAtlasGenerator(bool square = false, UINT32 maxTexWidth = 2048, UINT32 maxTexHeight = 2048, bool fixedSize = false);
-
-		/**
-		 * Creates an optimal texture layout by packing texture elements in order to end up with as little empty space 
-		 * as possible.
-		 *
-		 * @param[in]	elements	Elements to process. They need to have their input structures filled in,
-		 * 							and this method will fill output when it returns.
-		 * @return					One or more descriptors that determine the size of the final atlas textures. 
-		 *							Texture elements will reference these pages with their output.page parameter.
-		 *
-		 * @note	
-		 * Algorithm will split elements over multiple textures if they don't fit in a single texture (Determined by 
-		 * maximum texture size).
-		 */
-		Vector<TexAtlasPageDesc> createAtlasLayout(Vector<TexAtlasElementDesc>& elements) const;
-
-	private:
-		bool mSquare;
-		bool mFixedSize;
-		UINT32 mMaxTexWidth;
-		UINT32 mMaxTexHeight;
-
-		/**
-		 * Organize all of the provide elements and place them into minimum number of pages with the specified width and height.
-		 * 			
-		 * Caller must ensure @p elements array has the page indexes reset to -1 before calling, otherwise it will be assumed
-		 * those elements already have assigned pages.
-		 * 			
-		 * Using @p startPage parameter you may add an offset to the generated page indexes.
-		 *
-		 * @return	Number of pages generated.
-		 */
-		int generatePagesForSize(Vector<TexAtlasElementDesc>& elements, UINT32 width, UINT32 height, UINT32 startPage = 0) const;
-
-		/**
-		 * Finds the largest element without a page that fits within the provided node.
-		 *
-		 * @return	Array index of the found page, or -1 if all textures have a page.
-		 */
-		int addLargestTextureWithoutPageThatFits(Vector<TexAtlasElementDesc>& elements, TexAtlasNode& node) const;
-
-		/**
-		 * Scan all of the provided elements and find the largest one that still doesn't have a page assigned.
-		 * 			
-		 * @return	Array index of the found page, or -1 if all textures have a page.
-		 */
-		int findLargestTextureWithoutPage(const Vector<TexAtlasElementDesc>& elements) const;
-
-		/** Sorts all the texture elements so that larget elements come first. */
-		void sortBySize(Vector<TexAtlasElementDesc>& elements) const;
-	};
-
-	/** @} */
-}

+ 135 - 0
Source/BansheeUtility/Include/BsTextureAtlasLayout.h

@@ -0,0 +1,135 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+#include "BsVector2.h"
+
+namespace bs
+{
+	/** @addtogroup Image
+	 *  @{
+	 */
+
+	/** Organizes a set of textures into a single larger texture (an atlas) by minimizing empty space. */
+	class BS_UTILITY_EXPORT TextureAtlasLayout
+	{
+		/** Represent a single node in the texture atlas binary tree. */
+		class TexAtlasNode
+		{
+		public:
+			TexAtlasNode();
+			TexAtlasNode(UINT32 x, UINT32 y, UINT32 width, UINT32 height);
+
+			UINT32 x, y, width, height;
+			UINT32 children[2];
+			bool nodeFull;
+		};
+
+	public:
+		TextureAtlasLayout();
+
+		/**
+		 * Constructs a new texture atlas layout with the provided parameters.
+		 *
+		 * @param[in]	width 			Initial width of the atlas texture. 
+		 * @param[in]	height			Initial height of the atlas texture.
+		 * @param[in]	maxWidth		Maximum width the atlas texture is allowed to grow to, when elements don't fit.
+		 * @param[in]	maxHeight		Maximum height the atlas texture is allowed to grow to, when elements don't fit.
+		 * @param[in]	pow2			When true the resulting atlas size will always be a power of two.
+		 */
+		TextureAtlasLayout(UINT32 width, UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2 = false);
+
+		/**
+		 * Attempts to add a new element in the layout.
+		 * 
+		 * @param[in]	width	Width of the new element, in pixels.
+		 * @param[in]	height	Height of the new element, in pixels.
+		 * @param[out]	x		Horizontal position of the new element within the atlas. Only valid if method returns true.
+		 * @param[out]	y		Vertical position of the new element within the atlas. Only valid if method returns true.
+		 * @return				True if the element was added to the atlas, false if the element doesn't fit.
+		 */
+		bool addElement(UINT32 width, UINT32 height, UINT32& x, UINT32& y);
+
+		/** Returns the width of the atlas texture, in pixels. */
+		UINT32 getWidth() const { return mWidth; }
+
+		/** Returns the height of the atlas texture, in pixels. */
+		UINT32 getHeight() const { return mHeight; }
+
+	private:
+		/* 
+		 * Attempts to add a new element to the specified layout node. 
+		 * 
+		 * @param[in]	nodeIdx			Index of the node to which to add the element.
+		 * @param[in]	width			Width of the new element, in pixels.
+		 * @param[in]	height			Height of the new element, in pixels.
+		 * @param[out]	x				Horizontal position of the new element within the atlas. Only valid if method
+		 *								returns true.
+		 * @param[out]	y				Vertical position of the new element within the atlas. Only valid if method returns
+		 *								true.
+		 * @param[in]	allowGrowth		When true, the width/height of the atlas will be allowed to grow to fit the element.
+		 * @return						True if the element was added to the atlas, false if the element doesn't fit.
+		 */
+		bool addToNode(UINT32 nodeIdx, UINT32 width, UINT32 height, UINT32& x, UINT32& y, bool allowGrowth);
+
+		UINT32 mWidth;
+		UINT32 mHeight;
+		UINT32 mMaxWidth;
+		UINT32 mMaxHeight;
+		bool mPow2;
+
+		Vector<TexAtlasNode> mNodes;
+	};
+
+	/** Utility class used for texture atlas layouts. */
+	class BS_UTILITY_EXPORT TextureAtlasUtility
+	{
+	public:
+		/**
+		 * Represents a single element used as in input to TextureAtlasUtility. Usually represents a single texture.
+		 * 			
+		 * @note	input is required to be filled in before passing it to TextureAtlasUtility.
+		 * @note	output will be filled after a call to TextureAtlasUtility::createAtlasLayout().
+		 */
+		struct Element
+		{
+			struct
+			{
+				UINT32 width, height;
+			} input;
+		
+			struct
+			{
+				UINT32 x, y;
+				UINT32 idx;
+				INT32 page;
+			} output;
+		};
+
+		/** Describes a single page of the texture atlas. */
+		struct Page
+		{
+			UINT32 width, height;
+		};
+
+		/**
+		 * Creates an optimal texture layout by packing texture elements in order to end up with as little empty space 
+		 * as possible. Algorithm will split elements over multiple textures if they don't fit in a single texture.
+		 *
+		 * @param[in]	elements	Elements to process. They need to have their input structures filled in,
+		 * 							and this method will fill output when it returns.
+		 * @param[in]	width 		Initial width of the atlas texture.
+		 * @param[in]	height		Initial height of the atlas texture.
+		 * @param[in]	maxWidth	Maximum width the atlas texture is allowed to grow to, when elements don't fit.
+		 * @param[in]	maxHeight	Maximum height the atlas texture is allowed to grow to, when elements don't fit.
+		 * @param[in]	pow2		When true the resulting atlas size will always be a power of two.
+		 * @return					One or more descriptors that determine the size of the final atlas textures. 
+		 *							Texture elements will reference these pages with their output.page parameter.
+		 */
+		static Vector<Page> createAtlasLayout(Vector<Element>& elements, UINT32 width, UINT32 height, UINT32 maxWidth, 
+			UINT32 maxHeight, bool pow2 = false);
+	};
+
+	/** @} */
+}

+ 194 - 0
Source/BansheeUtility/Source/BsCompression.cpp

@@ -0,0 +1,194 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCompression.h"
+#include "BsDataStream.h"
+
+// Third party
+#include "snappy.h"
+#include "snappy-sinksource.h"
+#include "BsDebug.h"
+
+namespace bs
+{
+	/** Source accepting a data stream. Used for Snappy compression library. */
+	class DataStreamSource : public snappy::Source
+	{
+	public:
+		DataStreamSource(const SPtr<DataStream>& stream)
+			: mStream(stream), mReadBuffer(nullptr), mReadBufferContentSize(0), mBufferOffset(0)
+		{
+			mRemaining = mStream->size() - mStream->tell();
+
+			if (mStream->isFile())
+				mReadBuffer = (char*)bs_alloc(2048);
+		}
+
+		virtual ~DataStreamSource()
+		{
+			if (mReadBuffer != nullptr)
+				bs_free(mReadBuffer);
+		}
+
+		size_t Available() const override
+		{
+			return mRemaining;
+		}
+
+		const char* Peek(size_t* len) override
+		{
+			if (!mStream->isFile())
+			{
+				SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStream);
+
+				*len = Available();
+				return (char*)memStream->getPtr() + mBufferOffset;
+			}
+			else
+			{
+				while (mBufferOffset >= mReadBufferContentSize)
+				{
+					mBufferOffset -= mReadBufferContentSize;
+					mReadBufferContentSize = mStream->read(mReadBuffer, 2048);
+
+					if (mReadBufferContentSize == 0)
+						break;
+				}
+
+				*len = mReadBufferContentSize - mBufferOffset;
+				return (char*)(mReadBuffer + mBufferOffset);
+			}
+		}
+
+		void Skip(size_t n) override
+		{
+			mBufferOffset += n;
+			mRemaining -= n;
+		}
+	private:
+		SPtr<DataStream> mStream;
+
+		// File streams only
+		char* mReadBuffer;
+		size_t mReadBufferContentSize;
+		size_t mRemaining;
+		size_t mBufferOffset;
+	};
+
+	/** Sink (destination) accepting a data stream. Used for Snappy compression library. */
+	class DataStreamSink : public snappy::Sink
+	{
+		struct BufferPiece
+		{
+			char* buffer;
+			size_t size;
+		};
+
+	public:
+		DataStreamSink() { }
+		virtual ~DataStreamSink()
+		{
+			for (auto& entry : mBufferPieces)
+				bs_free(entry.buffer);
+		}
+
+		void Append(const char* data, size_t n) override
+		{
+			if(mBufferPieces.size() == 0 || mBufferPieces.back().buffer != data)
+			{
+				BufferPiece piece;
+				piece.buffer = (char*)bs_alloc((UINT32)n);
+				piece.size = n;
+
+				memcpy(piece.buffer, data, n);
+				mBufferPieces.push_back(piece);
+			}
+			else
+			{
+				BufferPiece& piece = mBufferPieces.back();
+				assert(piece.buffer == data);
+
+				piece.size = n;
+			}
+		}
+
+		char* GetAppendBuffer(size_t len, char* scratch) override
+		{
+			BufferPiece piece;
+			piece.buffer = (char*)bs_alloc((UINT32)len);
+			piece.size = 0;
+
+			mBufferPieces.push_back(piece);
+			return piece.buffer;
+		}
+
+		char* GetAppendBufferVariable(size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size,
+			size_t* allocated_size) override
+		{
+			BufferPiece piece;
+			piece.buffer = (char*)bs_alloc((UINT32)desired_size_hint);
+			piece.size = 0;
+
+			mBufferPieces.push_back(piece);
+
+			*allocated_size = desired_size_hint;
+			return piece.buffer;
+		}
+
+		void AppendAndTakeOwnership(char* bytes, size_t n, void(*deleter)(void*, const char*, size_t),
+			void *deleter_arg) override
+		{
+			BufferPiece& piece = mBufferPieces.back();
+
+			if (piece.buffer != bytes)
+			{
+				memcpy(piece.buffer, bytes, n);
+				(*deleter)(deleter_arg, bytes, n);
+			}
+
+			piece.size = n;
+		}
+
+		SPtr<MemoryDataStream> GetOutput()
+		{
+			size_t totalSize = 0;
+			for (auto& entry : mBufferPieces)
+				totalSize += entry.size;
+
+			SPtr<MemoryDataStream> ds = bs_shared_ptr_new<MemoryDataStream>(totalSize);
+			for (auto& entry : mBufferPieces)
+				ds->write(entry.buffer, entry.size);
+
+			ds->seek(0);
+			return ds;
+		}
+
+	private:
+		Vector<BufferPiece> mBufferPieces;
+	};
+
+	SPtr<MemoryDataStream> Compression::compress(SPtr<DataStream>& input)
+	{
+		DataStreamSource src(input);
+		DataStreamSink dst;
+
+		size_t bytesWritten = snappy::Compress(&src, &dst);
+		SPtr<MemoryDataStream> output = dst.GetOutput();
+		assert(output->size() == bytesWritten);
+
+		return output;
+	}
+
+	SPtr<MemoryDataStream> Compression::decompress(SPtr<DataStream>& input)
+	{
+		DataStreamSource src(input);
+		DataStreamSink dst;
+
+		if (!snappy::Uncompress(&src, &dst))
+		{
+			LOGERR("Decompression failed, corrupt data.");
+			return nullptr;
+		}
+
+		return dst.GetOutput();
+	}
+}

+ 70 - 44
Source/BansheeUtility/Source/BsConvexVolume.cpp

@@ -8,58 +8,84 @@
 
 namespace bs
 {
-	/**	Clip planes that form the camera frustum (visible area). */
-	enum FrustumPlane
-	{
-		FRUSTUM_PLANE_NEAR = 0,
-		FRUSTUM_PLANE_FAR = 1,
-		FRUSTUM_PLANE_LEFT = 2,
-		FRUSTUM_PLANE_RIGHT = 3,
-		FRUSTUM_PLANE_TOP = 4,
-		FRUSTUM_PLANE_BOTTOM = 5
-	};
-
 	ConvexVolume::ConvexVolume(const Vector<Plane>& planes)
 		:mPlanes(planes)
 	{ }
 
-	ConvexVolume::ConvexVolume(const Matrix4& projectionMatrix)
+	ConvexVolume::ConvexVolume(const Matrix4& projectionMatrix, bool useNearPlane)
 	{
-		mPlanes.resize(6);
+		mPlanes.reserve(6);
 
 		const Matrix4& proj = projectionMatrix;
 
-		mPlanes[FRUSTUM_PLANE_LEFT].normal.x = proj[3][0] + proj[0][0];
-		mPlanes[FRUSTUM_PLANE_LEFT].normal.y = proj[3][1] + proj[0][1];
-		mPlanes[FRUSTUM_PLANE_LEFT].normal.z = proj[3][2] + proj[0][2];
-		mPlanes[FRUSTUM_PLANE_LEFT].d = proj[3][3] + proj[0][3];
-
-		mPlanes[FRUSTUM_PLANE_RIGHT].normal.x = proj[3][0] - proj[0][0];
-		mPlanes[FRUSTUM_PLANE_RIGHT].normal.y = proj[3][1] - proj[0][1];
-		mPlanes[FRUSTUM_PLANE_RIGHT].normal.z = proj[3][2] - proj[0][2];
-		mPlanes[FRUSTUM_PLANE_RIGHT].d = proj[3][3] - proj[0][3];
-
-		mPlanes[FRUSTUM_PLANE_TOP].normal.x = proj[3][0] - proj[1][0];
-		mPlanes[FRUSTUM_PLANE_TOP].normal.y = proj[3][1] - proj[1][1];
-		mPlanes[FRUSTUM_PLANE_TOP].normal.z = proj[3][2] - proj[1][2];
-		mPlanes[FRUSTUM_PLANE_TOP].d = proj[3][3] - proj[1][3];
-
-		mPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = proj[3][0] + proj[1][0];
-		mPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = proj[3][1] + proj[1][1];
-		mPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = proj[3][2] + proj[1][2];
-		mPlanes[FRUSTUM_PLANE_BOTTOM].d = proj[3][3] + proj[1][3];
-
-		mPlanes[FRUSTUM_PLANE_NEAR].normal.x = proj[3][0] + proj[2][0];
-		mPlanes[FRUSTUM_PLANE_NEAR].normal.y = proj[3][1] + proj[2][1];
-		mPlanes[FRUSTUM_PLANE_NEAR].normal.z = proj[3][2] + proj[2][2];
-		mPlanes[FRUSTUM_PLANE_NEAR].d = proj[3][3] + proj[2][3];
-
-		mPlanes[FRUSTUM_PLANE_FAR].normal.x = proj[3][0] - proj[2][0];
-		mPlanes[FRUSTUM_PLANE_FAR].normal.y = proj[3][1] - proj[2][1];
-		mPlanes[FRUSTUM_PLANE_FAR].normal.z = proj[3][2] - proj[2][2];
-		mPlanes[FRUSTUM_PLANE_FAR].d = proj[3][3] - proj[2][3];
-
-		for (UINT32 i = 0; i < 6; i++)
+		// Left
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] + proj[0][0];
+			plane.normal.y = proj[3][1] + proj[0][1];
+			plane.normal.z = proj[3][2] + proj[0][2];
+			plane.d = proj[3][3] + proj[0][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		// Right
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] - proj[0][0];
+			plane.normal.y = proj[3][1] - proj[0][1];
+			plane.normal.z = proj[3][2] - proj[0][2];
+			plane.d = proj[3][3] - proj[0][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		// Top
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] - proj[1][0];
+			plane.normal.y = proj[3][1] - proj[1][1];
+			plane.normal.z = proj[3][2] - proj[1][2];
+			plane.d = proj[3][3] - proj[1][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		// Bottom
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] + proj[1][0];
+			plane.normal.y = proj[3][1] + proj[1][1];
+			plane.normal.z = proj[3][2] + proj[1][2];
+			plane.d = proj[3][3] + proj[1][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		// Near
+		if(useNearPlane)
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] + proj[2][0];
+			plane.normal.y = proj[3][1] + proj[2][1];
+			plane.normal.z = proj[3][2] + proj[2][2];
+			plane.d = proj[3][3] + proj[2][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		// Far
+		{
+			Plane plane;
+			plane.normal.x = proj[3][0] - proj[2][0];
+			plane.normal.y = proj[3][1] - proj[2][1];
+			plane.normal.z = proj[3][2] - proj[2][2];
+			plane.d = proj[3][3] - proj[2][3];
+
+			mPlanes.push_back(plane);
+		}
+
+		for (UINT32 i = 0; i < (UINT32)mPlanes.size(); i++)
 		{
 			float length = mPlanes[i].normal.normalize();
 			mPlanes[i].d /= -length;

+ 112 - 102
Source/BansheeUtility/Source/BsDataStream.cpp

@@ -38,12 +38,12 @@ namespace bs
 		return (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF);
 	}
 
-    template <typename T> DataStream& DataStream::operator>> (T& val)
-    {
-        read(static_cast<void*>(&val), sizeof(T));
+	template <typename T> DataStream& DataStream::operator>> (T& val)
+	{
+		read(static_cast<void*>(&val), sizeof(T));
 
-        return *this;
-    }
+		return *this;
+	}
 
 	void DataStream::writeString(const String& string, StringEncoding encoding)
 	{
@@ -246,65 +246,75 @@ namespace bs
 		}
 	}
 
-    MemoryDataStream::MemoryDataStream(void* memory, size_t inSize, bool freeOnClose)
+	MemoryDataStream::MemoryDataStream(size_t size)
+		: DataStream(READ | WRITE), mData(nullptr), mFreeOnClose(true)
+	{
+		mData = mPos = (UINT8*)bs_alloc(size);
+		mSize = size;
+		mEnd = mData + mSize;
+
+		assert(mEnd >= mPos);
+	}
+
+	MemoryDataStream::MemoryDataStream(void* memory, size_t inSize, bool freeOnClose)
 		: DataStream(READ | WRITE), mData(nullptr), mFreeOnClose(freeOnClose)
-    {
-        mData = mPos = static_cast<UINT8*>(memory);
-        mSize = inSize;
-        mEnd = mData + mSize;
+	{
+		mData = mPos = static_cast<UINT8*>(memory);
+		mSize = inSize;
+		mEnd = mData + mSize;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::MemoryDataStream(DataStream& sourceStream)
-        : DataStream(READ | WRITE), mData(nullptr)
-    {
-        // Copy data from incoming stream
-        mSize = sourceStream.size();
+	MemoryDataStream::MemoryDataStream(DataStream& sourceStream)
+		: DataStream(READ | WRITE), mData(nullptr)
+	{
+		// Copy data from incoming stream
+		mSize = sourceStream.size();
 
 		mData = (UINT8*)bs_alloc((UINT32)mSize);
 		mPos = mData;
 		mEnd = mData + sourceStream.read(mData, mSize);
 		mFreeOnClose = true;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::MemoryDataStream(const SPtr<DataStream>& sourceStream)
-        :DataStream(READ | WRITE), mData(nullptr)
-    {
-        // Copy data from incoming stream
-        mSize = sourceStream->size();
+	MemoryDataStream::MemoryDataStream(const SPtr<DataStream>& sourceStream)
+		:DataStream(READ | WRITE), mData(nullptr)
+	{
+		// Copy data from incoming stream
+		mSize = sourceStream->size();
 
 		mData = (UINT8*)bs_alloc((UINT32)mSize);
 		mPos = mData;
 		mEnd = mData + sourceStream->read(mData, mSize);
 		mFreeOnClose = true;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::~MemoryDataStream()
-    {
-        close();
-    }
+	MemoryDataStream::~MemoryDataStream()
+	{
+		close();
+	}
 
-    size_t MemoryDataStream::read(void* buf, size_t count)
-    {
-        size_t cnt = count;
+	size_t MemoryDataStream::read(void* buf, size_t count)
+	{
+		size_t cnt = count;
 
-        if (mPos + cnt > mEnd)
-            cnt = mEnd - mPos;
-        if (cnt == 0)
-            return 0;
+		if (mPos + cnt > mEnd)
+			cnt = mEnd - mPos;
+		if (cnt == 0)
+			return 0;
 
-        assert (cnt <= count);
+		assert (cnt <= count);
 
-        memcpy(buf, mPos, cnt);
-        mPos += cnt;
+		memcpy(buf, mPos, cnt);
+		mPos += cnt;
 
-        return cnt;
-    }
+		return cnt;
+	}
 
 	size_t MemoryDataStream::write(const void* buf, size_t count)
 	{
@@ -325,29 +335,29 @@ namespace bs
 		return written;
 	}
 
-    void MemoryDataStream::skip(size_t count)
-    {
-        size_t newpos = (size_t)( (mPos - mData) + count );
-        assert(mData + newpos <= mEnd);        
+	void MemoryDataStream::skip(size_t count)
+	{
+		size_t newpos = (size_t)( (mPos - mData) + count );
+		assert(mData + newpos <= mEnd);        
 
-        mPos = mData + newpos;
-    }
+		mPos = mData + newpos;
+	}
 
-    void MemoryDataStream::seek(size_t pos)
-    {
-        assert(mData + pos <= mEnd);
-        mPos = mData + pos;
-    }
+	void MemoryDataStream::seek(size_t pos)
+	{
+		assert(mData + pos <= mEnd);
+		mPos = mData + pos;
+	}
 
-    size_t MemoryDataStream::tell() const
+	size_t MemoryDataStream::tell() const
 	{
 		return mPos - mData;
 	}
 
-    bool MemoryDataStream::eof() const
-    {
-        return mPos >= mEnd;
-    }
+	bool MemoryDataStream::eof() const
+	{
+		return mPos >= mEnd;
+	}
 
 	SPtr<DataStream> MemoryDataStream::clone(bool copyData) const
 	{
@@ -357,20 +367,20 @@ namespace bs
 		return bs_shared_ptr_new<MemoryDataStream>(*this);
 	}
 
-    void MemoryDataStream::close()    
-    {
-        if (mData != nullptr)
-        {
+	void MemoryDataStream::close()    
+	{
+		if (mData != nullptr)
+		{
 			if(mFreeOnClose)
 				bs_free(mData);
 
-            mData = nullptr;
-        }
-    }
+			mData = nullptr;
+		}
+	}
 
-    FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
-        : DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
-    {
+	FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
+		: DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
+	{
 		// Always open in binary mode
 		// Also, always include reading
 		std::ios::openmode mode = std::ios::binary;
@@ -399,22 +409,22 @@ namespace bs
 			return;
 		}
 		
-        mInStream->seekg(0, std::ios_base::end);
-        mSize = (size_t)mInStream->tellg();
-        mInStream->seekg(0, std::ios_base::beg);
-    }
-
-    FileDataStream::~FileDataStream()
-    {
-        close();
-    }
-
-    size_t FileDataStream::read(void* buf, size_t count)
-    {
+		mInStream->seekg(0, std::ios_base::end);
+		mSize = (size_t)mInStream->tellg();
+		mInStream->seekg(0, std::ios_base::beg);
+	}
+
+	FileDataStream::~FileDataStream()
+	{
+		close();
+	}
+
+	size_t FileDataStream::read(void* buf, size_t count)
+	{
 		mInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
 
-        return (size_t)mInStream->gcount();
-    }
+		return (size_t)mInStream->gcount();
+	}
 
 	size_t FileDataStream::write(const void* buf, size_t count)
 	{
@@ -427,41 +437,41 @@ namespace bs
 
 		return written;
 	}
-    void FileDataStream::skip(size_t count)
-    {	
+	void FileDataStream::skip(size_t count)
+	{	
 		mInStream->clear(); // Clear fail status in case eof was set
 		mInStream->seekg(static_cast<std::ifstream::pos_type>(count), std::ios::cur);
-    }
+	}
 
-    void FileDataStream::seek(size_t pos)
-    {
+	void FileDataStream::seek(size_t pos)
+	{
 		mInStream->clear(); // Clear fail status in case eof was set
 		mInStream->seekg(static_cast<std::streamoff>(pos), std::ios::beg);
 	}
 
-    size_t FileDataStream::tell() const
+	size_t FileDataStream::tell() const
 	{
 		mInStream->clear(); // Clear fail status in case eof was set
 
 		return (size_t)mInStream->tellg();
 	}
 
-    bool FileDataStream::eof() const
-    {
-        return mInStream->eof();
-    }
+	bool FileDataStream::eof() const
+	{
+		return mInStream->eof();
+	}
 
 	SPtr<DataStream> FileDataStream::clone(bool copyData) const
 	{
 		return bs_shared_ptr_new<FileDataStream>(mPath, (AccessMode)getAccessMode(), true);
 	}
 
-    void FileDataStream::close()
-    {
-        if (mInStream)
-        {
+	void FileDataStream::close()
+	{
+		if (mInStream)
+		{
 			if (mFStreamRO)
-	            mFStreamRO->close();
+				mFStreamRO->close();
 
 			if (mFStream)
 			{
@@ -469,12 +479,12 @@ namespace bs
 				mFStream->close();
 			}
 
-            if (mFreeOnClose)
-            {
+			if (mFreeOnClose)
+			{
 				mInStream = nullptr;
 				mFStreamRO = nullptr; 
 				mFStream = nullptr; 
-            }
-        }
-    }
+			}
+		}
+	}
 }

+ 2 - 1
Source/BansheeUtility/Source/BsDebug.cpp

@@ -52,7 +52,8 @@ namespace bs
 		logToIDEConsole(msg);
 	}
 
-	void Debug::writeAsBMP(UINT8* rawPixels, UINT32 bytesPerPixel, UINT32 width, UINT32 height, const Path& filePath, bool overwrite) const
+	void Debug::writeAsBMP(UINT8* rawPixels, UINT32 bytesPerPixel, UINT32 width, UINT32 height, const Path& filePath, 
+		bool overwrite) const
 	{
 		if(FileSystem::isFile(filePath))
 		{

+ 1 - 1
Source/BansheeUtility/Source/BsFileSerializer.cpp

@@ -22,7 +22,7 @@ namespace bs
 		Path parentDir = fileLocation.getDirectory();
 		if (!FileSystem::exists(parentDir))
 			FileSystem::createDir(parentDir);
-
+		
 		mOutputStream.open(fileLocation.toPlatformString().c_str(), std::ios::out | std::ios::binary);
 		if (mOutputStream.fail())
 		{

+ 11 - 3
Source/BansheeUtility/Source/BsMatrix4.cpp

@@ -376,15 +376,23 @@ namespace bs
 		mat[3][0] = 0.0f;	mat[3][1] = 0.0f;	mat[3][2] = -1;	mat[3][3] = 0.0f;
 
 		return mat;
-    }
+	}
+
+	Matrix4 Matrix4::projectionOrthographic(float left, float right, float top, float bottom, float near, float far)
+	{
+		Matrix4 output;
+		output.makeProjectionOrtho(left, right, top, bottom, near, far);
+
+		return output;
+	}
 
 	Matrix4 Matrix4::view(const Vector3& position, const Quaternion& orientation)
-    {
+	{
 		Matrix4 mat;
 		mat.makeView(position, orientation);
 
 		return mat;
-    }
+	}
 
 	Matrix4 Matrix4::TRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
 	{

+ 0 - 332
Source/BansheeUtility/Source/BsTexAtlasGenerator.cpp

@@ -1,332 +0,0 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsTexAtlasGenerator.h"
-#include "BsDebug.h"
-
-namespace bs
-{
-	class TexAtlasNode
-	{
-	public:
-		TexAtlasNode()
-			:x(0), y(0), width(0), height(0), children(nullptr), nodeFull(false)
-		{ }
-
-		TexAtlasNode(UINT32 _x, UINT32 _y, UINT32 _width, UINT32 _height)
-			:x(_x), y(_y), width(_width), height(_height), children(nullptr), nodeFull(false)
-		{ }
-
-		~TexAtlasNode()
-		{
-			if(children != nullptr)
-				bs_deleteN(children, 2);
-		}
-
-		UINT32 x, y, width, height;
-		TexAtlasNode* children;
-		bool nodeFull;
-
-		bool insert(TexAtlasElementDesc& element)
-		{
-			float aspect = width / (float)height;
-
-			return insert(element, aspect);
-		}
-
-		bool insert(TexAtlasElementDesc& element, float aspect)
-		{
-			if (children != nullptr)
-			{
-				if (children[0].insert(element))
-					return true;
-
-				return children[1].insert(element);
-			}
-			else
-			{
-				if(nodeFull)
-					return false;
-
-				if (element.input.width > width || element.input.height > height)
-					return false;
-
-				if (element.input.width == width && element.input.height == height)
-				{
-					element.output.x = x;
-					element.output.y = y;
-					nodeFull = true;
-
-					return true;
-				}
-
-				float dw = (float)(width - element.input.width);
-				float dh = (height - element.input.height) * aspect;
-
-				children = bs_newN<TexAtlasNode>(2);
-
-				if (dw > dh)
-				{
-					children[0].x = x;
-					children[0].y = y;
-					children[0].width = element.input.width;
-					children[0].height = height;
-
-					children[1].x = x + element.input.width;
-					children[1].y = y;
-					children[1].width = width - element.input.width;
-					children[1].height = height;
-				}
-				else
-				{
-					children[0].x = x;
-					children[0].y = y;
-					children[0].width = width;
-					children[0].height = element.input.height;
-
-					children[1].x = x;
-					children[1].y = y + element.input.height;
-					children[1].width = width;
-					children[1].height = height - element.input.height;
-				}
-
-				return children[0].insert(element);
-			}
-		}
-	};
-
-	TexAtlasGenerator::TexAtlasGenerator(bool square, UINT32 maxTexWidth, UINT32 maxTexHeight, bool fixedSize)
-		:mSquare(square), mFixedSize(fixedSize), mMaxTexWidth(maxTexWidth), mMaxTexHeight(maxTexHeight)
-	{
-		if(square)
-		{
-			if(maxTexWidth > maxTexHeight)
-				maxTexWidth = maxTexHeight;
-
-			if(maxTexHeight > maxTexWidth)
-				maxTexHeight = maxTexWidth;
-		}
-	}
-
-	Vector<TexAtlasPageDesc> TexAtlasGenerator::createAtlasLayout(Vector<TexAtlasElementDesc>& elements) const
-	{
-		for(size_t i = 0; i < elements.size(); i++)
-			elements[i].output.page = -1;
-
-		//sortBySize(elements);
-		int numPages = generatePagesForSize(elements, mMaxTexWidth, mMaxTexHeight);
-
-		if(numPages == -1)
-		{
-			LOGWRN("Some of the provided elements don't fit in an atlas of provided size. Returning empty array of pages.");
-			return Vector<TexAtlasPageDesc>();
-		}
-
-		if(numPages == 0)
-			return Vector<TexAtlasPageDesc>();
-
-		UINT32 lastPageWidth = mMaxTexWidth;
-		UINT32 lastPageHeight = mMaxTexHeight;
-		INT32 lastPageIdx = numPages - 1;
-
-		// If size isn't fixed, try to reduce the size of the last page
-		if(!mFixedSize)
-		{
-			while (true)
-			{
-				if (lastPageWidth <= 1 || lastPageHeight <= 1)
-					break;
-
-				int newLastPageWidth = lastPageWidth;
-				int newLastPageHeight = lastPageHeight;
-
-				if(mSquare)
-				{
-					if (newLastPageWidth > newLastPageHeight)
-						newLastPageWidth /= 2;
-					else
-						newLastPageHeight /= 2;
-				}
-				else
-				{
-					if (newLastPageWidth > newLastPageHeight)
-						newLastPageWidth /= 2;
-					else
-						newLastPageHeight /= 2;
-				}
-
-				// Clear page indexes so we know which pages to process
-				for(size_t i = 0; i < elements.size(); i++)
-				{
-					if(elements[i].output.page >= lastPageIdx)
-						elements[i].output.page = -1;
-				}
-
-				if(generatePagesForSize(elements, newLastPageWidth, newLastPageHeight, lastPageIdx) == 1)
-				{
-					lastPageWidth = newLastPageWidth;
-					lastPageHeight = newLastPageHeight;
-				}
-				else
-				{
-					// We're done but we need to re-do all the pages with the last valid size
-					for(size_t i = 0; i < elements.size(); i++)
-					{
-						if(elements[i].output.page >= lastPageIdx)
-							elements[i].output.page = -1;
-					}
-
-					generatePagesForSize(elements, lastPageWidth, lastPageHeight, lastPageIdx);
-
-					break;
-				}
-			}
-		}
-
-		// Handle degenerate case
-		for(size_t i = 0; i < elements.size(); i++)
-		{
-			if(elements[i].output.page == -1 && elements[i].input.width == 0 && elements[i].input.height == 0)
-			{
-				elements[i].output.x = 0;
-				elements[i].output.y = 0;
-			}
-		}
-
-		// Create page descriptors and return
-		Vector<TexAtlasPageDesc> pages;
-		for(int i = 0; i < numPages - 1; i++)
-		{
-			TexAtlasPageDesc pageDesc;
-			pageDesc.width = mMaxTexWidth;
-			pageDesc.height = mMaxTexHeight;
-
-			pages.push_back(pageDesc);
-		}
-
-		TexAtlasPageDesc lastPageDesc;
-		lastPageDesc.width = lastPageWidth;
-		lastPageDesc.height = lastPageHeight;
-
-		pages.push_back(lastPageDesc);
-
-		return pages;
-	}
-
-	int TexAtlasGenerator::generatePagesForSize(Vector<TexAtlasElementDesc>& elements, UINT32 width, UINT32 height, UINT32 startPage) const
-	{
-		if(elements.size() == 0)
-			return 0;
-
-		int currentPage = startPage;
-		int numPages = 0;
-		while (true)
-		{
-			int largestTexId = findLargestTextureWithoutPage(elements); // Start with the largest available texture
-
-			if (largestTexId == -1) // No more textures, we're done
-				return numPages;
-
-			TexAtlasElementDesc& currentElem = elements[largestTexId];
-
-			// If the texture is larger than the atlas size then it can never fit
-			while (width < currentElem.input.width || height < currentElem.input.height)
-				return -1;
-
-			TexAtlasNode atlasNode(0, 0, width, height);
-			atlasNode.insert(elements[largestTexId]);
-			elements[largestTexId].output.page = currentPage;
-
-			// Now that the first (largest) texture has been added, do the same for every other texture until atlas is full
-			while (true)
-			{
-				int addedTextureId = addLargestTextureWithoutPageThatFits(elements, atlasNode); // Try to add next largest texture
-				if (addedTextureId == -1)
-					break;
-
-				elements[addedTextureId].output.page = currentPage;
-			}
-
-			currentPage++;
-			numPages++;
-		}
-	}
-
-	int TexAtlasGenerator::addLargestTextureWithoutPageThatFits(Vector<TexAtlasElementDesc>& elements, TexAtlasNode& node) const
-	{
-		UINT32 sizeLimit = std::numeric_limits<UINT32>::max();
-		while (true)
-		{
-			UINT32 largestSize = 0;
-			INT32 largestId = -1;
-
-			for (size_t i = 0; i < elements.size(); i++)
-			{
-				if (elements[i].output.page == -1) // Signifies that we haven't processed this node yet
-				{
-					UINT32 size = elements[i].input.width * elements[i].input.height;
-					if (size > largestSize && size < sizeLimit)
-					{
-						largestSize = size;
-						largestId = (INT32)i;
-					}
-				}
-			}
-
-			if (largestId == -1)
-				return -1;
-
-			if (node.insert(elements[largestId]))
-				return largestId;
-			else
-				sizeLimit = largestSize;
-		}
-	}
-
-	int TexAtlasGenerator::findLargestTextureWithoutPage(const Vector<TexAtlasElementDesc>& elements) const
-	{
-		INT32 largestId = -1;
-		UINT32 largestSize = 0;
-
-		for (size_t i = 0; i < elements.size(); i++)
-		{
-			if (elements[i].output.page == -1) // Signifies that we haven't processed this node yet
-			{
-				UINT32 size = elements[i].input.width * elements[i].input.height;
-				if (size > largestSize)
-				{
-					largestSize = size;
-					largestId = (INT32)i;
-				}
-			}
-		}
-
-		return largestId;
-	}
-
-	void TexAtlasGenerator::sortBySize(Vector<TexAtlasElementDesc>& elements) const
-	{
-		for (size_t i = 0; i < elements.size(); i++)
-		{
-			INT32 largestId = -1;
-			UINT32 largestSize = 0;
-
-			for (size_t j = i; j < elements.size(); j++)
-			{
-				UINT32 size = elements[j].input.width * elements[j].input.height;
-
-				if (size > largestSize)
-				{
-					largestSize = size;
-					largestId = (INT32)j;
-				}
-			}
-
-			if(largestId != -1)
-			{
-				TexAtlasElementDesc temp = elements[i];
-				elements[i] = elements[largestId];
-				elements[largestId] = temp;
-			}
-		}
-	}
-}

+ 187 - 0
Source/BansheeUtility/Source/BsTextureAtlasLayout.cpp

@@ -0,0 +1,187 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsTextureAtlasLayout.h"
+#include "BsDebug.h"
+#include "BsBitwise.h"
+
+namespace bs
+{
+	TextureAtlasLayout::TexAtlasNode::TexAtlasNode()
+		:x(0), y(0), width(0), height(0), children{ (UINT32)-1, (UINT32)-1 }, nodeFull(false)
+	{ }
+
+	TextureAtlasLayout::TexAtlasNode::TexAtlasNode(UINT32 x, UINT32 y, UINT32 width, UINT32 height)
+		: x(x), y(y), width(width), height(height), children{ (UINT32)-1, (UINT32)-1 }, nodeFull(false)
+	{ }
+
+	TextureAtlasLayout::TextureAtlasLayout()
+		: mWidth(0), mHeight(0), mMaxWidth(0), mMaxHeight(0), mPow2(false)
+	{ }
+
+	TextureAtlasLayout::TextureAtlasLayout(UINT32 width, UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2)
+		: mWidth(width), mHeight(height), mMaxWidth(maxWidth), mMaxHeight(maxHeight), mPow2(pow2)
+	{
+		mNodes.push_back(TexAtlasNode(0, 0, maxWidth, maxHeight));
+	}
+
+	bool TextureAtlasLayout::addElement(UINT32 width, UINT32 height, UINT32& x, UINT32& y)
+	{
+		if(width == 0 || height == 0)
+		{
+			x = 0;
+			y = 0;
+			return true;
+		}
+
+		// Try adding without expanding, if that fails try to expand
+		if(!addToNode(0, width, height, x, y, false))
+		{
+			if (!addToNode(0, width, height, x, y, true))
+				return false;
+		}
+
+		// Update size to cover all nodes
+		if(mPow2)
+		{
+			mWidth = std::max(mWidth, Bitwise::nextPow2(x + width));
+			mHeight = std::max(mHeight, Bitwise::nextPow2(y + height));
+		}
+		else
+		{
+			mWidth = std::max(mWidth, x + width);
+			mHeight = std::max(mHeight, y + height);
+		}
+
+		return true;
+	}
+
+	bool TextureAtlasLayout::addToNode(UINT32 nodeIdx, UINT32 width, UINT32 height, UINT32& x, UINT32& y, bool allowGrowth)
+	{
+		TexAtlasNode* node = &mNodes[nodeIdx];
+		float aspect = node->width / (float)node->height;
+
+		if (node->children[0] != -1)
+		{
+			if (addToNode(node->children[0], width, height, x, y, allowGrowth))
+				return true;
+
+			return addToNode(node->children[1], width, height, x, y, allowGrowth);
+		}
+		else
+		{
+			if (node->nodeFull)
+				return false;
+
+			if (width > node->width || height > node->height)
+				return false;
+
+			if(!allowGrowth)
+			{
+				if (node->x + width > mWidth || node->y + height > mHeight)
+					return false;
+			}
+
+			if (width == node->width && height == node->height)
+			{
+				x = node->x;
+				y = node->y;
+				node->nodeFull = true;
+
+				return true;
+			}
+
+			float dw = (float)(node->width - width);
+			float dh = (node->height - height) * aspect;
+
+			UINT32 nextChildIdx = (UINT32)mNodes.size();
+			node->children[0] = nextChildIdx;
+			node->children[1] = nextChildIdx + 1;
+
+			TexAtlasNode nodeCopy = *node;
+			node = nullptr; // Undefined past this point
+			if (dw > dh)
+			{
+				mNodes.emplace_back(nodeCopy.x, nodeCopy.y, width, nodeCopy.height);
+				mNodes.emplace_back(nodeCopy.x + width, nodeCopy.y, nodeCopy.width - width, nodeCopy.height);
+			}
+			else
+			{
+				mNodes.emplace_back(nodeCopy.x, nodeCopy.y, nodeCopy.width, height);
+				mNodes.emplace_back(nodeCopy.x, nodeCopy.y + height, nodeCopy.width, nodeCopy.height - height);
+			}
+
+			return addToNode(nodeCopy.children[0], width, height, x, y, allowGrowth);
+		}
+	}
+
+	Vector<TextureAtlasUtility::Page> TextureAtlasUtility::createAtlasLayout(Vector<Element>& elements, UINT32 width, 
+		UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2)
+	{
+		for (size_t i = 0; i < elements.size(); i++)
+		{
+			elements[i].output.idx = i; // Preserve original index before sorting
+			elements[i].output.page = -1;
+		}
+
+		std::sort(elements.begin(), elements.end(), 
+			[](const Element& a, const Element& b)
+		{
+			return a.input.width * a.input.height > b.input.width * b.input.height;
+		});
+
+		Vector<TextureAtlasLayout> layouts;
+		UINT32 remainingCount = (UINT32)elements.size();
+		while (remainingCount > 0)
+		{
+			layouts.push_back(TextureAtlasLayout(width, height, maxWidth, maxHeight, pow2));
+			TextureAtlasLayout& curLayout = layouts.back();
+
+			// Find largest unassigned element that fits
+			UINT32 sizeLimit = std::numeric_limits<UINT32>::max();
+			while (true)
+			{
+				UINT32 largestId = -1;
+
+				// Assumes elements are sorted from largest to smallest
+				for (UINT32 i = 0; i < (UINT32)elements.size(); i++)
+				{
+					if (elements[i].output.page == -1)
+					{
+						UINT32 size = elements[i].input.width * elements[i].input.height;
+						if (size < sizeLimit)
+						{
+							largestId = i;
+							break;
+						}
+					}
+				}
+
+				if (largestId == -1)
+					break; // Nothing fits, start a new page
+
+				Element& element = elements[largestId];
+
+				// Check if an element is too large to ever fit
+				if(element.input.width > maxWidth || element.input.height > maxHeight)
+				{
+					LOGWRN("Some of the provided elements don't fit in an atlas of provided size. Returning empty array of pages.");
+					return Vector<Page>();
+				}
+
+				if (curLayout.addElement(element.input.width, element.input.height, element.output.x, element.output.y))
+				{
+					element.output.page = (UINT32)layouts.size() - 1;
+					remainingCount--;
+				}
+				else
+					sizeLimit = element.input.width * element.input.height;
+			}
+		}
+
+		Vector<Page> pages;
+		for (auto& layout : layouts)
+			pages.push_back({ layout.getWidth(), layout.getHeight() });
+
+		return pages;
+	}
+}

+ 44 - 0
Source/CMake/Modules/FindSnappy.cmake

@@ -0,0 +1,44 @@
+# Find Snappy dependency
+#
+# This module defines
+#  Snappy_INCLUDE_DIRS
+#  Snappy_LIBRARIES
+#  Snappy_FOUND
+
+set(Snappy_INSTALL_DIR ${PROJECT_SOURCE_DIR}/../Dependencies/Snappy CACHE PATH "")
+set(Snappy_INCLUDE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/include")
+
+if(BS_64BIT)
+	list(APPEND Snappy_LIBRARY_RELEASE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x64/Release")
+	list(APPEND Snappy_LIBRARY_DEBUG_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x64/Debug")
+else()
+	list(APPEND Snappy_LIBRARY_RELEASE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x86/Release")
+	list(APPEND Snappy_LIBRARY_DEBUG_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x86/Debug")
+endif()
+
+message(STATUS "Looking for Snappy installation...")
+	
+find_path(Snappy_INCLUDE_DIR snappy.h PATHS ${Snappy_INCLUDE_SEARCH_DIRS})	
+	
+if(Snappy_INCLUDE_DIR)
+	set(Snappy_FOUND TRUE)
+else()
+	set(Snappy_FOUND FALSE)
+endif()	
+	
+find_imported_library(Snappy Snappy)
+
+if(NOT Snappy_FOUND)
+	if(Snappy_FIND_REQUIRED)
+		message(FATAL_ERROR "Cannot find Snappy installation. Try modifying the Snappy_INSTALL_DIR path.")
+	elseif(NOT Snappy_FIND_QUIETLY)
+		message(WARNING "Cannot find Snappy installation. Try modifying the Snappy_INSTALL_DIR path.")
+	endif()
+else()
+	message(STATUS "...Snappy OK.")
+endif()
+
+mark_as_advanced(Snappy_INSTALL_DIR Snappy_INCLUDE_DIR)
+
+set(Snappy_INCLUDE_DIRS ${Snappy_INCLUDE_DIR})
+set(Snappy_LIBRARIES Snappy)

+ 3 - 3
Source/CMakeLists.txt

@@ -5,7 +5,7 @@ project (Banshee)
 set (BS_VERSION_MAJOR 0)
 set (BS_VERSION_MINOR 4)
 
-set (BS_PREBUILT_DEPENDENCIES_VERSION 1)
+set (BS_PREBUILT_DEPENDENCIES_VERSION 2)
 set (BS_SRC_DEPENDENCIES_VERSION 8)
 
 # Configuration types
@@ -65,11 +65,11 @@ mark_as_advanced(CMAKE_INSTALL_PREFIX)
 ## Check prebuilt dependencies that user downloads in a .zip
 set(BUILTIN_DEP_VERSION_FILE ${PROJECT_SOURCE_DIR}/../Dependencies/.version)
 if(NOT EXISTS ${BUILTIN_DEP_VERSION_FILE})
-	message(FATAL_ERROR "Precompiled dependencies package is missing our out of date. Re-run setup or manually re-download and extract dependencies.")
+	message(FATAL_ERROR "Precompiled dependencies package is missing our out of date. Re-run setup or manually download the latest dependencies package.")
 else()
 	file (STRINGS ${BUILTIN_DEP_VERSION_FILE} CURRENT_BUILTIN_DEP_VERSION)
 	if(${BS_PREBUILT_DEPENDENCIES_VERSION} GREATER ${CURRENT_BUILTIN_DEP_VERSION})
-		message(FATAL_ERROR "Your precomiled dependencies package is out of date. Re-run setup or manually re-download and extract dependencies.")
+		message(FATAL_ERROR "Your precomiled dependencies package is out of date. Re-run setup or manually download the latest dependencies package.")
 	endif()
 endif()
 	

+ 12 - 1
Source/MBansheeEditor/Windows/Inspector/InspectorWindow.cs

@@ -69,6 +69,7 @@ namespace BansheeEditor
         private int undoCommandIdx = -1;
         private GUITextBox soNameInput;
         private GUIToggle soActiveToggle;
+        private GUIEnumField soMobility;
         private GUILayout soPrefabLayout;
         private bool soHasPrefab;
         private GUIFloatField soPosX;
@@ -256,6 +257,14 @@ namespace BansheeEditor
             nameLayout.AddElement(soNameInput);
             nameLayout.AddFlexibleSpace();
 
+            GUILayoutX mobilityLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel mobilityLbl = new GUILabel(new LocEdString("Mobility"), GUIOption.FixedWidth(50));
+            soMobility = new GUIEnumField(typeof(ObjectMobility), "", 0, GUIOption.FixedWidth(85));
+            soMobility.Value = (ulong)activeSO.Mobility;
+            soMobility.OnSelectionChanged += value => activeSO.Mobility = (ObjectMobility) value;
+            mobilityLayout.AddElement(mobilityLbl);
+            mobilityLayout.AddElement(soMobility);
+
             soPrefabLayout = sceneObjectLayout.AddLayoutX();
 
             GUILayoutX positionLayout = sceneObjectLayout.AddLayoutX();
@@ -360,6 +369,7 @@ namespace BansheeEditor
 
             soNameInput.Text = activeSO.Name;
             soActiveToggle.Value = activeSO.Active;
+            soMobility.Value = (ulong) activeSO.Mobility;
 
             SceneObject prefabParent = PrefabUtility.GetPrefabParent(activeSO);
 
@@ -724,6 +734,7 @@ namespace BansheeEditor
             activeSO = null;
             soNameInput = null;
             soActiveToggle = null;
+            soMobility = null;
             soPrefabLayout = null;
             soHasPrefab = false;
             soPosX = null;
@@ -747,7 +758,7 @@ namespace BansheeEditor
         /// <returns>Area of the title bar, relative to the window.</returns>
         private Rect2I GetTitleBounds()
         {
-            return new Rect2I(0, 0, Width, 115);
+            return new Rect2I(0, 0, Width, 135);
         }
 
         /// <summary>

+ 7 - 3
Source/MBansheeEngine/Scene/GameObject.cs

@@ -43,8 +43,12 @@ namespace BansheeEngine
         /// <summary>
         /// Component will be notified when its parent changes.
         /// </summary>
-        Parent = 0x02
-    }
+        Parent = 0x02,
+        /// <summary>
+        /// Component will be notified when its scene object's mobility state changes.
+        /// </summary>
+        Mobility = 0x04
+}
 
-    /** @} */
+/** @} */
 }

+ 18 - 0
Source/MBansheeEngine/Scene/SceneObject.cs

@@ -42,6 +42,18 @@ namespace BansheeEngine
             set { Internal_SetActive(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+        /// with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+        /// the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+        /// set. By default scene object's mobility is unrestricted.
+        /// </summary>
+        public ObjectMobility Mobility
+        {
+            get { return (ObjectMobility)Internal_GetMobility(mCachedPtr); }
+            set { Internal_SetMobility(mCachedPtr, (int)value); }
+        }
+
         /// <summary>
         /// World position. This includes local position of this object, plus position offset of any parents.
         /// </summary>
@@ -468,6 +480,12 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_GetActive(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMobility(IntPtr nativeInstance, int value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetMobility(IntPtr nativeInstance);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetParent(IntPtr nativeInstance, SceneObject parent);
 

+ 4 - 2
Source/RenderBeast/CMakeSources.cmake

@@ -9,10 +9,11 @@ set(BS_RENDERBEAST_INC_NOFILTER
 	"Include/BsObjectRendering.h"
 	"Include/BsLightRendering.h"
 	"Include/BsPostProcessing.h"
-	"Include/BsRendererCamera.h"
+	"Include/BsRendererView.h"
 	"Include/BsRendererObject.h"
 	"Include/BsLightGrid.h"
 	"Include/BsImageBasedLighting.h"
+	"Include/BsShadowRendering.h"
 )
 
 set(BS_RENDERBEAST_SRC_NOFILTER
@@ -25,10 +26,11 @@ set(BS_RENDERBEAST_SRC_NOFILTER
 	"Source/BsObjectRendering.cpp"
 	"Source/BsLightRendering.cpp"
 	"Source/BsPostProcessing.cpp"
-	"Source/BsRendererCamera.cpp"
+	"Source/BsRendererView.cpp"
 	"Source/BsRendererObject.cpp"
 	"Source/BsLightGrid.cpp"
 	"Source/BsImageBasedLighting.cpp"
+	"Source/BsShadowRendering.cpp"
 )
 
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})

+ 1 - 1
Source/RenderBeast/Include/BsImageBasedLighting.h

@@ -72,7 +72,7 @@ namespace bs { namespace ct
 		bool customTexture : 1;
 		bool textureDirty : 1;
 		bool arrayDirty : 1;
-		bool errorFlagged : 1;
+		mutable bool errorFlagged : 1;
 	};
 
 	BS_PARAM_BLOCK_BEGIN(TiledImageBasedLightingParamDef)

+ 3 - 3
Source/RenderBeast/Include/BsLightGrid.h

@@ -43,7 +43,7 @@ namespace bs { namespace ct
 					   const SPtr<GpuBuffer>& lightsBuffer, const SPtr<GpuBuffer>& probesBuffer);
 
 		/** Binds the material for rendering, sets up per-camera parameters and executes it. */
-		void execute(const RendererCamera& view);
+		void execute(const RendererView& view);
 
 		/** Returns the buffers generated by execute(). */
 		void getOutputs(SPtr<GpuBuffer>& lightsLLHeads, SPtr<GpuBuffer>& lightsLL, SPtr<GpuBuffer>& probesLLHeads, 
@@ -85,7 +85,7 @@ namespace bs { namespace ct
 			const SPtr<GpuBuffer>& probeLLHeads, const SPtr<GpuBuffer>& probeLL);
 
 		/** Binds the material for renderingand executes it. */
-		void execute(const RendererCamera& view);
+		void execute(const RendererView& view);
 
 		/** Returns the buffers generated by execute(). */
 		void getOutputs(SPtr<GpuBuffer>& gridLightOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices,
@@ -127,7 +127,7 @@ namespace bs { namespace ct
 		LightGrid();
 
 		/** Updates the light grid from the provided view. */
-		void updateGrid(const RendererCamera& view, const GPULightData& lightData, const GPUReflProbeData& probeData, 
+		void updateGrid(const RendererView& view, const GPULightData& lightData, const GPUReflProbeData& probeData, 
 			bool noLighting);
 
 		/** 

+ 1 - 1
Source/RenderBeast/Include/BsPostProcessing.h

@@ -247,7 +247,7 @@ namespace bs { namespace ct
 		 * view's final output render target. Once the method exits, final render target is guaranteed to be currently
 		 * bound for rendering. 
 		 */
-		void postProcess(RendererCamera* viewInfo, const SPtr<Texture>& sceneColor, float frameDelta);
+		void postProcess(RendererView* viewInfo, const SPtr<Texture>& sceneColor, float frameDelta);
 		
 	private:
 		DownsampleMat mDownsample;

+ 8 - 50
Source/RenderBeast/Include/BsRenderBeast.h

@@ -4,15 +4,14 @@
 
 #include "BsRenderBeastPrerequisites.h"
 #include "BsRenderer.h"
-#include "BsBounds.h"
-#include "BsSamplerOverrides.h"
 #include "BsRendererMaterial.h"
 #include "BsLightRendering.h"
 #include "BsImageBasedLighting.h"
 #include "BsObjectRendering.h"
 #include "BsPostProcessing.h"
-#include "BsRendererCamera.h"
+#include "BsRendererView.h"
 #include "BsRendererObject.h"
+#include "BsRendererScene.h"
 
 namespace bs 
 { 
@@ -41,13 +40,6 @@ namespace bs
 	 */
 	class RenderBeast : public Renderer
 	{
-		/**	Renderer information specific to a single render target. */
-		struct RendererRenderTarget
-		{
-			SPtr<RenderTarget> target;
-			Vector<const Camera*> cameras;
-		};
-
 		/** Renderer information for a single material. */
 		struct RendererMaterial
 		{
@@ -137,16 +129,6 @@ namespace bs
 		/** @copydoc Renderer::notifySkyboxRemoved */
 		void notifySkyboxRemoved(Skybox* skybox) override;
 
-		/** 
-		 * Updates (or adds) renderer specific data for the specified camera. Should be called whenever camera properties
-		 * change. 
-		 *
-		 * @param[in]	camera		Camera whose data to update.
-		 * @param[in]	forceRemove	If true, the camera data will be removed instead of updated.
-		 * @return					Renderer camera object that represents the camera. Null if camera was removed.
-		 */
-		RendererCamera* updateCameraData(const Camera* camera, bool forceRemove = false);
-
 		/**
 		 * Updates the render options on the core thread.
 		 *
@@ -169,21 +151,21 @@ namespace bs
 		 * 
 		 * @note	Core thread only. 
 		 */
-		void renderViews(RendererCamera** views, UINT32 numViews, const FrameInfo& frameInfo);
+		void renderViews(RendererView** views, UINT32 numViews, const FrameInfo& frameInfo);
 
 		/**
 		 * Renders all objects visible by the provided view.
 		 *			
 		 * @note	Core thread only.
 		 */
-		void renderView(RendererCamera* viewInfo, float frameDelta);
+		void renderView(RendererView* viewInfo, float frameDelta);
 
 		/**
 		 * Renders all overlay callbacks of the provided view.
 		 * 					
 		 * @note	Core thread only.
 		 */
-		void renderOverlay(RendererCamera* viewInfo);
+		void renderOverlay(RendererView* viewInfo);
 
 		/** 
 		 * Renders a single element of a renderable object. 
@@ -215,36 +197,15 @@ namespace bs
 		/** Updates light probes, rendering & filtering ones that are dirty and updating the global probe cubemap array. */
 		void updateLightProbes(const FrameInfo& frameInfo);
 
-		/**
-		 * Checks all sampler overrides in case material sampler states changed, and updates them.
-		 *
-		 * @param[in]	force	If true, all sampler overrides will be updated, regardless of a change in the material
-		 *						was detected or not.
-		 */
-		void refreshSamplerOverrides(bool force = false);
-
 		// Core thread only fields
 
 		// Scene data
-		//// Cameras and render targets
-		Vector<RendererRenderTarget> mRenderTargets;
-		UnorderedMap<const Camera*, RendererCamera*> mCameras;
-		
-		//// Renderables
-		Vector<RendererObject*> mRenderables;
-		Vector<CullInfo> mRenderableCullInfos;
+		SPtr<RendererScene> mScene;
 		Vector<bool> mRenderableVisibility; // Transient
-
-		//// Lights
-		Vector<RendererLight> mDirectionalLights;
-		Vector<RendererLight> mRadialLights;
-		Vector<RendererLight> mSpotLights;
-		Vector<Sphere> mPointLightWorldBounds;
-		Vector<Sphere> mSpotLightWorldBounds;
+		Vector<bool> mRadialLightVisibility; // Transient
+		Vector<bool> mSpotLightVisibility; // Transient
 
 		//// Reflection probes
-		Vector<RendererReflectionProbe> mReflProbes;
-		Vector<Sphere> mReflProbeWorldBounds;
 		Vector<bool> mCubemapArrayUsedSlots;
 		SPtr<Texture> mReflCubemapArrayTex;
 
@@ -256,7 +217,6 @@ namespace bs
 
 		// Materials & GPU data
 		//// Base pass
-		DefaultMaterial* mDefaultMaterial = nullptr;
 		ObjectRenderer* mObjectRenderer = nullptr;
 
 		//// Lighting
@@ -277,11 +237,9 @@ namespace bs
 		FlatFramebufferToTextureMat* mFlatFramebufferToTextureMat = nullptr;
 
 		SPtr<RenderBeastOptions> mCoreOptions;
-		UnorderedMap<SamplerOverrideKey, MaterialSamplerOverrides*> mSamplerOverrides;
 
 		// Helpers to avoid memory allocations
 		Vector<LightData> mLightDataTemp;
-		Vector<bool> mLightVisibilityTemp;
 
 		Vector<ReflProbeData> mReflProbeDataTemp;
 		Vector<bool> mReflProbeVisibilityTemp;

+ 1 - 1
Source/RenderBeast/Include/BsRenderBeastPrerequisites.h

@@ -20,6 +20,6 @@ namespace bs { namespace ct
 	struct RenderBeastOptions;
 	struct PooledRenderTexture;
 	class RenderTargets;
-	class RendererCamera;
+	class RendererView;
 	struct LightData;
 }}

+ 1 - 1
Source/RenderBeast/Include/BsRenderTargets.h

@@ -4,7 +4,7 @@
 
 #include "BsRenderBeastPrerequisites.h"
 #include "BsPixelUtil.h"
-#include "BsRendererCamera.h"
+#include "BsRendererView.h"
 
 namespace bs { namespace ct
 {

+ 126 - 0
Source/RenderBeast/Include/BsRendererScene.h

@@ -0,0 +1,126 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsObjectRendering.h"
+#include "BsSamplerOverrides.h"
+#include "BsLightRendering.h"
+#include "BsRendererView.h"
+
+namespace bs 
+{ 
+	struct RendererAnimationData;
+
+	namespace ct
+	{
+	/** @addtogroup RenderBeast
+	 *  @{
+	 */
+
+	/** Contains most scene objects relevant to the renderer. */
+	struct SceneInfo
+	{
+		// Cameras and render targets
+		Vector<RendererRenderTarget> renderTargets;
+		UnorderedMap<const Camera*, RendererView*> views;
+		
+		// Renderables
+		Vector<RendererObject*> renderables;
+		Vector<CullInfo> renderableCullInfos;
+
+		// Lights
+		Vector<RendererLight> directionalLights;
+		Vector<RendererLight> radialLights;
+		Vector<RendererLight> spotLights;
+		Vector<Sphere> radialLightWorldBounds;
+		Vector<Sphere> spotLightWorldBounds;
+
+		// Reflection probes
+		Vector<RendererReflectionProbe> reflProbes;
+		Vector<Sphere> reflProbeWorldBounds;
+	};
+
+	/** Contains information about the scene (e.g. renderables, lights, cameras) required by the renderer. */
+	class RendererScene
+	{
+	public:
+		RendererScene(const SPtr<RenderBeastOptions>& options);
+		~RendererScene();
+
+		/** Registers a new camera in the scene. */
+		void registerCamera(const Camera* camera);
+
+		/** Updates information about a previously registered camera. */
+		void updateCamera(const Camera* camera, UINT32 updateFlag);
+
+		/** Removes a camera from the scene. */
+		void unregisterCamera(const Camera* camera);
+
+		/** Registers a new light in the scene. */
+		void registerLight(Light* light);
+
+		/** Updates information about a previously registered light. */
+		void updateLight(Light* light);
+
+		/** Removes a light from the scene. */
+		void unregisterLight(Light* light);
+
+		/** Registers a new renderable object in the scene. */
+		void registerRenderable(Renderable* renderable);
+
+		/** Updates information about a previously registered renderable object. */
+		void updateRenderable(Renderable* renderable);
+
+		/** Removes a renderable object from the scene. */
+		void unregisterRenderable(Renderable* renderable);
+
+		/** Registers a new reflection probe in the scene. */
+		void registerReflectionProbe(ReflectionProbe* probe);
+
+		/** Updates information about a previously registered reflection probe. */
+		void updateReflectionProbe(ReflectionProbe* probe);
+
+		/** Removes a reflection probe from the scene. */
+		void unregisterReflectionProbe(ReflectionProbe* probe);
+
+		/** Updates or replaces the filtered reflection texture of the probe at the specified index. */
+		void setReflectionProbeTexture(UINT32 probeIdx, const SPtr<Texture>& texture);
+
+		/** Updates the index at which the reflection probe's texture is stored at, in the global array. */
+		void setReflectionProbeArrayIndex(UINT32 probeIdx, UINT32 arrayIdx, bool markAsClean);
+
+		/** Returns a container with all relevant scene objects. */
+		const SceneInfo& getSceneInfo() const { return mInfo; }
+
+		/** Updates scene according to the newly provided renderer options. */
+		void setOptions(const SPtr<RenderBeastOptions>& options);
+
+		/**
+		 * Checks all sampler overrides in case material sampler states changed, and updates them.
+		 *
+		 * @param[in]	force	If true, all sampler overrides will be updated, regardless of a change in the material
+		 *						was detected or not.
+		 */
+		void refreshSamplerOverrides(bool force = false);
+	private:
+		/** 
+		 * Updates (or adds) renderer specific data for the specified camera. Should be called whenever camera properties
+		 * change. 
+		 *
+		 * @param[in]	camera		Camera whose data to update.
+		 * @param[in]	forceRemove	If true, the camera data will be removed instead of updated.
+		 * @return					Renderer view object that represents the camera. Null if camera was removed.
+		 */
+		RendererView* updateCameraData(const Camera* camera, bool forceRemove = false);
+
+		SceneInfo mInfo;
+		UnorderedMap<SamplerOverrideKey, MaterialSamplerOverrides*> mSamplerOverrides;
+
+		DefaultMaterial* mDefaultMaterial = nullptr;
+		SPtr<RenderBeastOptions> mOptions;
+	};
+
+
+	/** @} */
+}}

+ 65 - 72
Source/RenderBeast/Include/BsRendererCamera.h → Source/RenderBeast/Include/BsRendererView.h

@@ -60,8 +60,31 @@ namespace bs { namespace ct
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
 	};
 
-	/** Set of properties describing the output render target used by a renderer view. */
-	struct RENDERER_VIEW_TARGET_DESC
+	/** Data shared between RENDERER_VIEW_DESC and RendererViewProperties */
+	struct RendererViewData
+	{
+		Matrix4 viewTransform;
+		Matrix4 projTransform;
+		Vector3 viewDirection;
+		Vector3 viewOrigin;
+		bool flipView;
+		float nearPlane;
+		float farPlane;
+		ProjectionType projType;
+
+		bool isOverlay : 1;
+		bool isHDR : 1;
+		bool noLighting : 1;
+		bool triggerCallbacks : 1;
+		bool runPostProcessing : 1;
+		bool renderingReflections : 1;
+
+		UINT64 visibleLayers;
+		ConvexVolume cullFrustum;
+	};
+
+	/** Data shared between RENDERER_VIEW_TARGET_DESC and RendererViewTargetProperties */
+	struct RendererViewTargetData
 	{
 		SPtr<RenderTarget> target;
 
@@ -77,32 +100,35 @@ namespace bs { namespace ct
 		UINT16 clearStencilValue;
 	};
 
+	/** Set of properties describing the output render target used by a renderer view. */
+	struct RENDERER_VIEW_TARGET_DESC : RendererViewTargetData
+	{ };
+
 	/** Set of properties used describing a specific view that the renderer can render. */
-	struct RENDERER_VIEW_DESC
+	struct RENDERER_VIEW_DESC : RendererViewData
 	{
 		RENDERER_VIEW_TARGET_DESC target;
 
-		Matrix4 viewTransform;
-		Matrix4 projTransform;
-		Vector3 viewDirection;
-		Vector3 viewOrigin;
-		bool flipView;
-		float nearPlane;
-		float farPlane;
+		StateReduction stateReduction;
+		const Camera* sceneCamera;
+	};
 
-		bool isOverlay : 1;
-		bool isHDR : 1;
-		bool noLighting : 1;
-		bool triggerCallbacks : 1;
-		bool runPostProcessing : 1;
-		bool renderingReflections : 1;
+	/** Set of properties used describing a specific view that the renderer can render. */
+	struct RendererViewProperties : RendererViewData
+	{
+		RendererViewProperties() {}
+		RendererViewProperties(const RENDERER_VIEW_DESC& src);
 
-		UINT64 visibleLayers;
-		ConvexVolume cullFrustum;
+		Matrix4 viewProjTransform;
 
-		StateReduction stateReduction;
+		SPtr<RenderTarget> target;
+		Rect2 nrmViewRect;
+		UINT32 numSamples;
 
-		const Camera* sceneCamera;
+		UINT32 clearFlags;
+		Color clearColor;
+		float clearDepthValue;
+		UINT16 clearStencilValue;
 	};
 
 	/** Information whether certain scene objects are visible in a view, per object type. */
@@ -122,12 +148,19 @@ namespace bs { namespace ct
 		UINT64 layer;
 	};
 
-	/** Contains information about a Camera, used by the Renderer. */
-	class RendererCamera
+	/**	Renderer information specific to a single render target. */
+	struct RendererRenderTarget
+	{
+		SPtr<RenderTarget> target;
+		Vector<const Camera*> cameras;
+	};
+
+	/** Contains information about a single view into the scene, used by the renderer. */
+	class RendererView
 	{
 	public:
-		RendererCamera();
-		RendererCamera(const RENDERER_VIEW_DESC& desc);
+		RendererView();
+		RendererView(const RENDERER_VIEW_DESC& desc);
 
 		/** Sets state reduction mode that determines how do render queues group & sort renderables. */
 		void setStateReductionMode(StateReduction reductionMode);
@@ -142,53 +175,11 @@ namespace bs { namespace ct
 		/** Updates all internal information with new view information. */
 		void setView(const RENDERER_VIEW_DESC& desc);
 
-		/** Returns the world position of the view. */
-		Vector3 getViewOrigin() const { return mViewDesc.viewOrigin; }
-
-		/** Returns a matrix that contains combined projection and view transforms. */
-		Matrix4 getViewProjMatrix() const { return mViewDesc.projTransform * mViewDesc.viewTransform; }
-
-		/** Returns the distance to the near clipping plane. */
-		float getNearPlane() const { return mViewDesc.nearPlane; }
-
-		/** Returns the distance to the far clipping plane. */
-		float getFarPlane() const { return mViewDesc.farPlane; }
-
-		/** Returns true if the view requires high dynamic range rendering. */
-		bool isHDR() const { return mViewDesc.isHDR; }
-
-		/** Returns true if this view only renders overlay, and not scene objects. */
-		bool isOverlay() const { return mViewDesc.isOverlay; }
-
-		/** Returns true if the view should be rendered with no lighting. */
-		bool renderWithNoLighting() const { return mViewDesc.noLighting; }
-
-		/** Returns the final render target the rendered contents should be output to. */
-		SPtr<RenderTarget> getFinalTarget() const { return mViewDesc.target.target; }
-
-		/** Returns normalized coordinates of the viewport area this view renders to. */
-		Rect2 getViewportRect() const { return mViewDesc.target.nrmViewRect; }
-
-		/** Returns true if the resulting render target should be flipped vertically. */
-		bool getFlipView() const { return mViewDesc.flipView; }
-
-		/** Returns the color to clear the non-rendered areas of the scene color target to. */
-		Color getClearColor() const { return mViewDesc.target.clearColor; }
-
-		/** Returns the number of samples per pixel to render. */
-		UINT32 getNumSamples() const { return mViewDesc.target.numSamples; }
-
-		/** Returns true if the current view is being used to render reflection probes. */
-		bool isRenderingReflections() const { return mViewDesc.renderingReflections; }
+		/** Returns a structure describing the view. */
+		const RendererViewProperties& getProperties() const { return mProperties; }
 
 		/** Returns the scene camera this object is based of. This can be null for manually constructed renderer cameras. */
-		const Camera* getSceneCamera() const { return mViewDesc.sceneCamera; }
-
-		/** Returns true if external render callbacks should trigger for this view. */
-		bool checkTriggerCallbacks() const { return mViewDesc.triggerCallbacks; }
-
-		/** Returns true if post-processing effects should be triggered for this view. */
-		bool checkRunPostProcessing() const { return mViewDesc.runPostProcessing; }
+		const Camera* getSceneCamera() const { return mCamera; }
 
 		/** 
 		 * Prepares render targets for rendering. When done call endRendering().
@@ -235,13 +226,13 @@ namespace bs { namespace ct
 
 		/**
 		 * Culls the provided set of bounds against the current frustum and outputs a set of visibility flags determining
-		 * which entry is or ins't visible by this view. Both inputs must be arrays of the same size.
+		 * which entry is or isn't visible by this view. Both inputs must be arrays of the same size.
 		 */
 		void calculateVisibility(const Vector<CullInfo>& cullInfos, Vector<bool>& visibility) const;
 
 		/**
 		* Culls the provided set of bounds against the current frustum and outputs a set of visibility flags determining
-		* which entry is or ins't visible by this view. Both inputs must be arrays of the same size.
+		* which entry is or isn't visible by this view. Both inputs must be arrays of the same size.
 		*/
 		void calculateVisibility(const Vector<Sphere>& bounds, Vector<bool>& visibility) const;
 
@@ -281,7 +272,9 @@ namespace bs { namespace ct
 		 */
 		Vector2 getNDCZTransform(const Matrix4& projMatrix) const;
 
-		RENDERER_VIEW_DESC mViewDesc;
+		RendererViewProperties mProperties;
+		RENDERER_VIEW_TARGET_DESC mTargetDesc;
+		const Camera* mCamera;
 
 		SPtr<RenderQueue> mOpaqueQueue;
 		SPtr<RenderQueue> mTransparentQueue;

+ 52 - 0
Source/RenderBeast/Include/BsShadowRendering.h

@@ -0,0 +1,52 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsModule.h"
+#include "BsMatrix4.h"
+#include "BsConvexVolume.h"
+
+namespace bs { namespace ct
+{
+	/** @addtogroup RenderBeast
+	 *  @{
+	 */
+
+	// TODO - Define normal and omni vertex shaders and their params
+	// TODO - Move renderable objects from RenderBeast into a separate object so I can pass them here?
+	//  - SceneInfo?
+
+	/** Provides functionality for rendering shadow maps. */
+	class ShadowRendering : public Module<ShadowRendering>
+	{
+	public:
+		/**
+		 * Generates a frustum for a single cascade of a cascaded shadow map. Also outputs spherical bounds of the
+		 * split view frustum.
+		 * 
+		 * @param[in]	view		View whose frustum to split.
+		 * @param[in]	lightDir	Direction of the light for which we're generating the shadow map.
+		 * @param[in]	cascade		Index of the cascade to generate the frustum for.
+		 * @param[in]	numCascades	Maximum number of cascades in the cascaded shadow map. Must be greater than zero.
+		 * @param[out]	outBounds	Spherical bounds of the split view frustum.
+		 * @return					Convex volume covering the area of the split view frustum visible from the light.
+		 */
+		static ConvexVolume getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade, 
+			UINT32 numCascades, Sphere& outBounds);
+
+		/**
+		 * Finds the distance (along the view direction) of the frustum split for the specified index. Used for cascaded
+		 * shadow maps.
+		 * 
+		 * @param[in]	view			View whose frustum to split.
+		 * @param[in]	index			Index of the split. 0 = near plane.
+		 * @param[in]	numCascades		Maximum number of cascades in the cascaded shadow map. Must be greater than zero
+		 *								and greater or equal to @p index.
+		 * @return						Distance to the split position along the view direction.
+		 */
+		static float getCSMSplitDistance(const RendererView& view, UINT32 index, UINT32 numCascades);
+	};
+
+	/* @} */
+}}

+ 4 - 4
Source/RenderBeast/Source/BsLightGrid.cpp

@@ -4,7 +4,7 @@
 #include "BsGpuBuffer.h"
 #include "BsGpuParamsSet.h"
 #include "BsRendererUtility.h"
-#include "BsRendererCamera.h"
+#include "BsRendererView.h"
 #include "BsRenderTargets.h"
 #include "BsLightRendering.h"
 #include "BsImageBasedLighting.h"
@@ -108,7 +108,7 @@ namespace bs { namespace ct
 		mProbesBufferParam.set(probesBuffer);
 	}
 
-	void LightGridLLCreationMat::execute(const RendererCamera& view)
+	void LightGridLLCreationMat::execute(const RendererView& view)
 	{
 		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
 
@@ -214,7 +214,7 @@ namespace bs { namespace ct
 		mProbesLLParam.set(probeLL);
 	}
 
-	void LightGridLLReductionMat::execute(const RendererCamera& view)
+	void LightGridLLReductionMat::execute(const RendererView& view)
 	{
 		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
 
@@ -242,7 +242,7 @@ namespace bs { namespace ct
 		mGridParamBuffer = gLightGridParamDefDef.createBuffer();
 	}
 
-	void LightGrid::updateGrid(const RendererCamera& view, const GPULightData& lightData, const GPUReflProbeData& probeData,
+	void LightGrid::updateGrid(const RendererView& view, const GPULightData& lightData, const GPUReflProbeData& probeData,
 		bool noLighting)
 	{
 		UINT32 width = view.getRenderTargets()->getWidth();

+ 7 - 5
Source/RenderBeast/Source/BsPostProcessing.cpp

@@ -7,7 +7,7 @@
 #include "BsTextureManager.h"
 #include "BsCamera.h"
 #include "BsGpuParamsSet.h"
-#include "BsRendererCamera.h"
+#include "BsRendererView.h"
 
 namespace bs { namespace ct
 {
@@ -426,15 +426,17 @@ namespace bs { namespace ct
 	template class TonemappingMat<true, false>;
 	template class TonemappingMat<false, false>;
 
-	void PostProcessing::postProcess(RendererCamera* viewInfo, const SPtr<Texture>& sceneColor, float frameDelta)
+	void PostProcessing::postProcess(RendererView* viewInfo, const SPtr<Texture>& sceneColor, float frameDelta)
 	{
+		auto& viewProps = viewInfo->getProperties();
+
 		PostProcessInfo& ppInfo = viewInfo->getPPInfo();
 		const StandardPostProcessSettings& settings = *ppInfo.settings;
 
-		SPtr<RenderTarget> finalRT = viewInfo->getFinalTarget();
-		Rect2 viewportRect = viewInfo->getViewportRect();
+		SPtr<RenderTarget> finalRT = viewProps.target;
+		Rect2 viewportRect = viewProps.nrmViewRect;
 
-		bool hdr = viewInfo->isHDR();
+		bool hdr = viewProps.isHDR;
 
 		if(hdr && settings.enableAutoExposure)
 		{

File diff suppressed because it is too large
+ 91 - 551
Source/RenderBeast/Source/BsRenderBeast.cpp


+ 668 - 0
Source/RenderBeast/Source/BsRendererScene.cpp

@@ -0,0 +1,668 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsRendererScene.h"
+#include "BsCamera.h"
+#include "BsLight.h"
+#include "BsLightProbeCache.h"
+#include "BsReflectionProbe.h"
+#include "BsMesh.h"
+#include "BsRenderer.h"
+#include "BsPass.h"
+#include "BsGpuParamsSet.h"
+#include "BsRenderBeastOptions.h"
+
+namespace bs {	namespace ct
+{
+	RendererScene::RendererScene(const SPtr<RenderBeastOptions>& options)
+		:mOptions(options)
+	{
+		mDefaultMaterial = bs_new<DefaultMaterial>();
+	}
+
+	RendererScene::~RendererScene()
+	{
+		for (auto& entry : mInfo.renderables)
+			bs_delete(entry);
+
+		for (auto& entry : mInfo.views)
+			bs_delete(entry.second);
+
+		assert(mSamplerOverrides.empty());
+
+		bs_delete(mDefaultMaterial);
+	}
+
+	void RendererScene::registerCamera(const Camera* camera)
+	{
+		RendererView* view = updateCameraData(camera);
+		view->updatePerViewBuffer();
+	}
+
+	void RendererScene::updateCamera(const Camera* camera, UINT32 updateFlag)
+	{
+		RendererView* rendererCam;
+		if((updateFlag & (UINT32)CameraDirtyFlag::Everything) != 0)
+		{
+			rendererCam = updateCameraData(camera);
+		}
+		else if((updateFlag & (UINT32)CameraDirtyFlag::PostProcess) != 0)
+		{
+			rendererCam = mInfo.views[camera];
+
+			rendererCam->setPostProcessSettings(camera->getPostProcessSettings());
+		}
+		else // Transform
+		{
+			rendererCam = mInfo.views[camera];
+
+			rendererCam->setTransform(
+				camera->getPosition(),
+				camera->getForward(),
+				camera->getViewMatrix(),
+				camera->getProjectionMatrixRS(),
+				camera->getWorldFrustum());
+		}
+
+		rendererCam->updatePerViewBuffer();
+	}
+
+	void RendererScene::unregisterCamera(const Camera* camera)
+	{
+		updateCameraData(camera, true);
+	}
+
+	void RendererScene::registerLight(Light* light)
+	{
+		if (light->getType() == LightType::Directional)
+		{
+			UINT32 lightId = (UINT32)mInfo.directionalLights.size();
+			light->setRendererId(lightId);
+
+			mInfo.directionalLights.push_back(RendererLight(light));
+		}
+		else
+		{
+			if (light->getType() == LightType::Radial)
+			{
+				UINT32 lightId = (UINT32)mInfo.radialLights.size();
+				light->setRendererId(lightId);
+
+				mInfo.radialLights.push_back(RendererLight(light));
+				mInfo.radialLightWorldBounds.push_back(light->getBounds());
+			}
+			else // Spot
+			{
+				UINT32 lightId = (UINT32)mInfo.spotLights.size();
+				light->setRendererId(lightId);
+
+				mInfo.spotLights.push_back(RendererLight(light));
+				mInfo.spotLightWorldBounds.push_back(light->getBounds());
+			}
+		}
+	}
+
+	void RendererScene::updateLight(Light* light)
+	{
+		UINT32 lightId = light->getRendererId();
+
+		if (light->getType() == LightType::Radial)
+			mInfo.radialLightWorldBounds[lightId] = light->getBounds();
+		else if(light->getType() == LightType::Spot)
+			mInfo.spotLightWorldBounds[lightId] = light->getBounds();
+	}
+
+	void RendererScene::unregisterLight(Light* light)
+	{
+		UINT32 lightId = light->getRendererId();
+		if (light->getType() == LightType::Directional)
+		{
+			Light* lastLight = mInfo.directionalLights.back().getInternal();
+			UINT32 lastLightId = lastLight->getRendererId();
+
+			if (lightId != lastLightId)
+			{
+				// Swap current last element with the one we want to erase
+				std::swap(mInfo.directionalLights[lightId], mInfo.directionalLights[lastLightId]);
+				lastLight->setRendererId(lightId);
+			}
+
+			// Last element is the one we want to erase
+			mInfo.directionalLights.erase(mInfo.directionalLights.end() - 1);
+		}
+		else
+		{
+			if (light->getType() == LightType::Radial)
+			{
+				Light* lastLight = mInfo.radialLights.back().getInternal();
+				UINT32 lastLightId = lastLight->getRendererId();
+
+				if (lightId != lastLightId)
+				{
+					// Swap current last element with the one we want to erase
+					std::swap(mInfo.radialLights[lightId], mInfo.radialLights[lastLightId]);
+					std::swap(mInfo.radialLightWorldBounds[lightId], mInfo.radialLightWorldBounds[lastLightId]);
+
+					lastLight->setRendererId(lightId);
+				}
+
+				// Last element is the one we want to erase
+				mInfo.radialLights.erase(mInfo.radialLights.end() - 1);
+				mInfo.radialLightWorldBounds.erase(mInfo.radialLightWorldBounds.end() - 1);
+			}
+			else // Spot
+			{
+				Light* lastLight = mInfo.spotLights.back().getInternal();
+				UINT32 lastLightId = lastLight->getRendererId();
+
+				if (lightId != lastLightId)
+				{
+					// Swap current last element with the one we want to erase
+					std::swap(mInfo.spotLights[lightId], mInfo.spotLights[lastLightId]);
+					std::swap(mInfo.spotLightWorldBounds[lightId], mInfo.spotLightWorldBounds[lastLightId]);
+
+					lastLight->setRendererId(lightId);
+				}
+
+				// Last element is the one we want to erase
+				mInfo.spotLights.erase(mInfo.spotLights.end() - 1);
+				mInfo.spotLightWorldBounds.erase(mInfo.spotLightWorldBounds.end() - 1);
+			}
+		}
+	}
+
+	void RendererScene::registerRenderable(Renderable* renderable)
+	{
+		UINT32 renderableId = (UINT32)mInfo.renderables.size();
+
+		renderable->setRendererId(renderableId);
+
+		mInfo.renderables.push_back(bs_new<RendererObject>());
+		mInfo.renderableCullInfos.push_back(CullInfo(renderable->getBounds(), renderable->getLayer()));
+
+		RendererObject* rendererObject = mInfo.renderables.back();
+		rendererObject->renderable = renderable;
+		rendererObject->updatePerObjectBuffer();
+
+		SPtr<Mesh> mesh = renderable->getMesh();
+		if (mesh != nullptr)
+		{
+			const MeshProperties& meshProps = mesh->getProperties();
+			SPtr<VertexDeclaration> vertexDecl = mesh->getVertexData()->vertexDeclaration;
+
+			for (UINT32 i = 0; i < meshProps.getNumSubMeshes(); i++)
+			{
+				rendererObject->elements.push_back(BeastRenderableElement());
+				BeastRenderableElement& renElement = rendererObject->elements.back();
+
+				renElement.mesh = mesh;
+				renElement.subMesh = meshProps.getSubMesh(i);
+				renElement.renderableId = renderableId;
+				renElement.animType = renderable->getAnimType();
+				renElement.animationId = renderable->getAnimationId();
+				renElement.morphShapeVersion = 0;
+				renElement.morphShapeBuffer = renderable->getMorphShapeBuffer();
+				renElement.boneMatrixBuffer = renderable->getBoneMatrixBuffer();
+				renElement.morphVertexDeclaration = renderable->getMorphVertexDeclaration();
+
+				renElement.material = renderable->getMaterial(i);
+				if (renElement.material == nullptr)
+					renElement.material = renderable->getMaterial(0);
+
+				if (renElement.material != nullptr && renElement.material->getShader() == nullptr)
+					renElement.material = nullptr;
+
+				// If no mInfo.aterial use the default mInfo.aterial
+				if (renElement.material == nullptr)
+					renElement.material = mDefaultMaterial->getMaterial();
+
+				// Determine which technique to use
+				static StringID techniqueIDLookup[4] = { StringID::NONE, RTag_Skinned, RTag_Morph, RTag_SkinnedMorph };
+				static_assert((UINT32)RenderableAnimType::Count == 4, "RenderableAnimType is expected to have four sequential entries.");
+
+				UINT32 techniqueIdx = -1;
+				RenderableAnimType animType = renderable->getAnimType();
+				if (animType != RenderableAnimType::None)
+					techniqueIdx = renElement.material->findTechnique(techniqueIDLookup[(int)animType]);
+
+				if (techniqueIdx == (UINT32)-1)
+					techniqueIdx = renElement.material->getDefaultTechnique();
+
+				renElement.techniqueIdx = techniqueIdx;
+
+				// Validate mesh <-> shader vertex bindings
+				if (renElement.material != nullptr)
+				{
+					UINT32 numPasses = renElement.material->getNumPasses(techniqueIdx);
+					for (UINT32 j = 0; j < numPasses; j++)
+					{
+						SPtr<Pass> pass = renElement.material->getPass(j, techniqueIdx);
+
+						SPtr<VertexDeclaration> shaderDecl = pass->getVertexProgram()->getInputDeclaration();
+						if (!vertexDecl->isCompatible(shaderDecl))
+						{
+							Vector<VertexElement> missingElements = vertexDecl->getMissingElements(shaderDecl);
+
+							// If using mInfo.orph shapes ignore POSITION1 and NORMAL1 mInfo.issing since we assign them from within the renderer
+							if (animType == RenderableAnimType::Morph || animType == RenderableAnimType::SkinnedMorph)
+							{
+								auto removeIter = std::remove_if(missingElements.begin(), missingElements.end(), [](const VertexElement& x)
+								{
+									return (x.getSemantic() == VES_POSITION && x.getSemanticIdx() == 1) ||
+										(x.getSemantic() == VES_NORMAL && x.getSemanticIdx() == 1);
+								});
+
+								missingElements.erase(removeIter, missingElements.end());
+							}
+
+							if (!missingElements.empty())
+							{
+								StringStream wrnStream;
+								wrnStream << "Provided mesh is mInfo.issing required vertex attributes to render with the provided shader. Missing elements: " << std::endl;
+
+								for (auto& entry : missingElements)
+									wrnStream << "\t" << toString(entry.getSemantic()) << entry.getSemanticIdx() << std::endl;
+
+								LOGWRN(wrnStream.str());
+								break;
+							}
+						}
+					}
+				}
+
+				// Generate or assigned renderer specific data for the mInfo.aterial
+				renElement.params = renElement.material->createParamsSet(techniqueIdx);
+				renElement.material->updateParamsSet(renElement.params, true);
+
+				// Generate or assign sampler state overrides
+				SamplerOverrideKey samplerKey(renElement.material, techniqueIdx);
+				auto iterFind = mSamplerOverrides.find(samplerKey);
+				if (iterFind != mSamplerOverrides.end())
+				{
+					renElement.samplerOverrides = iterFind->second;
+					iterFind->second->refCount++;
+				}
+				else
+				{
+					SPtr<Shader> shader = renElement.material->getShader();
+					MaterialSamplerOverrides* samplerOverrides = SamplerOverrideUtility::generateSamplerOverrides(shader,
+						renElement.material->_getInternalParams(), renElement.params, mOptions);
+
+					mSamplerOverrides[samplerKey] = samplerOverrides;
+
+					renElement.samplerOverrides = samplerOverrides;
+					samplerOverrides->refCount++;
+				}
+			}
+		}
+	}
+
+	void RendererScene::updateRenderable(Renderable* renderable)
+	{
+		UINT32 renderableId = renderable->getRendererId();
+
+		mInfo.renderables[renderableId]->updatePerObjectBuffer();
+		mInfo.renderableCullInfos[renderableId].bounds = renderable->getBounds();
+	}
+
+	void RendererScene::unregisterRenderable(Renderable* renderable)
+	{
+		UINT32 renderableId = renderable->getRendererId();
+		Renderable* lastRenerable = mInfo.renderables.back()->renderable;
+		UINT32 lastRenderableId = lastRenerable->getRendererId();
+
+		RendererObject* rendererObject = mInfo.renderables[renderableId];
+		Vector<BeastRenderableElement>& elements = rendererObject->elements;
+		for (auto& element : elements)
+		{
+			SamplerOverrideKey samplerKey(element.material, element.techniqueIdx);
+
+			auto iterFind = mSamplerOverrides.find(samplerKey);
+			assert(iterFind != mSamplerOverrides.end());
+
+			MaterialSamplerOverrides* samplerOverrides = iterFind->second;
+			samplerOverrides->refCount--;
+			if (samplerOverrides->refCount == 0)
+			{
+				SamplerOverrideUtility::destroySamplerOverrides(samplerOverrides);
+				mSamplerOverrides.erase(iterFind);
+			}
+
+			element.samplerOverrides = nullptr;
+		}
+
+		if (renderableId != lastRenderableId)
+		{
+			// Swap current last element with the one we want to erase
+			std::swap(mInfo.renderables[renderableId], mInfo.renderables[lastRenderableId]);
+			std::swap(mInfo.renderableCullInfos[renderableId], mInfo.renderableCullInfos[lastRenderableId]);
+
+			lastRenerable->setRendererId(renderableId);
+
+			for (auto& element : elements)
+				element.renderableId = renderableId;
+		}
+
+		// Last element is the one we want to erase
+		mInfo.renderables.erase(mInfo.renderables.end() - 1);
+		mInfo.renderableCullInfos.erase(mInfo.renderableCullInfos.end() - 1);
+
+		bs_delete(rendererObject);
+	}
+
+	void RendererScene::registerReflectionProbe(ReflectionProbe* probe)
+	{
+		UINT32 probeId = (UINT32)mInfo.reflProbes.size();
+		probe->setRendererId(probeId);
+
+		mInfo.reflProbes.push_back(RendererReflectionProbe(probe));
+		RendererReflectionProbe& probeInfo = mInfo.reflProbes.back();
+
+		mInfo.reflProbeWorldBounds.push_back(probe->getBounds());
+	}
+
+	void RendererScene::updateReflectionProbe(ReflectionProbe* probe)
+	{
+		// Should only get called if transform changes, any other mInfo.ajor changes and ReflProbeInfo entry gets rebuild
+		UINT32 probeId = probe->getRendererId();
+		mInfo.reflProbeWorldBounds[probeId] = probe->getBounds();
+
+		RendererReflectionProbe& probeInfo = mInfo.reflProbes[probeId];
+		probeInfo.arrayDirty = true;
+
+		LightProbeCache::instance().notifyDirty(probe->getUUID());
+		probeInfo.textureDirty = true;
+	}
+
+	void RendererScene::unregisterReflectionProbe(ReflectionProbe* probe)
+	{
+		UINT32 probeId = probe->getRendererId();
+
+		ReflectionProbe* lastProbe = mInfo.reflProbes.back().probe;
+		UINT32 lastProbeId = lastProbe->getRendererId();
+
+		if (probeId != lastProbeId)
+		{
+			// Swap current last element with the one we want to erase
+			std::swap(mInfo.reflProbes[probeId], mInfo.reflProbes[lastProbeId]);
+			std::swap(mInfo.reflProbeWorldBounds[probeId], mInfo.reflProbeWorldBounds[lastProbeId]);
+
+			probe->setRendererId(probeId);
+		}
+
+		// Last element is the one we want to erase
+		mInfo.radialLights.erase(mInfo.radialLights.end() - 1);
+		mInfo.radialLightWorldBounds.erase(mInfo.radialLightWorldBounds.end() - 1);
+
+		LightProbeCache::instance().unloadCachedTexture(probe->getUUID());
+	}
+
+	void RendererScene::setReflectionProbeTexture(UINT32 probeIdx, const SPtr<Texture>& texture)
+	{
+		RendererReflectionProbe* probe = &mInfo.reflProbes[probeIdx];
+		probe->texture = texture;
+		probe->textureDirty = false;
+	}
+
+	void RendererScene::setReflectionProbeArrayIndex(UINT32 probeIdx, UINT32 arrayIdx, bool markAsClean)
+	{
+		RendererReflectionProbe* probe = &mInfo.reflProbes[probeIdx];
+		probe->arrayIdx = arrayIdx;
+
+		if (markAsClean)
+			probe->arrayDirty = false;
+	}
+
+	void RendererScene::setOptions(const SPtr<RenderBeastOptions>& options)
+	{
+		mOptions = options;
+
+		for (auto& entry : mInfo.views)
+		{
+			RendererView* rendererCam = entry.second;
+			rendererCam->setStateReductionMode(mOptions->stateReductionMode);
+		}
+	}
+
+	RendererView* RendererScene::updateCameraData(const Camera* camera, bool forceRemove)
+	{
+		RendererView* output;
+
+		SPtr<RenderTarget> renderTarget = camera->getViewport()->getTarget();
+
+		auto iterFind = mInfo.views.find(camera);
+		if(forceRemove)
+		{
+			if(iterFind != mInfo.views.end())
+			{
+				bs_delete(iterFind->second);
+				mInfo.views.erase(iterFind);
+			}
+
+			renderTarget = nullptr;
+			output = nullptr;
+		}
+		else
+		{
+			SPtr<Viewport> viewport = camera->getViewport();
+			RENDERER_VIEW_DESC viewDesc;
+
+			viewDesc.target.clearFlags = 0;
+			if (viewport->getRequiresColorClear())
+				viewDesc.target.clearFlags |= FBT_COLOR;
+
+			if (viewport->getRequiresDepthClear())
+				viewDesc.target.clearFlags |= FBT_DEPTH;
+
+			if (viewport->getRequiresStencilClear())
+				viewDesc.target.clearFlags |= FBT_STENCIL;
+
+			viewDesc.target.clearColor = viewport->getClearColor();
+			viewDesc.target.clearDepthValue = viewport->getClearDepthValue();
+			viewDesc.target.clearStencilValue = viewport->getClearStencilValue();
+
+			viewDesc.target.target = viewport->getTarget();
+			viewDesc.target.nrmViewRect = viewport->getNormArea();
+			viewDesc.target.viewRect = Rect2I(
+				viewport->getX(),
+				viewport->getY(),
+				(UINT32)viewport->getWidth(),
+				(UINT32)viewport->getHeight());
+
+			if (viewDesc.target.target != nullptr)
+			{
+				viewDesc.target.targetWidth = viewDesc.target.target->getProperties().getWidth();
+				viewDesc.target.targetHeight = viewDesc.target.target->getProperties().getHeight();
+			}
+			else
+			{
+				viewDesc.target.targetWidth = 0;
+				viewDesc.target.targetHeight = 0;
+			}
+
+			viewDesc.target.numSamples = camera->getMSAACount();
+
+			viewDesc.isOverlay = camera->getFlags().isSet(CameraFlag::Overlay);
+			viewDesc.isHDR = camera->getFlags().isSet(CameraFlag::HDR);
+			viewDesc.noLighting = camera->getFlags().isSet(CameraFlag::NoLighting);
+			viewDesc.triggerCallbacks = true;
+			viewDesc.runPostProcessing = true;
+			viewDesc.renderingReflections = false;
+
+			viewDesc.cullFrustum = camera->getWorldFrustum();
+			viewDesc.visibleLayers = camera->getLayers();
+			viewDesc.nearPlane = camera->getNearClipDistance();
+			viewDesc.farPlane = camera->getFarClipDistance();
+			viewDesc.flipView = false;
+
+			viewDesc.viewOrigin = camera->getPosition();
+			viewDesc.viewDirection = camera->getForward();
+			viewDesc.projTransform = camera->getProjectionMatrixRS();
+			viewDesc.viewTransform = camera->getViewMatrix();
+			viewDesc.projType = camera->getProjectionType();
+
+			viewDesc.stateReduction = mOptions->stateReductionMode;
+			viewDesc.sceneCamera = camera;
+
+			if (iterFind != mInfo.views.end())
+			{
+				output = iterFind->second;
+				output->setView(viewDesc);
+			}
+			else
+			{
+				output = bs_new<RendererView>(viewDesc);
+				mInfo.views[camera] = output;
+			}
+
+			output->setPostProcessSettings(camera->getPostProcessSettings());
+		}
+
+		// Remove from render target list
+		int rtChanged = 0; // 0 - No RT, 1 - RT found, 2 - RT changed
+		for (auto iterTarget = mInfo.renderTargets.begin(); iterTarget != mInfo.renderTargets.end(); ++iterTarget)
+		{
+			RendererRenderTarget& target = *iterTarget;
+			for (auto iterCam = target.cameras.begin(); iterCam != target.cameras.end(); ++iterCam)
+			{
+				if (camera == *iterCam)
+				{
+					if (renderTarget != target.target)
+					{
+						target.cameras.erase(iterCam);
+						rtChanged = 2;
+
+					}
+					else
+						rtChanged = 1;
+
+					break;
+				}
+			}
+
+			if (target.cameras.empty())
+			{
+				mInfo.renderTargets.erase(iterTarget);
+				break;
+			}
+		}
+
+		// Register in render target list
+		if (renderTarget != nullptr && (rtChanged == 0 || rtChanged == 2))
+		{
+			auto findIter = std::find_if(mInfo.renderTargets.begin(), mInfo.renderTargets.end(),
+				[&](const RendererRenderTarget& x) { return x.target == renderTarget; });
+
+			if (findIter != mInfo.renderTargets.end())
+			{
+				findIter->cameras.push_back(camera);
+			}
+			else
+			{
+				mInfo.renderTargets.push_back(RendererRenderTarget());
+				RendererRenderTarget& renderTargetData = mInfo.renderTargets.back();
+
+				renderTargetData.target = renderTarget;
+				renderTargetData.cameras.push_back(camera);
+			}
+
+			// Sort render targets based on priority
+			auto cameraComparer = [&](const Camera* a, const Camera* b) { return a->getPriority() > b->getPriority(); };
+			auto renderTargetInfoComparer = [&](const RendererRenderTarget& a, const RendererRenderTarget& b)
+			{ return a.target->getProperties().getPriority() > b.target->getProperties().getPriority(); };
+			std::sort(begin(mInfo.renderTargets), end(mInfo.renderTargets), renderTargetInfoComparer);
+
+			for (auto& camerasPerTarget : mInfo.renderTargets)
+			{
+				Vector<const Camera*>& cameras = camerasPerTarget.cameras;
+
+				std::sort(begin(cameras), end(cameras), cameraComparer);
+			}
+		}
+
+		return output;
+	}
+
+	void RendererScene::refreshSamplerOverrides(bool force)
+	{
+		bool anyDirty = false;
+		for (auto& entry : mSamplerOverrides)
+		{
+			SPtr<MaterialParams> materialParams = entry.first.material->_getInternalParams();
+
+			MaterialSamplerOverrides* materialOverrides = entry.second;
+			for(UINT32 i = 0; i < materialOverrides->numOverrides; i++)
+			{
+				SamplerOverride& override = materialOverrides->overrides[i];
+				const MaterialParamsBase::ParamData* materialParamData = materialParams->getParamData(override.paramIdx);
+
+				SPtr<SamplerState> samplerState;
+				materialParams->getSamplerState(*materialParamData, samplerState);
+
+				UINT64 hash = 0;
+				if (samplerState != nullptr)
+					hash = samplerState->getProperties().getHash();
+
+				if (hash != override.originalStateHash || force)
+				{
+					if (samplerState != nullptr)
+						override.state = SamplerOverrideUtility::generateSamplerOverride(samplerState, mOptions);
+					else
+						override.state = SamplerOverrideUtility::generateSamplerOverride(SamplerState::getDefault(), mOptions);
+
+					override.originalStateHash = override.state->getProperties().getHash();
+					materialOverrides->isDirty = true;
+				}
+
+				// Dirty flag can also be set externally, so check here even though we assign it above
+				if (materialOverrides->isDirty)
+					anyDirty = true;
+			}
+		}
+
+		// Early exit if possible
+		if (!anyDirty)
+			return;
+
+		UINT32 numRenderables = (UINT32)mInfo.renderables.size();
+		for (UINT32 i = 0; i < numRenderables; i++)
+		{
+			for(auto& element : mInfo.renderables[i]->elements)
+			{
+				MaterialSamplerOverrides* overrides = element.samplerOverrides;
+				if(overrides != nullptr && overrides->isDirty)
+				{
+					UINT32 numPasses = element.material->getNumPasses();
+					for(UINT32 j = 0; j < numPasses; j++)
+					{
+						SPtr<GpuParams> params = element.params->getGpuParams(j);
+
+						const UINT32 numStages = 6;
+						for (UINT32 k = 0; k < numStages; k++)
+						{
+							GpuProgramType type = (GpuProgramType)k;
+
+							SPtr<GpuParamDesc> paramDesc = params->getParamDesc(type);
+							if (paramDesc == nullptr)
+								continue;
+
+							for (auto& samplerDesc : paramDesc->samplers)
+							{
+								UINT32 set = samplerDesc.second.set;
+								UINT32 slot = samplerDesc.second.slot;
+
+								UINT32 overrideIndex = overrides->passes[j].stateOverrides[set][slot];
+								if (overrideIndex == (UINT32)-1)
+									continue;
+
+								params->setSamplerState(set, slot, overrides->overrides[overrideIndex].state);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		for (auto& entry : mSamplerOverrides)
+			entry.second->isDirty = false;
+	}
+}}

+ 66 - 48
Source/RenderBeast/Source/BsRendererCamera.cpp → Source/RenderBeast/Source/BsRendererView.cpp

@@ -1,6 +1,6 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsRendererCamera.h"
+#include "BsRendererView.h"
 #include "BsCamera.h"
 #include "BsRenderable.h"
 #include "BsMaterial.h"
@@ -54,21 +54,36 @@ namespace bs { namespace ct
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 
-	RendererCamera::RendererCamera()
+	RendererViewProperties::RendererViewProperties(const RENDERER_VIEW_DESC& src)
+		:RendererViewData(src)
+	{
+		viewProjTransform = src.projTransform * src.viewTransform;
+
+		target = src.target.target;
+		nrmViewRect = src.target.nrmViewRect;
+		numSamples = src.target.numSamples;
+
+		clearFlags = src.target.clearFlags;
+		clearColor = src.target.clearColor;
+		clearDepthValue = src.target.clearDepthValue;
+		clearStencilValue = src.target.clearStencilValue;
+	}
+
+	RendererView::RendererView()
 		: mUsingGBuffer(false)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 	}
 
-	RendererCamera::RendererCamera(const RENDERER_VIEW_DESC& desc)
-		: mViewDesc(desc), mUsingGBuffer(false)
+	RendererView::RendererView(const RENDERER_VIEW_DESC& desc)
+		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera), mUsingGBuffer(false)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 
 		setStateReductionMode(desc.stateReduction);
 	}
 
-	void RendererCamera::setStateReductionMode(StateReduction reductionMode)
+	void RendererView::setStateReductionMode(StateReduction reductionMode)
 	{
 		mOpaqueQueue = bs_shared_ptr_new<RenderQueue>(reductionMode);
 
@@ -79,7 +94,7 @@ namespace bs { namespace ct
 		mTransparentQueue = bs_shared_ptr_new<RenderQueue>(transparentStateReduction);
 	}
 
-	void RendererCamera::setPostProcessSettings(const SPtr<PostProcessSettings>& ppSettings)
+	void RendererView::setPostProcessSettings(const SPtr<PostProcessSettings>& ppSettings)
 	{
 		if (mPostProcessInfo.settings == nullptr)
 			mPostProcessInfo.settings = bs_shared_ptr_new<StandardPostProcessSettings>();
@@ -93,45 +108,48 @@ namespace bs { namespace ct
 		mPostProcessInfo.settingDirty = true;
 	}
 
-	void RendererCamera::setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view, 
+	void RendererView::setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view, 
 									  const Matrix4& proj, const ConvexVolume& worldFrustum)
 	{
-		mViewDesc.viewOrigin = origin;
-		mViewDesc.viewDirection = direction;
-		mViewDesc.viewTransform = view;
-		mViewDesc.projTransform = proj;
-		mViewDesc.cullFrustum = worldFrustum;
+		mProperties.viewOrigin = origin;
+		mProperties.viewDirection = direction;
+		mProperties.viewTransform = view;
+		mProperties.projTransform = proj;
+		mProperties.cullFrustum = worldFrustum;
+		mProperties.viewProjTransform = proj * view;
 	}
 
-	void RendererCamera::setView(const RENDERER_VIEW_DESC& desc)
+	void RendererView::setView(const RENDERER_VIEW_DESC& desc)
 	{
-		if (mViewDesc.target.targetWidth != desc.target.targetWidth ||
-			mViewDesc.target.targetHeight != desc.target.targetHeight)
+		if (mTargetDesc.targetWidth != desc.target.targetWidth ||
+			mTargetDesc.targetHeight != desc.target.targetHeight)
 			mRenderTargets = nullptr;
 
-		mViewDesc = desc;
+		mCamera = desc.sceneCamera;
+		mProperties = desc;
+		mTargetDesc = desc.target;
 
 		setStateReductionMode(desc.stateReduction);
 	}
 
-	void RendererCamera::beginRendering(bool useGBuffer)
+	void RendererView::beginRendering(bool useGBuffer)
 	{
 		if (useGBuffer)
 		{
 			// Render scene objects to g-buffer
 			bool createGBuffer = mRenderTargets == nullptr ||
-				mRenderTargets->getHDR() != mViewDesc.isHDR ||
-				mRenderTargets->getNumSamples() != mViewDesc.target.numSamples;
+				mRenderTargets->getHDR() != mProperties.isHDR ||
+				mRenderTargets->getNumSamples() != mTargetDesc.numSamples;
 
 			if (createGBuffer)
-				mRenderTargets = RenderTargets::create(mViewDesc.target, mViewDesc.isHDR);
+				mRenderTargets = RenderTargets::create(mTargetDesc, mProperties.isHDR);
 
 			mRenderTargets->prepare();
 			mUsingGBuffer = true;
 		}
 	}
 
-	void RendererCamera::endRendering()
+	void RendererView::endRendering()
 	{
 		mOpaqueQueue->clear();
 		mTransparentQueue->clear();
@@ -143,13 +161,13 @@ namespace bs { namespace ct
 		}
 	}
 
-	void RendererCamera::determineVisible(const Vector<RendererObject*>& renderables, const Vector<CullInfo>& cullInfos,
+	void RendererView::determineVisible(const Vector<RendererObject*>& renderables, const Vector<CullInfo>& cullInfos,
 		Vector<bool>* visibility)
 	{
 		mVisibility.renderables.clear();
 		mVisibility.renderables.resize(renderables.size(), false);
 
-		if (mViewDesc.isOverlay)
+		if (mProperties.isOverlay)
 			return;
 
 		calculateVisibility(cullInfos, mVisibility.renderables);
@@ -161,7 +179,7 @@ namespace bs { namespace ct
 				continue;
 
 			const AABox& boundingBox = cullInfos[i].bounds.getBox();
-			float distanceToCamera = (mViewDesc.viewOrigin - boundingBox.getCenter()).length();
+			float distanceToCamera = (mProperties.viewOrigin - boundingBox.getCenter()).length();
 
 			for (auto& renderElem : renderables[i]->elements)
 			{
@@ -190,10 +208,10 @@ namespace bs { namespace ct
 		mTransparentQueue->sort();
 	}
 
-	void RendererCamera::calculateVisibility(const Vector<CullInfo>& cullInfos, Vector<bool>& visibility) const
+	void RendererView::calculateVisibility(const Vector<CullInfo>& cullInfos, Vector<bool>& visibility) const
 	{
-		UINT64 cameraLayers = mViewDesc.visibleLayers;
-		const ConvexVolume& worldFrustum = mViewDesc.cullFrustum;
+		UINT64 cameraLayers = mProperties.visibleLayers;
+		const ConvexVolume& worldFrustum = mProperties.cullFrustum;
 
 		for (UINT32 i = 0; i < (UINT32)cullInfos.size(); i++)
 		{
@@ -215,9 +233,9 @@ namespace bs { namespace ct
 		}
 	}
 
-	void RendererCamera::calculateVisibility(const Vector<Sphere>& bounds, Vector<bool>& visibility) const
+	void RendererView::calculateVisibility(const Vector<Sphere>& bounds, Vector<bool>& visibility) const
 	{
-		const ConvexVolume& worldFrustum = mViewDesc.cullFrustum;
+		const ConvexVolume& worldFrustum = mProperties.cullFrustum;
 
 		for (UINT32 i = 0; i < (UINT32)bounds.size(); i++)
 		{
@@ -226,7 +244,7 @@ namespace bs { namespace ct
 		}
 	}
 
-	Vector2 RendererCamera::getDeviceZTransform(const Matrix4& projMatrix) const
+	Vector2 RendererView::getDeviceZTransform(const Matrix4& projMatrix) const
 	{
 		// Returns a set of values that will transform depth buffer values (in range [0, 1]) to a distance
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
@@ -273,7 +291,7 @@ namespace bs { namespace ct
 		return output;
 	}
 
-	Vector2 RendererCamera::getNDCZTransform(const Matrix4& projMatrix) const
+	Vector2 RendererView::getNDCZTransform(const Matrix4& projMatrix) const
 	{
 		// Returns a set of values that will transform depth buffer values (e.g. [0, 1] in DX, [-1, 1] in GL) to a distance
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
@@ -312,16 +330,16 @@ namespace bs { namespace ct
 		return output;
 	}
 
-	void RendererCamera::updatePerViewBuffer()
+	void RendererView::updatePerViewBuffer()
 	{
-		Matrix4 viewProj = mViewDesc.projTransform * mViewDesc.viewTransform;
+		Matrix4 viewProj = mProperties.projTransform * mProperties.viewTransform;
 		Matrix4 invViewProj = viewProj.inverse();
 
-		gPerCameraParamDef.gMatProj.set(mParamBuffer, mViewDesc.projTransform);
-		gPerCameraParamDef.gMatView.set(mParamBuffer, mViewDesc.viewTransform);
+		gPerCameraParamDef.gMatProj.set(mParamBuffer, mProperties.projTransform);
+		gPerCameraParamDef.gMatView.set(mParamBuffer, mProperties.viewTransform);
 		gPerCameraParamDef.gMatViewProj.set(mParamBuffer, viewProj);
 		gPerCameraParamDef.gMatInvViewProj.set(mParamBuffer, invViewProj); // Note: Calculate inverses separately (better precision possibly)
-		gPerCameraParamDef.gMatInvProj.set(mParamBuffer, mViewDesc.projTransform.inverse());
+		gPerCameraParamDef.gMatInvProj.set(mParamBuffer, mProperties.projTransform.inverse());
 
 		// Construct a special inverse view-projection matrix that had projection entries that affect z and w eliminated.
 		// Used to transform a vector(clip_x, clip_y, view_z, view_w), where clip_x/clip_y are in clip space, and 
@@ -329,21 +347,21 @@ namespace bs { namespace ct
 
 		// Only projects z/w coordinates
 		Matrix4 projZ = Matrix4::IDENTITY;
-		projZ[2][2] = mViewDesc.projTransform[2][2];
-		projZ[2][3] = mViewDesc.projTransform[2][3];
-		projZ[3][2] = mViewDesc.projTransform[3][2];
+		projZ[2][2] = mProperties.projTransform[2][2];
+		projZ[2][3] = mProperties.projTransform[2][3];
+		projZ[3][2] = mProperties.projTransform[3][2];
 		projZ[3][3] = 0.0f;
 		
 		gPerCameraParamDef.gMatScreenToWorld.set(mParamBuffer, invViewProj * projZ);
-		gPerCameraParamDef.gViewDir.set(mParamBuffer, mViewDesc.viewDirection);
-		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mViewDesc.viewOrigin);
-		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZTransform(mViewDesc.projTransform));
-		gPerCameraParamDef.gNDCZToWorldZ.set(mParamBuffer, getNDCZTransform(mViewDesc.projTransform));
+		gPerCameraParamDef.gViewDir.set(mParamBuffer, mProperties.viewDirection);
+		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mProperties.viewOrigin);
+		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZTransform(mProperties.projTransform));
+		gPerCameraParamDef.gNDCZToWorldZ.set(mParamBuffer, getNDCZTransform(mProperties.projTransform));
 
-		Vector2 nearFar(mViewDesc.nearPlane, mViewDesc.farPlane);
+		Vector2 nearFar(mProperties.nearPlane, mProperties.farPlane);
 		gPerCameraParamDef.gNearFar.set(mParamBuffer, nearFar);
 
-		const Rect2I& viewRect = mViewDesc.target.viewRect;
+		const Rect2I& viewRect = mTargetDesc.viewRect;
 
 		Vector4I viewportRect;
 		viewportRect[0] = viewRect.x;
@@ -356,8 +374,8 @@ namespace bs { namespace ct
 		float halfWidth = viewRect.width * 0.5f;
 		float halfHeight = viewRect.height * 0.5f;
 
-		float rtWidth = mViewDesc.target.targetWidth != 0 ? (float)mViewDesc.target.targetWidth : 20.0f;
-		float rtHeight = mViewDesc.target.targetHeight != 0 ? (float)mViewDesc.target.targetHeight : 20.0f;
+		float rtWidth = mTargetDesc.targetWidth != 0 ? (float)mTargetDesc.targetWidth : 20.0f;
+		float rtHeight = mTargetDesc.targetHeight != 0 ? (float)mTargetDesc.targetHeight : 20.0f;
 
 		RenderAPI& rapi = RenderAPI::instance();
 		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
@@ -374,7 +392,7 @@ namespace bs { namespace ct
 
 		gPerCameraParamDef.gClipToUVScaleOffset.set(mParamBuffer, clipToUVScaleOffset);
 
-		if (mViewDesc.noLighting)
+		if (mProperties.noLighting)
 			gPerCameraParamDef.gAmbientFactor.set(mParamBuffer, 100.0f);
 		else
 			gPerCameraParamDef.gAmbientFactor.set(mParamBuffer, 0.0f);

+ 183 - 0
Source/RenderBeast/Source/BsShadowRendering.cpp

@@ -0,0 +1,183 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsShadowRendering.h"
+#include "BsRendererView.h"
+
+namespace bs { namespace ct
+{
+	ConvexVolume ShadowRendering::getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade, 
+		UINT32 numCascades, Sphere& outBounds)
+	{
+		// Determine split range
+		float splitNear = getCSMSplitDistance(view, cascade, numCascades);
+		float splitFar = getCSMSplitDistance(view, cascade + 1, numCascades);
+
+		// Calculate the eight vertices of the split frustum
+		auto& viewProps = view.getProperties();
+
+		const Matrix4& projMat = viewProps.projTransform;
+
+		float aspect;
+		float nearHalfWidth, nearHalfHeight;
+		float farHalfWidth, farHalfHeight;
+		if(viewProps.projType == PT_PERSPECTIVE)
+		{
+			aspect = projMat[0][0] / projMat[1][1];
+			float tanHalfFOV = 1.0f / projMat[0][0];
+
+			nearHalfWidth = splitNear * tanHalfFOV;
+			nearHalfHeight = nearHalfWidth * aspect;
+
+			farHalfWidth = splitFar * tanHalfFOV;
+			farHalfHeight = farHalfWidth * aspect;
+		}
+		else
+		{
+			aspect = projMat[0][0] / projMat[1][1];
+
+			nearHalfWidth = farHalfWidth = projMat[0][0] / 4.0f;
+			nearHalfHeight = farHalfHeight = projMat[1][1] / 4.0f;
+		}
+
+		const Matrix4& viewMat = viewProps.viewTransform;
+		Vector3 cameraRight = Vector3(viewMat[0]);
+		Vector3 cameraUp = Vector3(viewMat[1]);
+
+		const Vector3& viewOrigin = viewProps.viewOrigin;
+		const Vector3& viewDir = viewProps.viewDirection;
+
+		Vector3 frustumVerts[] =
+		{
+			viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, left, top
+			viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, right, top
+			viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, right, bottom
+			viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, left, bottom
+			viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, left, top
+			viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, right, top
+			viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, right, bottom
+			viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, left, bottom
+		};
+
+		// Calculate the bounding sphere of the frustum
+		float diagonalNearSq = nearHalfWidth * nearHalfWidth + nearHalfHeight * nearHalfHeight;
+		float diagonalFarSq = farHalfWidth * farHalfWidth + farHalfHeight * farHalfHeight;
+
+		float length = splitFar - splitNear;
+		float offset = (diagonalNearSq - diagonalFarSq) / 2 * length + length * 0.5f;
+		float distToCenter = Math::clamp(splitFar - offset, splitNear, splitFar);
+
+		Vector3 center = viewOrigin + viewDir * distToCenter;
+
+		float radius = 0.0f;
+		for (auto& entry : frustumVerts)
+			radius = std::max(radius, center.squaredDistance(entry));
+
+		radius = std::max(sqrt(radius), 1.0f);
+		outBounds = Sphere(center, radius);
+
+		// Generate light frustum planes
+		Plane viewPlanes[6];
+		viewPlanes[FRUSTUM_PLANE_NEAR] = Plane(frustumVerts[0], frustumVerts[1], frustumVerts[2]);
+		viewPlanes[FRUSTUM_PLANE_FAR] = Plane(frustumVerts[5], frustumVerts[4], frustumVerts[7]);
+		viewPlanes[FRUSTUM_PLANE_LEFT] = Plane(frustumVerts[4], frustumVerts[0], frustumVerts[3]);
+		viewPlanes[FRUSTUM_PLANE_RIGHT] = Plane(frustumVerts[1], frustumVerts[5], frustumVerts[6]);
+		viewPlanes[FRUSTUM_PLANE_TOP] = Plane(frustumVerts[4], frustumVerts[5], frustumVerts[1]);
+		viewPlanes[FRUSTUM_PLANE_BOTTOM] = Plane(frustumVerts[3], frustumVerts[2], frustumVerts[6]);
+
+		Vector<Plane> lightVolume;
+
+		//// Add camera's planes facing towards the lights (forming the back of the volume)
+		for(auto& entry : viewPlanes)
+		{
+			if (entry.normal.dot(lightDir) < 0.0f)
+				lightVolume.push_back(entry);
+		}
+
+		//// Determine edge planes by testing adjacent planes with different facing
+		////// Pairs of frustum planes that share an edge
+		UINT32 adjacentPlanes[][2] =
+		{
+			{ FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_LEFT },
+			{ FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_RIGHT },
+			{ FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_TOP },
+			{ FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_BOTTOM },
+
+			{ FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_LEFT },
+			{ FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_RIGHT },
+			{ FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_TOP },
+			{ FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_BOTTOM },
+
+			{ FRUSTUM_PLANE_LEFT, FRUSTUM_PLANE_TOP },
+			{ FRUSTUM_PLANE_TOP, FRUSTUM_PLANE_RIGHT },
+			{ FRUSTUM_PLANE_RIGHT, FRUSTUM_PLANE_BOTTOM },
+			{ FRUSTUM_PLANE_BOTTOM, FRUSTUM_PLANE_LEFT },
+		};
+
+		////// Vertex indices of edges on the boundary between two planes
+		UINT32 sharedEdges[][2] =
+		{
+			{ 3, 0 },{ 1, 2 },{ 0, 1 },{ 2, 3 },
+			{ 4, 7 },{ 6, 5 },{ 5, 4 },{ 7, 6 },
+			{ 4, 0 },{ 5, 1 },{ 6, 2 },{ 7, 3 }
+		};
+
+		for(UINT32 i = 0; i < 12; i++)
+		{
+			const Plane& planeA = viewPlanes[adjacentPlanes[i][0]];
+			const Plane& planeB = viewPlanes[adjacentPlanes[i][1]];
+
+			float dotA = planeA.normal.dot(lightDir);
+			float dotB = planeB.normal.dot(lightDir);
+
+			if((dotA * dotB) < 0.0f)
+			{
+				const Vector3& vertA = frustumVerts[sharedEdges[i][0]];
+				const Vector3& vertB = frustumVerts[sharedEdges[i][1]];
+				Vector3 vertC = vertA + lightDir;
+
+				if (dotA >= 0.0f)
+					lightVolume.push_back(Plane(vertA, vertB, vertC));
+				else
+					lightVolume.push_back(Plane(vertB, vertA, vertC));
+			}
+		}
+
+		return ConvexVolume(lightVolume);
+	}
+
+	float ShadowRendering::getCSMSplitDistance(const RendererView& view, UINT32 index, UINT32 numCascades)
+	{
+		// Determines the size of each subsequent cascade split. Value of 1 means the cascades will be linearly split.
+		// Value of 2 means each subsequent split will be twice the size of the previous one. Valid range is roughly
+		// [1, 4].
+		// Note: Make this an adjustable property?
+		const static float DISTRIBUTON_EXPONENT = 1.0f;
+
+		// First determine the scale of the split, relative to the entire range
+		float scaleModifier = 1.0f;
+		float scale = 0.0f;
+		float totalScale = 0.0f;
+
+		//// Split 0 corresponds to near plane
+		if (index > 0)
+		{
+			for (UINT32 i = 0; i < numCascades; i++)
+			{
+				if (i < index)
+					scale += scaleModifier;
+
+				totalScale += scaleModifier;
+				scaleModifier *= DISTRIBUTON_EXPONENT;
+			}
+		}
+
+		scale = scale / totalScale;
+
+		// Calculate split distance in Z
+		auto& viewProps = view.getProperties();
+		float near = viewProps.nearPlane;
+		float far = viewProps.farPlane;
+
+		return near + (far - near) * scale;
+	}
+}}

+ 3 - 0
Source/SBansheeEngine/Include/BsScriptSceneObject.h

@@ -58,6 +58,9 @@ namespace bs
 		static void internal_setActive(ScriptSceneObject* nativeInstance, bool value);
 		static bool internal_getActive(ScriptSceneObject* nativeInstance);
 
+		static void internal_setMobility(ScriptSceneObject* nativeInstance, int value);
+		static int internal_getMobility(ScriptSceneObject* nativeInstance);
+
 		static void internal_setParent(ScriptSceneObject* nativeInstance, MonoObject* parent);
 		static MonoObject* internal_getParent(ScriptSceneObject* nativeInstance);
 

+ 2 - 1
Source/SBansheeEngine/Source/BsScriptComponent.cpp

@@ -191,7 +191,8 @@ namespace bs
 				else
 				{
 					ScriptComponentBase* scriptComponent = ScriptGameObjectManager::instance().getBuiltinScriptComponent(component);
-					managedComponents.push_back(scriptComponent->getManagedInstance());
+					if(scriptComponent != nullptr)
+						managedComponents.push_back(scriptComponent->getManagedInstance());
 				}
 			}
 		}

+ 18 - 0
Source/SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -25,6 +25,9 @@ namespace bs
 		metaData.scriptClass->addInternalCall("Internal_SetName", &ScriptSceneObject::internal_setName);
 		metaData.scriptClass->addInternalCall("Internal_GetActive", &ScriptSceneObject::internal_getActive);
 		metaData.scriptClass->addInternalCall("Internal_SetActive", &ScriptSceneObject::internal_setActive);
+		metaData.scriptClass->addInternalCall("Internal_GetMobility", &ScriptSceneObject::internal_getMobility);
+		metaData.scriptClass->addInternalCall("Internal_SetMobility", &ScriptSceneObject::internal_setMobility);
+		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptSceneObject::internal_getParent);
 		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptSceneObject::internal_getParent);
 		metaData.scriptClass->addInternalCall("Internal_SetParent", &ScriptSceneObject::internal_setParent);
 		metaData.scriptClass->addInternalCall("Internal_GetNumChildren", &ScriptSceneObject::internal_getNumChildren);
@@ -102,6 +105,21 @@ namespace bs
 		return nativeInstance->mSceneObject->getActive(true);
 	}
 
+	void ScriptSceneObject::internal_setMobility(ScriptSceneObject* nativeInstance, int value)
+	{
+		if (checkIfDestroyed(nativeInstance))
+			return;
+
+		nativeInstance->mSceneObject->setMobility((ObjectMobility)value);
+	}
+
+	int ScriptSceneObject::internal_getMobility(ScriptSceneObject* nativeInstance)
+	{
+		if (checkIfDestroyed(nativeInstance))
+			return false;
+
+		return (int)nativeInstance->mSceneObject->getMobility();
+	}
 	void ScriptSceneObject::internal_setParent(ScriptSceneObject* nativeInstance, MonoObject* parent)
 	{
 		if (checkIfDestroyed(nativeInstance))

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