Parcourir la source

Refactored GUI so you can now specify custom materials for rendering GUI elements (needed for fancy line rendering material)
Some fixes to the documentation

BearishSun il y a 9 ans
Parent
commit
eeb700c5df
39 fichiers modifiés avec 497 ajouts et 196 suppressions
  1. 24 7
      Documentation/Manuals/Native/gettingStarted.md
  2. 3 1
      Documentation/Manuals/Native/gui.md
  3. 6 0
      Source/BansheeCore/Include/BsMeshUtility.h
  4. 1 1
      Source/BansheeEditor/Include/BsGUIColor.h
  5. 8 2
      Source/BansheeEditor/Source/BsGUIColor.cpp
  6. 6 0
      Source/BansheeEngine/CMakeSources.cmake
  7. 1 1
      Source/BansheeEngine/Include/BsGUIButtonBase.h
  8. 2 1
      Source/BansheeEngine/Include/BsGUICanvas.h
  9. 1 1
      Source/BansheeEngine/Include/BsGUIElement.h
  10. 1 1
      Source/BansheeEngine/Include/BsGUIElementContainer.h
  11. 1 1
      Source/BansheeEngine/Include/BsGUIInputBox.h
  12. 1 1
      Source/BansheeEngine/Include/BsGUILabel.h
  13. 12 33
      Source/BansheeEngine/Include/BsGUIManager.h
  14. 1 1
      Source/BansheeEngine/Include/BsGUIScrollBar.h
  15. 1 1
      Source/BansheeEngine/Include/BsGUISliderHandle.h
  16. 1 1
      Source/BansheeEngine/Include/BsGUITexture.h
  17. 1 1
      Source/BansheeEngine/Include/BsGUIViewport.h
  18. 1 0
      Source/BansheeEngine/Include/BsPrerequisites.h
  19. 12 51
      Source/BansheeEngine/Include/BsSprite.h
  20. 76 0
      Source/BansheeEngine/Include/BsSpriteManager.h
  21. 97 0
      Source/BansheeEngine/Include/BsSpriteMaterial.h
  22. 36 0
      Source/BansheeEngine/Include/BsSpriteMaterials.h
  23. 3 0
      Source/BansheeEngine/Source/BsApplication.cpp
  24. 13 4
      Source/BansheeEngine/Source/BsGUIButtonBase.cpp
  25. 9 7
      Source/BansheeEngine/Source/BsGUICanvas.cpp
  26. 1 1
      Source/BansheeEngine/Source/BsGUIElementContainer.cpp
  27. 3 2
      Source/BansheeEngine/Source/BsGUIInputBox.cpp
  28. 2 1
      Source/BansheeEngine/Source/BsGUILabel.cpp
  29. 27 44
      Source/BansheeEngine/Source/BsGUIManager.cpp
  30. 2 1
      Source/BansheeEngine/Source/BsGUIScrollBar.cpp
  31. 2 1
      Source/BansheeEngine/Source/BsGUISliderHandle.cpp
  32. 2 1
      Source/BansheeEngine/Source/BsGUITexture.cpp
  33. 1 1
      Source/BansheeEngine/Source/BsGUIViewport.cpp
  34. 6 1
      Source/BansheeEngine/Source/BsImageSprite.cpp
  35. 5 26
      Source/BansheeEngine/Source/BsSprite.cpp
  36. 33 0
      Source/BansheeEngine/Source/BsSpriteManager.cpp
  37. 73 0
      Source/BansheeEngine/Source/BsSpriteMaterial.cpp
  38. 19 0
      Source/BansheeEngine/Source/BsSpriteMaterials.cpp
  39. 3 1
      Source/BansheeEngine/Source/BsTextSprite.cpp

+ 24 - 7
Documentation/Manuals/Native/gettingStarted.md

@@ -5,19 +5,36 @@ Getting started								{#gettingStarted}
 This manual offers a quick overview of commonly used Banshee functionality, in order to give you a better idea of how Banshee works. For a fully working example check out the `ExampleProject` project available with the source code.
 
 # Starting an application
-Banshee is started through the @ref BansheeEngine::Application "Application" interface. To start the engine you need to provide it with a description of the primary render window, and the wanted @ref BansheeEngine::RenderAPIPlugin "render API" plugin.
+Banshee is started through the @ref BansheeEngine::Application "Application" interface. To start the engine you need to provide it with a description of the primary render window and choose which modules to load. Since Banshee is a plugin based engine you have a selection between multiple modules for each system like render API or physics, and a set of completely optional plugins (like importers for various file formats).
 
 After the application is started by calling @ref BansheeEngine::Application::startUp "Application::startUp", you can set up your custom code in the form of @ref BansheeEngine::Component "components" (see later). After that you can run the main loop with @ref BansheeEngine::Application::runMainLoop "Application::runMainLoop" which will execute your code and actually get everything in motion.
 
 Once the main loop terminates use @ref BansheeEngine::Application::shutDown "Application::shutDown" to clean everything up.
 
 ~~~~~~~~~~~~~{.cpp}
-RENDER_WINDOW_DESC renderWindowDesc;
-renderWindowDesc.videoMode = VideoMode(1280, 720);
-renderWindowDesc.title = "My App";
-renderWindowDesc.fullscreen = false;
-
-Application::startUp(renderWindowDesc, RenderAPIPlugin::DX11);
+// Descriptor used for initializing the engine
+START_UP_DESC startUpDesc;
+
+// Choose which plugins to load. In this case use the default values as specified by the build system.
+startUpDesc.renderAPI = BS_RENDER_API_MODULE;
+startUpDesc.renderer = BS_RENDERER_MODULE;
+startUpDesc.audio = BS_AUDIO_MODULE;
+startUpDesc.physics = BS_PHYSICS_MODULE;
+startUpDesc.input = BS_INPUT_MODULE;
+
+// List of optional importer plugins we plan on using for importing various resources.
+startUpDesc.importers.push_back("BansheeFreeImgImporter"); // For importing textures
+startUpDesc.importers.push_back("BansheeFBXImporter"); // For importing meshes
+startUpDesc.importers.push_back("BansheeFontImporter"); // For importing fonts
+startUpDesc.importers.push_back("BansheeSL"); // For importing shaders
+
+// Descriptor used for initializing the primary application window.
+startUpDesc.primaryWindowDesc.videoMode = VideoMode(windowResWidth, windowResHeight);
+startUpDesc.primaryWindowDesc.title = "Banshee Example App";
+startUpDesc.primaryWindowDesc.fullscreen = false;
+startUpDesc.primaryWindowDesc.depthBuffer = false;
+
+Application::startUp(startUpDesc);
 ... set up game code ...
 Application::instance().runMainLoop();
 Application::shutDown();

+ 3 - 1
Documentation/Manuals/Native/gui.md

@@ -18,11 +18,13 @@ When creating a custom GUI element you will need to override the @ref BansheeEng
  - @ref BansheeEngine::GUIElement::_getNumRenderElements() "_getNumRenderElements()" - Return the number of separate elements that your GUIElement consists of. Each element has its own mesh, material and texture. In most cases there is only one element.
  - @ref BansheeEngine::GUIElement::_getMeshSize() "_getMeshSize()" - Returns the number of vertices and indices for the mesh of the render element at the specified index (e.g. one quad (4 vertices, 6 indices) if the GUI element displays just one texture). This allows external systems to know how big of a buffer to allocate for the element's mesh.
  - @ref BansheeEngine::GUIElement::_fillBuffer() "_fillBuffer()" - This is the meat of the `GUIElement` override. It allows you to fill a buffer with vertices, uv coordinates and indices for a mesh for the specified render element. This allows you to fully control the look of your GUI element by providing customized geometry.
- - @ref BansheeEngine::GUIElement::_getMaterial() "_getMaterial()" - Here you should return a material that will be applied to the mesh output in the previous method. You can choose from a few pre-built material types and provide a texture and a color. 
+ - @ref BansheeEngine::GUIElement::_getMaterial() "_getMaterial()" - Here you should return a material that will be applied to the mesh output in the previous method, as well as an optional texture or color tint. You can choose from a set of pre-built materials or use your own material. 
  - @ref BansheeEngine::GUIElement::updateRenderElementsInternal() "updateRenderElementsInternal()" - Called whenever the element's size or style changes. In this method you should rebuild the element's mesh. (While you could build the mesh in `_fillBuffer` it is inefficient as `_fillBuffer` will get called much more often than `updateRenderElementsInternal` due to mesh batching).
  
 In order to help you with creating GUI meshes and materials make sure to take a look at @ref BansheeEngine::ImageSprite "ImageSprite" and @ref BansheeEngine::TextSprite "TextSprite" classes. `ImageSprite` can easily generate image geometry of specified size, whether a simple quad or a scale-9-grid image. And `TextSprite` will take a text string, font and additional options as input, and output a set of quads required for text rendering.
 
+To retrieve materials used for rendering GUI use the @ref BansheeEngine::SpriteManager "SpriteManager", which contains a few built-in materials. You can also create your own materials by implementing the @ref BansheeEngine::SpriteMaterial "SpriteMaterial" interface and registering it with @ref BansheeEngine::SpriteManager "SpriteManager" by calling @ref BansheeEngine::SpriteManager::registerMaterial "SpriteManager::registerMaterial".
+
 When constructing the `GUIElement` you will also need to provide a style-name (see next chapter) and dimensions to the `GUIElement` constructor. The dimensions determine the initial size of the element. To use the default dimensions just provide the constructor with return value from @ref BansheeEngine::GUIDimensions::create() "GUIDimensions::create()". `GUIElement` will internally use dimensions provided by its style, but providing custom `GUIDimensions` allows you to override the dimensions of the style. For that reason you should provide the user with a constructor that accepts @ref BansheeEngine::GUIOptions "GUIOptions" object, which will contain optional dimension overrides the user can specify. Then you can create a `GUIDimension` by providing the `GUIOptions` object as a parameter.
 
 It is highly suggested you check out an implementation of @ref BansheeEngine::GUITexture "GUITexture" (in *BsGUITexture.cpp*) for a simple implementation of everything mentioned.

+ 6 - 0
Source/BansheeCore/Include/BsMeshUtility.h

@@ -90,6 +90,9 @@ namespace BansheeEngine
 		 *									from each other.
 		 * @param[in]	uvs					A set of UV coordinates in Vector2 format. Each coordinate should be 
 		 *									@p vertexStride bytes from each other. Can be null if UV is not needed.
+		 * @param[in]	numTris				Number of triangles to clip (must be number of vertices/uvs / 3).
+		 * @param[in]	vertexStride		Distance in bytes between two separate vertex or UV values in the provided
+		 *									@p vertices and @p uvs buffers.
 		 * @param[in]	clipPlanes			A set of planes to clip the vertices against. Since the vertices are 
 		 *									two-dimensional the plane's Z coordinate should be zero.
 		 * @param[in]	writeCallback		Callback that will be triggered when clipped vertices and UV coordinates are
@@ -106,6 +109,9 @@ namespace BansheeEngine
 		 *									from each other.
 		 * @param[in]	uvs					A set of UV coordinates in Vector2 format. Each coordinate should be 
 		 *									@p vertexStride bytes from each other. Can be null if UV is not needed.
+		 * @param[in]	numTris				Number of triangles to clip (must be number of vertices/uvs / 3).
+		 * @param[in]	vertexStride		Distance in bytes between two separate vertex or UV values in the provided
+		 *									@p vertices and @p uvs buffers.
 		 * @param[in]	clipPlanes			A set of planes to clip the vertices against. 
 		 * @param[in]	writeCallback		Callback that will be triggered when clipped vertices and UV coordinates are
 		 *									generated and need to be stored. Vertices are always generate in tuples of

+ 1 - 1
Source/BansheeEditor/Include/BsGUIColor.h

@@ -62,7 +62,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial() */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 8 - 2
Source/BansheeEditor/Source/BsGUIColor.cpp

@@ -62,14 +62,20 @@ namespace BansheeEngine
 		return numElements;
 	}
 
-	const SpriteMaterialInfo& GUIColor::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIColor::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		UINT32 alphaSpriteIdx = mColorSprite->getNumRenderElements();
 
-		if(renderElementIdx >= alphaSpriteIdx)
+		if (renderElementIdx >= alphaSpriteIdx)
+		{
+			*material = mAlphaSprite->getMaterial(alphaSpriteIdx - renderElementIdx);
 			return mAlphaSprite->getMaterialInfo(alphaSpriteIdx - renderElementIdx);
+		}
 		else
+		{
+			*material = mColorSprite->getMaterial(renderElementIdx);
 			return mColorSprite->getMaterialInfo(renderElementIdx);
+		}
 	}
 
 	void GUIColor::_getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const

+ 6 - 0
Source/BansheeEngine/CMakeSources.cmake

@@ -110,6 +110,9 @@ set(BS_BANSHEEENGINE_SRC_2D
 	"Source/BsSprite.cpp"
 	"Source/BsSpriteTexture.cpp"
 	"Source/BsTextSprite.cpp"
+	"Source/BsSpriteMaterial.cpp"
+	"Source/BsSpriteMaterials.cpp"
+	"Source/BsSpriteManager.cpp"
 )
 
 set(BS_BANSHEEENGINE_SRC_UTILITY
@@ -126,6 +129,9 @@ set(BS_BANSHEEENGINE_INC_2D
 	"Include/BsSprite.h"
 	"Include/BsSpriteTexture.h"
 	"Include/BsTextSprite.h"
+	"Include/BsSpriteMaterial.h"
+	"Include/BsSpriteMaterials.h"
+	"Include/BsSpriteManager.h"
 )
 
 set(BS_BANSHEEENGINE_INC_RTTI

+ 1 - 1
Source/BansheeEngine/Include/BsGUIButtonBase.h

@@ -69,7 +69,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 2 - 1
Source/BansheeEngine/Include/BsGUICanvas.h

@@ -99,6 +99,7 @@ namespace BansheeEngine
 		 * @param[in]	text		Text to draw.
 		 * @param[in]	position	Position of the text to draw. This represents the top-left corner of the text. It is
 		 *							relative to the canvas origin (top-left).
+		 * @param[in]	font		Font to draw the text with.
 		 * @param[in]	size		Size of the font.
 		 * @param[in]	color		Color of the text.
 		 */
@@ -185,7 +186,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUIElement.h

@@ -105,7 +105,7 @@ namespace BansheeEngine
 		 *
 		 * @see		_getNumRenderElements()
 		 */
-		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const = 0;
+		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const = 0;
 
 		/**
 		 * Returns the number of vertices and indices that the specified render element will use. You will need this value

+ 1 - 1
Source/BansheeEngine/Include/BsGUIElementContainer.h

@@ -22,7 +22,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUIInputBox.h

@@ -104,7 +104,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial() */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUILabel.h

@@ -82,7 +82,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 12 - 33
Source/BansheeEngine/Include/BsGUIManager.h

@@ -48,6 +48,13 @@ namespace BansheeEngine
 			Dragging
 		};
 
+		/** Material data required for rendering a single GUI mesh. */
+		struct GUIMaterialData
+		{
+			SpriteMaterial* material;
+			SpriteMaterialInfo matInfo;
+		};
+
 		/**	GUI render data for a single viewport. */
 		struct GUIRenderData
 		{
@@ -56,7 +63,7 @@ namespace BansheeEngine
 			{ }
 
 			Vector<SPtr<TransientMesh>> cachedMeshes;
-			Vector<SpriteMaterialInfo> cachedMaterials;
+			Vector<GUIMaterialData> cachedMaterials;
 			Vector<GUIWidget*> cachedWidgetsPerMesh;
 			Vector<GUIWidget*> widgets;
 			bool isDirty;
@@ -67,9 +74,10 @@ namespace BansheeEngine
 		{
 			SPtr<TransientMeshCore> mesh;
 			SPtr<TextureCore> texture;
-			SpriteMaterial materialType;
+			SpriteMaterial* material;
 			Color tint;
 			Matrix4 worldTransform;
+			SpriteMaterialExtraInfo* additionalData;
 		};
 
 		/**	Container for a GUI widget. */
@@ -415,34 +423,11 @@ namespace BansheeEngine
 	{
 		friend class GUIManager;
 
-		/** Material reference and parameter handles for a specific material type. */
-		struct MaterialInfo
-		{
-			MaterialInfo() { }
-			MaterialInfo(const SPtr<MaterialCore>& material);
-
-			SPtr<MaterialCore> material;
-
-			MaterialParamMat4Core worldTransformParam;
-			MaterialParamFloatCore invViewportWidthParam;
-			MaterialParamFloatCore invViewportHeightParam;
-			MaterialParamColorCore tintParam;
-			MaterialParamTextureCore textureParam;
-			MaterialParamSampStateCore samplerParam;
-		};
-
 	public:
 		~GUIManagerCore();
 
-		/**
-		 * Initializes the object. Must be called right after construction.
-		 *
-		 * @param[in]	textMat			Material used for drawing text sprites.
-		 * @param[in]	imageMat		Material used for drawing non-transparent image sprites.
-		 * @param[in]	imageAlphaMat	Material used for drawing transparent image sprites.
-		 */
-		void initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
-			const SPtr<MaterialCore>& imageAlphaMat);
+		/** Initializes the object. Must be called right after construction. */
+		void initialize();
 
 	private:
 		/**
@@ -456,12 +441,6 @@ namespace BansheeEngine
 		void render(const SPtr<CameraCore>& camera);
 
 		UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>> mPerCameraData;
-
-		// Immutable
-		MaterialInfo mTextMaterialInfo;
-		MaterialInfo mImageMaterialInfo;
-		MaterialInfo mImageAlphaMaterialInfo;
-
 		SPtr<SamplerStateCore> mSamplerState;
 	};
 

+ 1 - 1
Source/BansheeEngine/Include/BsGUIScrollBar.h

@@ -80,7 +80,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUISliderHandle.h

@@ -106,7 +106,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial() */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUITexture.h

@@ -163,7 +163,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

+ 1 - 1
Source/BansheeEngine/Include/BsGUIViewport.h

@@ -67,7 +67,7 @@ namespace BansheeEngine
 		UINT32 _getNumRenderElements() const override;
 
 		/** @copydoc GUIElement::_getMaterial */
-		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+		const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override;
 
 		/** @copydoc GUIElement::_getMeshSize() */
 		void _getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const override;

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

@@ -186,6 +186,7 @@ namespace BansheeEngine
 	class TextSprite;
 	class ImageSprite;
 	class SpriteTexture;
+	class SpriteMaterial;
 	struct SpriteMaterialInfo;
 
 	// Components

+ 12 - 51
Source/BansheeEngine/Include/BsSprite.h

@@ -3,6 +3,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
+#include "BsSpriteMaterial.h"
 #include "BsVector2I.h"
 #include "BsRect2I.h"
 #include "BsColor.h"
@@ -27,39 +28,11 @@ namespace BansheeEngine
 		SA_BottomRight
 	};
 
-	/** Types of materials available for rendering sprites. */
-	enum class SpriteMaterial
-	{
-		Text, Image, ImageAlpha
-	};
-
-	/** Contains information for initializing a sprite material. */
-	struct SpriteMaterialInfo
-	{
-		SpriteMaterialInfo()
-			:type(SpriteMaterial::Image), groupId(0) 
-		{ }
-
-		/** Generates a hash value that describes the contents of this object. */
-		UINT64 generateHash() const;
-
-		SpriteMaterial type;
-		UINT64 groupId;
-		HTexture texture;
-		Color tint;
-	};
-
-	/** Equals operator for SpriteMaterialInfo. */
-	bool operator==(const SpriteMaterialInfo& lhs, const SpriteMaterialInfo& rhs);
-
-	/** Not equals operator for SpriteMaterialInfo. */
-	bool operator!=(const SpriteMaterialInfo& lhs, const SpriteMaterialInfo& rhs);
-
 	/** Contains information about a single sprite render element, including its geometry and material. */
 	struct SpriteRenderElement
 	{
 		SpriteRenderElement()
-			:vertices(nullptr), uvs(nullptr), indexes(nullptr), numQuads(0)
+			:vertices(nullptr), uvs(nullptr), indexes(nullptr), numQuads(0), material(nullptr)
 		{ }
 
 		Vector2* vertices;
@@ -67,6 +40,7 @@ namespace BansheeEngine
 		UINT32* indexes;
 		UINT32 numQuads;
 		SpriteMaterialInfo matInfo;
+		SpriteMaterial* material;
 	};
 
 	/**	Generates geometry and contains information needed for rendering a two dimensional element. */
@@ -96,14 +70,19 @@ namespace BansheeEngine
 		UINT32 getNumRenderElements() const;
 
 		/**
-		 * Gets a material for the specified render element index.
-		 * 		
-		 * @return	Structure describing the material.
+		 * Gets material information required for rendering the element at the specified index.
 		 *
 		 * @see		getNumRenderElements()
 		 */
 		const SpriteMaterialInfo& getMaterialInfo(UINT32 renderElementIdx) const;
 
+		/**
+		 * Gets the material that will be used for rendering the element at the specified index.
+		 *
+		 * @see		getNumRenderElements()
+		 */
+		SpriteMaterial* getMaterial(UINT32 renderElementIdx) const;
+
 		/**
 		 * Returns the number of quads that the specified render element will use. You will need this value when creating
 		 * the buffers before calling fillBuffer().
@@ -186,22 +165,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
-
-/** @cond STDLIB */
-/** @addtogroup GUI
- *  @{
- */
-
-/**	Hash value generator for STL reference wrapper for SpriteMaterialInfo. */
-template<>
-struct std::hash<std::reference_wrapper<const BansheeEngine::SpriteMaterialInfo>>
-{
-	size_t operator()(const std::reference_wrapper<const BansheeEngine::SpriteMaterialInfo>& value) const
-	{
-		return (size_t)value.get().generateHash();
-	}
-};
-
-/** @} */
-/** @endcond */
+}

+ 76 - 0
Source/BansheeEngine/Include/BsSpriteManager.h

@@ -0,0 +1,76 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsModule.h"
+#include "BsSpriteMaterial.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup 2D-Internal
+	 *  @{
+	 */
+
+	/** Contains materials used for sprite rendering. */
+	class BS_EXPORT SpriteManager : public Module<SpriteManager>
+	{
+		/** Types of sprite materials accessible by default. */
+		enum class BuiltinSpriteMaterialType
+		{
+			ImageTransparent,
+			ImageOpaque,
+			Text,
+			Count // Keep at end
+		};
+
+	public:
+		SpriteManager();
+		~SpriteManager();
+
+		/** Returns the material used for rendering transparent image sprites. */
+		SpriteMaterial* getImageTransparentMaterial() const 
+			{ return getMaterial(builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageTransparent]); }
+
+		/** Returns the material used for rendering opaque image sprites. */
+		SpriteMaterial* getImageOpaqueMaterial() const
+			{ return getMaterial(builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageOpaque]); }
+
+		/** Returns the material used for rendering text sprites. */
+		SpriteMaterial* getTextMaterial() const
+			{ return getMaterial(builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Text]); }
+
+		/** Returns a sprite material with the specified ID. Returns null if one cannot be found. */
+		SpriteMaterial* getMaterial(UINT32 id) const;
+
+		/** 
+		 * Registers a new material in the sprite manager. Caller must ensure the material has a unique ID that doesn't
+		 * already exist in the sprite manager, otherwise the call will be ignored.
+		 *
+		 * @return	Newly created material, or at existing one if one already exists.
+		 */
+		template <class T>
+		SpriteMaterial* registerMaterial()
+		{
+			SpriteMaterial* newMaterial = bs_new<T>();
+			
+			UINT32 id = newMaterial->getId();
+			auto iterFind = mMaterials.find(id);
+			if (iterFind != mMaterials.end())
+			{
+				// Already exists
+				LOGWRN("Attempting to register a sprite material that already exists, ignoring request.");
+				bs_delete(newMaterial);
+				return iterFind->second;
+			}
+
+			mMaterials[id] = newMaterial;
+			return newMaterial;
+		}
+	private:
+		UnorderedMap<UINT32, SpriteMaterial*> mMaterials;
+		UINT32 builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Count];
+	};
+
+	/** @} */
+}

+ 97 - 0
Source/BansheeEngine/Include/BsSpriteMaterial.h

@@ -0,0 +1,97 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsMaterialParam.h"
+#include "BsVector2I.h"
+#include "BsColor.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup 2D-Internal
+	 *  @{
+	 */
+
+	/** Extension structure that can be used by SpriteMaterial%s to access specialized data. */
+	struct SpriteMaterialExtraInfo
+	{ };
+
+	/** Contains information for initializing a sprite material. */
+	struct SpriteMaterialInfo
+	{
+		SpriteMaterialInfo()
+			:groupId(0), additionalData(nullptr)
+		{ }
+
+		UINT64 groupId;
+		HTexture texture;
+		Color tint;
+		SpriteMaterialExtraInfo* additionalData;
+	};
+
+	/** Interfaced implemented by materials used for rendering sprites. This is expected to be used as a singleton. */
+	class BS_EXPORT SpriteMaterial
+	{
+	public:
+		SpriteMaterial(UINT32 id, const HMaterial& material);
+		virtual ~SpriteMaterial();
+
+		/** Returns the unique ID of the sprite material. */
+		UINT32 getId() const { return mId; };
+
+		/** 
+		 * Generates a hash value that describes the contents of the sprite material info structure. Returned hash doesn't
+		 * guarantee that the two objects with the same hash are idential, but rather that the objects are mergeable via
+		 * merge().
+		 */
+		virtual UINT64 getMergeHash(const SpriteMaterialInfo& info) const;
+
+		/** 
+		 * Merges two SpriteMaterialInfo%s into one structure. User must guarantee that the two objects are mergeable
+		 * by ensuring their merge hashes match (by calling getMergeHash()).
+		 *
+		 * @param[in, out]	mergeInto	Object that contains the first part of the data, and will contain the result of the
+		 *								merge.
+		 * @param[in]		mergeFrom	Object that contains the second part of the data to merge, which will be merged into
+		 *								the first object.
+		 */
+		virtual void merge(SpriteMaterialInfo& mergeInto, const SpriteMaterialInfo& mergeFrom) const { }
+
+		/**
+		 * Renders the provided mesh using the current material.
+		 *
+		 * @param[in]	mesh			Mesh to render, containing vertices in screen space.
+		 * @param[in]	texture			Optional texture to render the mesh with.
+		 * @param[in]	sampler			Optional sampler to render the texture with.
+		 * @param[in]	tint			Color tint to apply to the rendered mesh.
+		 * @param[in]	worldTransform	World transform to apply to the rendered mesh.
+		 * @param[in]	invViewportSize	Inverse size of the viewport the mesh will be rendered to.
+		 * @param[in]	additionalData	Optional additional data that might be required by the renderer.
+		 */
+		virtual void render(const SPtr<MeshCoreBase>& mesh, const SPtr<TextureCore>& texture,
+			const SPtr<SamplerStateCore>& sampler, const Color& tint, const Matrix4& worldTransform, 
+			const Vector2& invViewportSize, SpriteMaterialExtraInfo* additionalData) const;
+
+	protected:
+		/** Perform initialization of core-thread specific objects. */
+		virtual void initialize();
+
+		/** Destroys the core thread material. */
+		static void destroy(const SPtr<MaterialCore>& material);
+
+		UINT32 mId;
+
+		// Core thread only (everything below)
+		SPtr<MaterialCore> mMaterial;
+		mutable MaterialParamMat4Core mWorldTransformParam;
+		mutable MaterialParamFloatCore mInvViewportWidthParam;
+		mutable MaterialParamFloatCore mInvViewportHeightParam;
+		mutable MaterialParamColorCore mTintParam;
+		mutable MaterialParamTextureCore mTextureParam;
+		mutable MaterialParamSampStateCore mSamplerParam;
+	};
+
+	/** @} */
+}
+

+ 36 - 0
Source/BansheeEngine/Include/BsSpriteMaterials.h

@@ -0,0 +1,36 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsSpriteMaterial.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup 2D-Internal
+	 *  @{
+	 */
+
+	/** Sprite material used for rendering transparent images. */
+	class BS_EXPORT SpriteImageTransparentMaterial : public SpriteMaterial
+	{
+	public:
+		SpriteImageTransparentMaterial();
+	};
+
+	/** Sprite material used for rendering opaque images. */
+	class BS_EXPORT SpriteImageOpaqueMaterial : public SpriteMaterial
+	{
+	public:
+		SpriteImageOpaqueMaterial();
+	};
+
+	/** Sprite material used for rendering text. */
+	class BS_EXPORT SpriteTextMaterial : public SpriteMaterial
+	{
+	public:
+		SpriteTextMaterial();
+	};
+
+	/** @} */
+}

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

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsApplication.h"
 #include "BsGUIManager.h"
+#include "BsSpriteManager.h"
 #include "BsBuiltinResources.h"
 #include "BsScriptManager.h"
 #include "BsProfilingManager.h"
@@ -39,6 +40,7 @@ namespace BansheeEngine
 
 		ShortcutManager::shutDown();
 		GUIManager::shutDown();
+		SpriteManager::shutDown();
 		BuiltinResources::shutDown();
 		RendererMaterialManager::shutDown();
 		VirtualInput::shutDown();
@@ -55,6 +57,7 @@ namespace BansheeEngine
 		BuiltinResources::startUp();
 		RendererMaterialManager::startUp();
 		RendererManager::instance().initialize();
+		SpriteManager::startUp();
 		GUIManager::startUp();
 		ShortcutManager::startUp();
 

+ 13 - 4
Source/BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -69,17 +69,26 @@ namespace BansheeEngine
 		return numElements;
 	}
 
-	const SpriteMaterialInfo& GUIButtonBase::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIButtonBase::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
 		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
 
-		if(renderElementIdx >= contentImgSpriteIdx)
+		if (renderElementIdx >= contentImgSpriteIdx)
+		{
+			*material = mContentImageSprite->getMaterial(contentImgSpriteIdx - renderElementIdx);
 			return mContentImageSprite->getMaterialInfo(contentImgSpriteIdx - renderElementIdx);
-		else if(renderElementIdx >= textSpriteIdx)
+		}
+		else if (renderElementIdx >= textSpriteIdx)
+		{
+			*material = mTextSprite->getMaterial(textSpriteIdx - renderElementIdx);
 			return mTextSprite->getMaterialInfo(textSpriteIdx - renderElementIdx);
+		}
 		else
+		{
+			*material = mImageSprite->getMaterial(renderElementIdx);
 			return mImageSprite->getMaterialInfo(renderElementIdx);
+		}
 	}
 
 	void GUIButtonBase::_getMeshSize(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices) const
@@ -95,7 +104,7 @@ namespace BansheeEngine
 		else
 			numQuads = mImageSprite->getNumQuads(renderElementIdx);
 
-		numVertices = numQuads * 4;
+		numVertices = numQuads * 4;
 		numIndices = numQuads * 6;
 	}
 

+ 9 - 7
Source/BansheeEngine/Source/BsGUICanvas.cpp

@@ -6,6 +6,7 @@
 #include "BsGUIDimensions.h"
 #include "BsGUITexture.h"
 #include "BsShapeMeshes2D.h"
+#include "BsSpriteManager.h"
 #include "BsException.h"
 
 namespace BansheeEngine
@@ -56,7 +57,6 @@ namespace BansheeEngine
 		TriangleElementData& elemData = mTriangleElementData.back();
 		elemData.matInfo.groupId = 0;
 		elemData.matInfo.tint = color;
-		elemData.matInfo.type = SpriteMaterial::ImageAlpha; // TODO - Use line material here
 
 		mForceTriangleBuild = true;
 		_markContentAsDirty();
@@ -87,7 +87,6 @@ namespace BansheeEngine
 		TriangleElementData& elemData = mTriangleElementData.back();
 		elemData.matInfo.groupId = 0;
 		elemData.matInfo.tint = color;
-		elemData.matInfo.type = SpriteMaterial::ImageAlpha; // TODO - Use line material here
 
 		mForceTriangleBuild = true;
 		_markContentAsDirty();
@@ -138,7 +137,6 @@ namespace BansheeEngine
 		TriangleElementData& elemData = mTriangleElementData.back();
 		elemData.matInfo.groupId = 0;
 		elemData.matInfo.tint = color;
-		elemData.matInfo.type = SpriteMaterial::ImageAlpha;
 
 		mForceTriangleBuild = true;
 		_markContentAsDirty();
@@ -168,7 +166,6 @@ namespace BansheeEngine
 		TriangleElementData& elemData = mTriangleElementData.back();
 		elemData.matInfo.groupId = 0;
 		elemData.matInfo.tint = color;
-		elemData.matInfo.type = SpriteMaterial::ImageAlpha; // TODO - Use line material here
 
 		mForceTriangleBuild = true;
 		_markContentAsDirty();
@@ -214,7 +211,7 @@ namespace BansheeEngine
 		return mNumRenderElements;
 	}
 
-	const SpriteMaterialInfo& GUICanvas::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUICanvas::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		static const SpriteMaterialInfo defaultMatInfo;
 
@@ -222,14 +219,19 @@ namespace BansheeEngine
 		switch (element.type)
 		{
 		case CanvasElementType::Line:
+			*material = nullptr; // TODO - Assign line material and additionalData of .matInfo
 			return mTriangleElementData[element.dataId].matInfo;
 		case CanvasElementType::Image:
-			return element.imageSprite->getMaterialInfo(renderElementIdx);
+			*material = element.imageSprite->getMaterial(0);
+			return element.imageSprite->getMaterialInfo(0);
 		case CanvasElementType::Text:
-			return element.textSprite->getMaterialInfo(renderElementIdx);
+			*material = element.imageSprite->getMaterial(renderElementIdx - element.renderElemStart);
+			return element.textSprite->getMaterialInfo(renderElementIdx - element.renderElemStart);
 		case CanvasElementType::Triangle:
+			*material = SpriteManager::instance().getImageTransparentMaterial();
 			return mTriangleElementData[element.dataId].matInfo;
 		default:
+			*material = nullptr;
 			return defaultMatInfo;
 		}
 	}

+ 1 - 1
Source/BansheeEngine/Source/BsGUIElementContainer.cpp

@@ -18,7 +18,7 @@ namespace BansheeEngine
 		return 0;
 	}
 
-	const SpriteMaterialInfo& GUIElementContainer::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIElementContainer::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		BS_EXCEPT(InvalidStateException, "Trying to retrieve a material from an element with no render elements.");
 		static SpriteMaterialInfo dummy;

+ 3 - 2
Source/BansheeEngine/Source/BsGUIInputBox.cpp

@@ -121,11 +121,12 @@ namespace BansheeEngine
 		return numElements;
 	}
 
-	const SpriteMaterialInfo& GUIInputBox::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIInputBox::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		UINT32 localRenderElementIdx;
 		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
 
+		*material = sprite->getMaterial(localRenderElementIdx);
 		return sprite->getMaterialInfo(localRenderElementIdx);
 	}
 
@@ -135,7 +136,7 @@ namespace BansheeEngine
 		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
 
 		UINT32 numQuads = sprite->getNumQuads(localRenderElementIdx);
-		numVertices = numQuads * 4;
+		numVertices = numQuads * 4;
 		numIndices = numQuads * 6;
 	}
 

+ 2 - 1
Source/BansheeEngine/Source/BsGUILabel.cpp

@@ -24,8 +24,9 @@ namespace BansheeEngine
 		return mTextSprite->getNumRenderElements();
 	}
 
-	const SpriteMaterialInfo& GUILabel::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUILabel::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
+		*material = mTextSprite->getMaterial(renderElementIdx);
 		return mTextSprite->getMaterialInfo(renderElementIdx);
 	}
 

+ 27 - 44
Source/BansheeEngine/Source/BsGUIManager.cpp

@@ -57,6 +57,7 @@ namespace BansheeEngine
 
 	struct GUIMaterialGroup
 	{
+		SpriteMaterial* material;
 		SpriteMaterialInfo matInfo;
 		UINT32 numVertices;
 		UINT32 numIndices;
@@ -111,12 +112,7 @@ namespace BansheeEngine
 		GUIManagerCore* core = bs_new<GUIManagerCore>();
 		mCore.store(core, std::memory_order_release);
 
-		HMaterial textMaterial = BuiltinResources::instance().createSpriteTextMaterial();
-		HMaterial imageMaterial = BuiltinResources::instance().createSpriteNonAlphaImageMaterial();
-		HMaterial imageAlphaMaterial = BuiltinResources::instance().createSpriteImageMaterial();
-
-		gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::initialize, core,
-			textMaterial->getCore(), imageMaterial->getCore(), imageAlphaMaterial->getCore()));
+		gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::initialize, core));
 	}
 
 	GUIManager::~GUIManager()
@@ -392,10 +388,10 @@ namespace BansheeEngine
 				UINT32 meshIdx = 0;
 				for (auto& mesh : renderData.cachedMeshes)
 				{
-					SpriteMaterialInfo materialInfo = renderData.cachedMaterials[meshIdx];
+					const GUIMaterialData& guiMaterial = renderData.cachedMaterials[meshIdx];
 					GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
 
-					if (materialInfo.texture == nullptr || !materialInfo.texture.isLoaded())
+					if (guiMaterial.matInfo.texture == nullptr || !guiMaterial.matInfo.texture.isLoaded())
 					{
 						meshIdx++;
 						continue;
@@ -410,11 +406,12 @@ namespace BansheeEngine
 					cameraData.push_back(GUICoreRenderData());
 					GUICoreRenderData& newEntry = cameraData.back();
 
-					newEntry.materialType = materialInfo.type;
-					newEntry.texture = materialInfo.texture->getCore();
-					newEntry.tint = materialInfo.tint;
+					newEntry.material = guiMaterial.material;
+					newEntry.texture = guiMaterial.matInfo.texture->getCore();
+					newEntry.tint = guiMaterial.matInfo.tint;
 					newEntry.mesh = mesh->getCore();
 					newEntry.worldTransform = widget->getWorldTfrm();
+					newEntry.additionalData = guiMaterial.matInfo.additionalData;
 
 					meshIdx++;
 				}
@@ -486,7 +483,7 @@ namespace BansheeEngine
 
 				// Group the elements in such a way so that we end up with a smallest amount of
 				// meshes, without breaking back to front rendering order
-				FrameUnorderedMap<std::reference_wrapper<const SpriteMaterialInfo>, FrameVector<GUIMaterialGroup>> materialGroups;
+				FrameUnorderedMap<UINT64, FrameVector<GUIMaterialGroup>> materialGroups;
 				for (auto& elem : allElements)
 				{
 					GUIElement* guiElem = elem.element;
@@ -496,8 +493,12 @@ namespace BansheeEngine
 					Rect2I tfrmedBounds = guiElem->_getClippedBounds();
 					tfrmedBounds.transform(guiElem->_getParentWidget()->getWorldTfrm());
 
-					const SpriteMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx);
-					FrameVector<GUIMaterialGroup>& groupsPerMaterial = materialGroups[std::cref(matInfo)];
+					SpriteMaterial* spriteMaterial = nullptr;
+					const SpriteMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx, &spriteMaterial);
+					assert(spriteMaterial != nullptr);
+
+					UINT64 hash = spriteMaterial->getMergeHash(matInfo);
+					FrameVector<GUIMaterialGroup>& groupsPerMaterial = materialGroups[hash];
 					
 					// Try to find a group this material will fit in:
 					//  - Group that has a depth value same or one below elements depth will always be a match
@@ -571,6 +572,8 @@ namespace BansheeEngine
 						foundGroup->bounds = tfrmedBounds;
 						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
 						foundGroup->matInfo = matInfo;
+						foundGroup->material = spriteMaterial;
+
 						guiElem->_getMeshSize(renderElemIdx, foundGroup->numVertices, foundGroup->numIndices);
 					}
 					else
@@ -584,6 +587,8 @@ namespace BansheeEngine
 						guiElem->_getMeshSize(renderElemIdx, numVertices, numIndices);
 						foundGroup->numVertices += numVertices;
 						foundGroup->numIndices += numIndices;
+
+						spriteMaterial->merge(foundGroup->matInfo, matInfo);
 					}
 				}
 
@@ -624,7 +629,9 @@ namespace BansheeEngine
 				UINT32 groupIdx = 0;
 				for(auto& group : sortedGroups)
 				{
-					renderData.cachedMaterials[groupIdx] = group->matInfo;
+					GUIMaterialData& matData = renderData.cachedMaterials[groupIdx];
+					matData.matInfo = group->matInfo;
+					matData.material = group->material;
 
 					if(mSeparateMeshesByWidget)
 					{
@@ -1729,13 +1736,8 @@ namespace BansheeEngine
 			activeRenderer->unregisterRenderCallback(cameraData.first.get(), 30);
 	}
 
-	void GUIManagerCore::initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
-		const SPtr<MaterialCore>& imageAlphaMat)
+	void GUIManagerCore::initialize()
 	{
-		mTextMaterialInfo = MaterialInfo(textMat);
-		mImageMaterialInfo = MaterialInfo(imageMat);
-		mImageAlphaMaterialInfo = MaterialInfo(imageAlphaMat);
-
 		SAMPLER_STATE_DESC ssDesc;
 		ssDesc.magFilter = FO_POINT;
 		ssDesc.minFilter = FO_POINT;
@@ -1806,34 +1808,15 @@ namespace BansheeEngine
 
 		float invViewportWidth = 1.0f / (camera->getViewport()->getWidth() * 0.5f);
 		float invViewportHeight = 1.0f / (camera->getViewport()->getHeight() * 0.5f);
+
+		Vector2 invViewportSize(invViewportWidth, invViewportHeight);
 		for (auto& entry : renderData)
 		{
-			MaterialInfo& matInfo = entry.materialType == SpriteMaterial::Text ? mTextMaterialInfo :
-				(entry.materialType == SpriteMaterial::Image ? mImageMaterialInfo : mImageAlphaMaterialInfo);
-
-			matInfo.textureParam.set(entry.texture);
-			matInfo.samplerParam.set(mSamplerState);
-			matInfo.tintParam.set(entry.tint);
-			matInfo.invViewportWidthParam.set(invViewportWidth);
-			matInfo.invViewportHeightParam.set(invViewportHeight);
-			matInfo.worldTransformParam.set(entry.worldTransform);
-
 			// TODO - I shouldn't be re-applying the entire material for each entry, instead just check which programs
 			// changed, and apply only those + the modified constant buffers and/or texture.
 
-			gRendererUtility().setPass(matInfo.material, 0);
-			gRendererUtility().draw(entry.mesh, entry.mesh->getProperties().getSubMesh(0));
+			entry.material->render(entry.mesh, entry.texture, mSamplerState, entry.tint, entry.worldTransform, 
+				invViewportSize, entry.additionalData);
 		}
 	}
-
-	GUIManagerCore::MaterialInfo::MaterialInfo(const SPtr<MaterialCore>& material)
-		:material(material)
-	{
-		textureParam = material->getParamTexture("mainTexture");
-		samplerParam = material->getParamSamplerState("mainTexSamp");
-		tintParam = material->getParamColor("tint");
-		invViewportWidthParam = material->getParamFloat("invViewportWidth");
-		invViewportHeightParam = material->getParamFloat("invViewportHeight");
-		worldTransformParam = material->getParamMat4("worldTransform");
-	}
 }

+ 2 - 1
Source/BansheeEngine/Source/BsGUIScrollBar.cpp

@@ -68,8 +68,9 @@ namespace BansheeEngine
 		return mImageSprite->getNumRenderElements();
 	}
 
-	const SpriteMaterialInfo& GUIScrollBar::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIScrollBar::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
+		*material = mImageSprite->getMaterial(renderElementIdx);
 		return mImageSprite->getMaterialInfo(renderElementIdx);
 	}
 

+ 2 - 1
Source/BansheeEngine/Source/BsGUISliderHandle.cpp

@@ -74,8 +74,9 @@ namespace BansheeEngine
 		return mImageSprite->getNumRenderElements();
 	}
 
-	const SpriteMaterialInfo& GUISliderHandle::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUISliderHandle::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
+		*material = mImageSprite->getMaterial(renderElementIdx);
 		return mImageSprite->getMaterialInfo(renderElementIdx);
 	}
 

+ 2 - 1
Source/BansheeEngine/Source/BsGUITexture.cpp

@@ -121,8 +121,9 @@ namespace BansheeEngine
 		return mImageSprite->getNumRenderElements();
 	}
 
-	const SpriteMaterialInfo& GUITexture::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUITexture::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
+		*material = mImageSprite->getMaterial(renderElementIdx);
 		return mImageSprite->getMaterialInfo(renderElementIdx);
 	}
 

+ 1 - 1
Source/BansheeEngine/Source/BsGUIViewport.cpp

@@ -46,7 +46,7 @@ namespace BansheeEngine
 		return 0;
 	}
 
-	const SpriteMaterialInfo& GUIViewport::_getMaterial(UINT32 renderElementIdx) const
+	const SpriteMaterialInfo& GUIViewport::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
 	{
 		BS_EXCEPT(InternalErrorException, "This element has no render element so no material can be retrieved.");
 		static SpriteMaterialInfo dummy;

+ 6 - 1
Source/BansheeEngine/Source/BsImageSprite.cpp

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsImageSprite.h"
 #include "BsSpriteTexture.h"
+#include "BsSpriteManager.h"
 #include "BsTexture.h"
 
 namespace BansheeEngine
@@ -60,7 +61,11 @@ namespace BansheeEngine
 			matInfo.groupId = groupId;
 			matInfo.texture = tex;
 			matInfo.tint = desc.color;
-			matInfo.type = desc.transparent ? SpriteMaterial::ImageAlpha : SpriteMaterial::Image;
+
+			if (desc.transparent)
+				renderElem.material = SpriteManager::instance().getImageTransparentMaterial();
+			else
+				renderElem.material = SpriteManager::instance().getImageOpaqueMaterial();
 
 			texPage++;
 		}

+ 5 - 26
Source/BansheeEngine/Source/BsSprite.cpp

@@ -2,7 +2,6 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsTextSprite.h"
 #include "BsVector2.h"
-#include "BsTexture.h"
 #include "BsPlane.h"
 #include "BsMeshUtility.h"
 
@@ -37,6 +36,11 @@ namespace BansheeEngine
 		return mCachedRenderElements.at(renderElementIdx).matInfo;
 	}
 
+	SpriteMaterial* Sprite::getMaterial(UINT32 renderElementIdx) const
+	{
+		return mCachedRenderElements.at(renderElementIdx).material;
+	}
+
 	UINT32 Sprite::getNumQuads(UINT32 renderElementIdx) const
 	{
 		return mCachedRenderElements.at(renderElementIdx).numQuads;
@@ -332,29 +336,4 @@ namespace BansheeEngine
 
 		MeshUtility::clip2D(vertices, uv, numTris, vertStride, clipPlanes, writeCallback);
 	}
-
-	UINT64 SpriteMaterialInfo::generateHash() const
-	{
-		UINT64 textureId = 0;
-		if (texture.isLoaded())
-			textureId = texture->getInternalID();
-
-		size_t hash = 0;
-		hash_combine(hash, groupId);
-		hash_combine(hash, type);
-		hash_combine(hash, textureId);
-		hash_combine(hash, tint);
-
-		return (UINT64)hash;
-	}
-
-	bool operator==(const SpriteMaterialInfo& lhs, const SpriteMaterialInfo& rhs)
-	{
-		return lhs.groupId == rhs.groupId && lhs.texture == rhs.texture && lhs.type == rhs.type && lhs.tint == rhs.tint;
-	}
-
-	bool operator!=(const SpriteMaterialInfo& lhs, const SpriteMaterialInfo& rhs)
-	{
-		return !(lhs == rhs);
-	}
 }

+ 33 - 0
Source/BansheeEngine/Source/BsSpriteManager.cpp

@@ -0,0 +1,33 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsSpriteManager.h"
+#include "BsSpriteMaterials.h"
+
+namespace BansheeEngine
+{
+	SpriteManager::SpriteManager()
+	{
+		SpriteMaterial* imageTransparentMat = registerMaterial<SpriteImageTransparentMaterial>();
+		SpriteMaterial* imageOpaqueMat = registerMaterial<SpriteImageOpaqueMaterial>();
+		SpriteMaterial* textMat = registerMaterial<SpriteTextMaterial>();
+
+		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageTransparent] = imageTransparentMat->getId();
+		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageOpaque] = imageOpaqueMat->getId();
+		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Text] = textMat->getId();
+	}
+
+	SpriteManager::~SpriteManager()
+	{
+		for(auto& entry : mMaterials)
+			bs_delete(entry.second);
+	}
+
+	SpriteMaterial* SpriteManager::getMaterial(UINT32 id) const
+	{
+		auto iterFind = mMaterials.find(id);
+		if (iterFind != mMaterials.end())
+			return iterFind->second;
+
+		return nullptr;
+	}
+}

+ 73 - 0
Source/BansheeEngine/Source/BsSpriteMaterial.cpp

@@ -0,0 +1,73 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsSprite.h"
+#include "BsMaterial.h"
+#include "BsTexture.h"
+#include "BsMesh.h"
+#include "BsRendererUtility.h"
+#include "BsCoreThread.h"
+
+namespace BansheeEngine
+{
+	SpriteMaterial::SpriteMaterial(UINT32 id, const HMaterial& material)
+		:mId(id)
+	{
+		mMaterial = material->getCore();
+		std::atomic_thread_fence(std::memory_order_release);
+
+		gCoreAccessor().queueCommand(std::bind(&SpriteMaterial::initialize, this));
+	}
+
+	SpriteMaterial::~SpriteMaterial()
+	{
+		gCoreAccessor().queueCommand(std::bind(&SpriteMaterial::destroy, mMaterial));
+	}
+
+	void SpriteMaterial::initialize()
+	{
+		// Make sure that mMaterial assignment completes on the previous thread before continuing
+		std::atomic_thread_fence(std::memory_order_acquire);
+
+		mTextureParam = mMaterial->getParamTexture("mainTexture");
+		mSamplerParam = mMaterial->getParamSamplerState("mainTexSamp");
+		mTintParam = mMaterial->getParamColor("tint");
+		mInvViewportWidthParam = mMaterial->getParamFloat("invViewportWidth");
+		mInvViewportHeightParam = mMaterial->getParamFloat("invViewportHeight");
+		mWorldTransformParam = mMaterial->getParamMat4("worldTransform");
+	}
+
+	void SpriteMaterial::destroy(const SPtr<MaterialCore>& material)
+	{
+		// Do nothing, we just need to make sure the material pointer's last reference is lost while on the core thread
+	}
+
+	UINT64 SpriteMaterial::getMergeHash(const SpriteMaterialInfo& info) const
+	{
+		UINT64 textureId = 0;
+		if (info.texture.isLoaded())
+			textureId = info.texture->getInternalID();
+
+		size_t hash = 0;
+		hash_combine(hash, info.groupId);
+		hash_combine(hash, getId());
+		hash_combine(hash, textureId);
+		hash_combine(hash, info.tint);
+
+		return (UINT64)hash;
+	}
+
+	void SpriteMaterial::render(const SPtr<MeshCoreBase>& mesh, const SPtr<TextureCore>& texture,
+		const SPtr<SamplerStateCore>& sampler, const Color& tint, const Matrix4& worldTransform,
+		const Vector2& invViewportSize, SpriteMaterialExtraInfo* additionalData) const
+	{
+		mTextureParam.set(texture);
+		mSamplerParam.set(sampler);
+		mTintParam.set(tint);
+		mInvViewportWidthParam.set(invViewportSize.x);
+		mInvViewportHeightParam.set(invViewportSize.y);
+		mWorldTransformParam.set(worldTransform);
+
+		gRendererUtility().setPass(mMaterial, 0);
+		gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
+	}
+}

+ 19 - 0
Source/BansheeEngine/Source/BsSpriteMaterials.cpp

@@ -0,0 +1,19 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsSpriteMaterials.h"
+#include "BsBuiltinResources.h"
+
+namespace BansheeEngine
+{
+	SpriteImageTransparentMaterial::SpriteImageTransparentMaterial()
+		:SpriteMaterial(0, BuiltinResources::instance().createSpriteImageMaterial())
+	{ }
+
+	SpriteImageOpaqueMaterial::SpriteImageOpaqueMaterial()
+		: SpriteMaterial(1, BuiltinResources::instance().createSpriteNonAlphaImageMaterial())
+	{ }
+
+	SpriteTextMaterial::SpriteTextMaterial()
+		: SpriteMaterial(2, BuiltinResources::instance().createSpriteTextMaterial())
+	{ }
+}

+ 3 - 1
Source/BansheeEngine/Source/BsTextSprite.cpp

@@ -3,6 +3,7 @@
 #include "BsTextSprite.h"
 #include "BsTextData.h"
 #include "BsVector2.h"
+#include "BsSpriteManager.h"
 
 namespace BansheeEngine
 {
@@ -55,7 +56,8 @@ namespace BansheeEngine
 				matInfo.groupId = groupId;
 				matInfo.texture = tex;
 				matInfo.tint = desc.color;
-				matInfo.type = SpriteMaterial::Text;
+
+				cachedElem.material = SpriteManager::instance().getTextMaterial();
 
 				texPage++;
 			}