Ver Fonte

More work on GUICanvas line rendering

BearishSun há 9 anos atrás
pai
commit
b03b62bf18

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

@@ -87,6 +87,10 @@ namespace BansheeEngine
 		/** Returns the size of a single element in the buffer, of the provided format, in bytes. */
 		static UINT32 getFormatSize(GpuBufferFormat format);
 
+		/** @copydoc HardwareBufferManager::createGpuBuffer */
+		static SPtr<GpuBuffer> create(UINT32 elementCount, UINT32 elementSize, GpuBufferType type,
+			GpuBufferFormat format, GpuBufferUsage usage, bool randomGpuWrite = false, bool useCounter = false);
+
 	protected:
 		friend class HardwareBufferManager;
 
@@ -191,6 +195,10 @@ namespace BansheeEngine
 		 */
 		static void releaseView(GpuBufferView* view);
 
+		/** @copydoc HardwareBufferManager::createGpuBuffer */
+		static SPtr<GpuBufferCore> create(UINT32 elementCount, UINT32 elementSize, GpuBufferType type,
+			GpuBufferFormat format, GpuBufferUsage usage, bool randomGpuWrite = false, bool useCounter = false);
+
 	protected:
 		GpuBufferCore(UINT32 elementCount, UINT32 elementSize, GpuBufferType type, GpuBufferFormat format,
 			GpuBufferUsage usage, bool randomGpuWrite = false, bool useCounter = false);

+ 4 - 1
Source/BansheeCore/Include/BsHardwareBufferManager.h

@@ -62,7 +62,9 @@ namespace BansheeEngine
 		 * type of data and can be used for various purposes. See "GpuBufferType" for explanation of different buffer types.
 		 *
 		 * @param[in]	elementCount  	Number of elements in the buffer. 
-		 * @param[in]	elementSize   	Size of each individual element in the buffer, in bytes.
+		 * @param[in]	elementSize   	Size of each individual element in the buffer, in bytes. Only needed if using
+		 *								non-standard buffer. If using standard buffer element size is calculated from
+		 *								format.
 		 * @param[in]	type		  	Type of the buffer.
 		 * @param[in]	format			Format if the data in the buffer. Only relevant for standard buffers.
 		 * @param[in]	usage		  	Usage that tells the hardware how will be buffer be used. 
@@ -119,6 +121,7 @@ namespace BansheeEngine
 		friend class VertexDeclaration;
 		friend class GpuParamBlockBuffer;
 		friend class GpuBuffer;
+		friend class GpuBufferCore;
 
 		/** @copydoc createVertexBuffer */
 		virtual SPtr<VertexBufferCore> createVertexBufferInternal(UINT32 vertexSize, UINT32 numVerts, GpuBufferUsage usage, 

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

@@ -89,6 +89,13 @@ namespace BansheeEngine
 		}
 	}
 
+	SPtr<GpuBufferCore> GpuBufferCore::create(UINT32 elementCount, UINT32 elementSize, GpuBufferType type,
+		GpuBufferFormat format, GpuBufferUsage usage, bool randomGpuWrite, bool useCounter)
+	{
+		return HardwareBufferCoreManager::instance().createGpuBufferInternal(elementCount, elementSize, type, format,
+			usage, randomGpuWrite, useCounter);
+	}
+
 	GpuBuffer::GpuBuffer(UINT32 elementCount, UINT32 elementSize, GpuBufferType type, GpuBufferFormat format, 
 		GpuBufferUsage usage, bool randomGpuWrite, bool useCounter)
 		:mProperties(elementCount, elementSize, type, format, usage, randomGpuWrite, useCounter)
@@ -156,4 +163,11 @@ namespace BansheeEngine
 
 		return lookup[(UINT32)format];
 	}
+
+	SPtr<GpuBuffer> GpuBuffer::create(UINT32 elementCount, UINT32 elementSize, GpuBufferType type,
+		GpuBufferFormat format, GpuBufferUsage usage, bool randomGpuWrite, bool useCounter)
+	{
+		return HardwareBufferManager::instance().createGpuBuffer(elementCount, elementSize, type, format,
+			usage, randomGpuWrite, useCounter);
+	}
 }

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

@@ -617,7 +617,7 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 		UINT32 samplerArraySize = mNumSamplerStates * sizeof(SPtr<SamplerStateCore>);
 
 		UINT32 totalSize = loadStoreSurfacesSize + paramBufferSize + textureArraySize + loadStoreTextureArraySize 
-			+ samplerArraySize;
+			+ bufferArraySize + samplerArraySize;
 
 		UINT32 textureInfoOffset = 0;
 		UINT32 paramBufferOffset = textureInfoOffset + loadStoreSurfacesSize;

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

@@ -211,7 +211,7 @@ namespace BansheeEngine
 
 		if(constantBufferDesc.Type != D3D_CT_CBUFFER && constantBufferDesc.Type != D3D_CT_TBUFFER)
 		{
-			LOGWRN("D3D11 HLSL parsing: Unsupported constant buffer type, skipping. Type: " + toString(constantBufferDesc.Type));
+			// Not supported (most likely a constant buffer used internally by DX)
 			return;
 		}
 

+ 8 - 3
Source/BansheeEngine/Include/BsBuiltinResources.h

@@ -84,15 +84,18 @@ namespace BansheeEngine
 		/**	Returns a shader used for rendering only a diffuse texture. */
 		HShader getDiffuseShader() const { return mShaderDiffuse; }
 
-		/**	Creates material used for textual sprite rendering (for example text in GUI). */
+		/**	Creates a material used for textual sprite rendering (for example text in GUI). */
 		HMaterial createSpriteTextMaterial() const;
 
-		/**	Creates material used for image sprite rendering (for example images in GUI). */
+		/**	Creates a material used for image sprite rendering (for example images in GUI). */
 		HMaterial createSpriteImageMaterial() const;
 
-		/**	Creates material used for non-transparent image sprite rendering (for example images in GUI). */
+		/**	Creates a material used for non-transparent image sprite rendering (for example images in GUI). */
 		HMaterial createSpriteNonAlphaImageMaterial() const;
 
+		/** Creates a material used for antialiased line rendering (for example curve rendering in GUI). */
+		HMaterial createSpriteLineMaterial() const;
+
 		/**	Retrieves one of the builtin meshes. */
 		HMesh getMesh(BuiltinMesh mesh) const;
 
@@ -165,6 +168,7 @@ namespace BansheeEngine
 		HShader mShaderSpriteText;
 		HShader mShaderSpriteImage;
 		HShader mShaderSpriteNonAlphaImage;
+		HShader mShaderSpriteLine;
 		HShader mShaderDiffuse;
 
 		SPtr<ResourceManifest> mResourceManifest;
@@ -292,6 +296,7 @@ namespace BansheeEngine
 		static const WString ShaderSpriteTextFile;
 		static const WString ShaderSpriteImageAlphaFile;
 		static const WString ShaderSpriteImageNoAlphaFile;
+		static const WString ShaderSpriteLineFile;
 		static const WString ShaderDiffuseFile;
 
 		static const WString MeshSphereFile;

+ 11 - 8
Source/BansheeEngine/Include/BsGUICanvas.h

@@ -46,9 +46,10 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	a		Starting point of the line, relative to the canvas origin (top-left).
 		 * @param[in]	b		Ending point of the line, relative to the canvas origin (top-left).
+		 * @param[in]	width	Width of the line, in pixels.
 		 * @param[in]	color	Color of the line.
 		 */
-		void drawLine(const Vector2I& a, const Vector2I& b, const Color& color = Color::White);
+		void drawLine(const Vector2I& a, const Vector2I& b, float width = 1.0f, const Color& color = Color::White);
 
 		/** 
 		 * Draws multiple lines following the path by the provided vertices. First vertex connects to the second vertex,
@@ -56,9 +57,10 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	vertices	Points to use for drawing the line. Must have at least two elements. All points are 
 		 *							relative to the canvas origin (top-left).
+		 * @param[in]	width		Width of the line, in pixels.
 		 * @param[in]	color		Color of the line.
 		 */
-		void drawPolyLine(const Vector<Vector2I>& vertices, const Color& color = Color::White);
+		void drawPolyLine(const Vector<Vector2I>& vertices, float width = 1.0f, const Color& color = Color::White);
 
 		/** 
 		 * Draws a quad with a the provided texture displayed.
@@ -137,18 +139,19 @@ namespace BansheeEngine
 			UINT32 renderElemEnd;
 			UINT32 dataId;
 
-			union
-			{
-				ImageSprite* imageSprite;
-				TextureScaleMode scaleMode;
-			};
-
 			union
 			{
 				UINT32 vertexStart;
 				UINT32 numVertices;
 				mutable UINT32 clippedVertexStart;
 				mutable UINT32 clippedNumVertices;
+				float lineWidth;
+			};
+
+			union
+			{
+				ImageSprite* imageSprite;
+				TextureScaleMode scaleMode;
 			};
 
 			union

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

@@ -77,7 +77,7 @@ namespace BansheeEngine
 			SpriteMaterial* material;
 			Color tint;
 			Matrix4 worldTransform;
-			SpriteMaterialExtraInfo* additionalData;
+			SPtr<SpriteMaterialExtraInfo> additionalData;
 		};
 
 		/**	Container for a GUI widget. */

+ 40 - 37
Source/BansheeEngine/Include/BsShapeMeshes2D.h

@@ -20,7 +20,7 @@ namespace BansheeEngine
 		 * Fills the mesh data with vertices representing a quad (2 triangles).
 		 *
 		 * @param[in]	area			Area in which to draw the quad.
-		 * @param[in]	meshData		Mesh data that will be populated.
+		 * @param[out]	meshData		Mesh data that will be populated.
 		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
 		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
 		 * 							
@@ -37,11 +37,11 @@ namespace BansheeEngine
 		/**
 		 * Fills the mesh data with vertices representing a per-pixel line.
 		 *
-		 * @param	a				Start point of the line.
-		 * @param	b				End point of the line.
-		 * @param	meshData		Mesh data that will be populated.
-		 * @param	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
-		 * @param	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
+		 * @param[in]	a				Start point of the line.
+		 * @param[in]	b				End point of the line.
+		 * @param[out]	meshData		Mesh data that will be populated.
+		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
+		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
 		 * 							
 		 * @note	
 		 * Provided MeshData must have some specific elements at least:
@@ -57,13 +57,13 @@ namespace BansheeEngine
 		/**
 		 * Fills the mesh data with vertices representing a line of specific width as a quad. 
 		 *
-		 * @param	a				Start point of the line.
-		 * @param	b				End point of the line.
-		 * @param	width			Width of the line.
-		 * @param	color			Color of the line.
-		 * @param	meshData		Mesh data that will be populated by this method.
-		 * @param	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
-		 * @param	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
+		 * @param[in]	a				Start point of the line.
+		 * @param[in]	b				End point of the line.
+		 * @param[in]	width			Width of the line.
+		 * @param[in]	color			Color of the line.
+		 * @param[out]	meshData		Mesh data that will be populated by this method.
+		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
+		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
 		 * 							
 		 * @note	
 		 * Provided MeshData must have some specific elements at least:
@@ -80,10 +80,10 @@ namespace BansheeEngine
 		/**
 		 * Fills the mesh data with vertices representing per-pixel lines.
 		 *
-		 * @param	linePoints		A list of start and end points for the lines. Must be a multiple of 2.
-		 * @param	meshData		Mesh data that will be populated.
-		 * @param	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
-		 * @param	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
+		 * @param[in]	linePoints		A list of start and end points for the lines. Must be a multiple of 2.
+		 * @param[out]	meshData		Mesh data that will be populated.
+		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
+		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
 		 * 							
 		 * @note	
 		 * Provided MeshData must have some specific elements at least:
@@ -99,12 +99,12 @@ namespace BansheeEngine
 		/**
 		 * Fills the mesh data with vertices representing a polyline of specific width as a set of quads. 
 		 *
-		 * @param	linePoints		A list of start and end points for the lines. Must be a multiple of 2.
-		 * @param	width			Width of the line.
-		 * @param	color			Color of the line.
-		 * @param	meshData		Mesh data that will be populated by this method.
-		 * @param	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
-		 * @param	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
+		 * @param[in]	linePoints		A list of start and end points for the lines.
+		 * @param[in]	width			Width of the line.
+		 * @param[in]	color			Color of the line.
+		 * @param[out]	meshData		Mesh data that will be populated by this method.
+		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
+		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
 		 * 							
 		 * @note	
 		 * Provided MeshData must have some specific elements at least:
@@ -118,6 +118,23 @@ namespace BansheeEngine
 		static void quadLineList(const Vector<Vector2>& linePoints, float width, 
 			const Color& color, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset);
 
+		/**
+		 * Fills the provided buffers with vertices representing a polyline of specific width as a set of quads 
+		 * (triangle list).
+		 *
+		 * @param[in]	linePoints		A list of start and end points for the lines.
+		 * @param[in]	numPoints		Number of points in the @p linePoints buffer.
+		 * @param[in]	width			Width of the line.
+		 * @param[out]	outVertices		Pre-allocated buffer for the vertices, of size ((numLines * 2) + 2) * @p vertexStride
+		 *								if @p indexed is true, or (numLines * 6) * @p vertexStride if false.
+		 * @param[in]	vertexStride	Distance between two vertices in the output buffer. Must be at least sizeof(Vector2).
+		 * @param[in]	indexed			If true there will be ((numLines * 2) + 2) vertices generated, assuming an index
+		 *								buffer will be used for rendering. If false then (numLines * 6) vertices will be
+		 *								generated.
+		 */
+		static void quadLineList(const Vector2* linePoints, UINT32 numPoints, float width, UINT8* outVertices, 
+			UINT32 vertexStride, bool indexed);
+
 		static const UINT32 NUM_VERTICES_AA_LINE;
 		static const UINT32 NUM_INDICES_AA_LINE;
 	protected:
@@ -148,20 +165,6 @@ namespace BansheeEngine
 		 */
 		static void pixelSolidPolygon(const Vector<Vector2>& points, UINT8* outVertices,
 			UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset);
-
-		/**
-		 * Fills the provided buffers with vertices representing a pixel-wide polygon border.
-		 *
-		 * @param[in]	points			Points defining the polygon. First point is assumed to be the start and end point.
-		 * @param[out]	outVertices		Output buffer that will store the vertex position data.
-		 * @param[out]	outColors		Output buffer that will store the vertex color data.
-		 * @param[in]	vertexOffset	Offset in number of vertices from the start of the buffer to start writing at.
-		 * @param[in]	vertexStride	Size of a single vertex, in bytes. (Same for both position and color buffer)
-		 * @param[out]	outIndices		Output buffer that will store the index data. Indices are 32bit.
-		 * @param[in]	indexOffset 	Offset in number of indices from the start of the buffer to start writing at.
-		 */
-		static void pixelWirePolygon(const Vector<Vector2>& points, UINT8* outVertices, UINT8* outColors,
-			UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset);
 	};
 
 	/** @} */

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

@@ -21,6 +21,7 @@ namespace BansheeEngine
 			ImageTransparent,
 			ImageOpaque,
 			Text,
+			Line,
 			Count // Keep at end
 		};
 
@@ -40,6 +41,10 @@ namespace BansheeEngine
 		SpriteMaterial* getTextMaterial() const
 			{ return getMaterial(builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Text]); }
 
+		/** Returns the material used for rendering antialiased lines. */
+		SpriteMaterial* getLineMaterial() const
+			{ return getMaterial(builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Line]); }
+
 		/** Returns a sprite material with the specified ID. Returns null if one cannot be found. */
 		SpriteMaterial* getMaterial(UINT32 id) const;
 

+ 29 - 4
Source/BansheeEngine/Include/BsSpriteMaterial.h

@@ -15,19 +15,44 @@ namespace BansheeEngine
 
 	/** Extension structure that can be used by SpriteMaterial%s to access specialized data. */
 	struct SpriteMaterialExtraInfo
-	{ };
+	{
+		virtual ~SpriteMaterialExtraInfo() { }
+
+		/** Creates a new deep copy of the object. */
+		virtual SPtr<SpriteMaterialExtraInfo> clone() const
+		{
+			return bs_shared_ptr_new<SpriteMaterialExtraInfo>();
+		}
+	};
 
 	/** Contains information for initializing a sprite material. */
 	struct SpriteMaterialInfo
 	{
 		SpriteMaterialInfo()
-			:groupId(0), additionalData(nullptr)
+			:groupId(0)
 		{ }
 
+		/** 
+		 * Creates a new deep copy of the object. This is different from standard copy constructor which will just reference
+		 * the original "additionalData" field, while this will copy it.
+		 */
+		SpriteMaterialInfo clone() const
+		{
+			SpriteMaterialInfo info;
+			info.groupId = groupId;
+			info.texture = texture;
+			info.tint = tint;
+
+			if(additionalData != nullptr)
+				info.additionalData = additionalData->clone();
+
+			return info;
+		}
+
 		UINT64 groupId;
 		HTexture texture;
 		Color tint;
-		SpriteMaterialExtraInfo* additionalData;
+		SPtr<SpriteMaterialExtraInfo> additionalData;
 	};
 
 	/** Interfaced implemented by materials used for rendering sprites. This is expected to be used as a singleton. */
@@ -71,7 +96,7 @@ namespace BansheeEngine
 		 */
 		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;
+			const Vector2& invViewportSize, const SPtr<SpriteMaterialExtraInfo>& additionalData) const;
 
 	protected:
 		/** Perform initialization of core-thread specific objects. */

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

@@ -32,5 +32,43 @@ namespace BansheeEngine
 		SpriteTextMaterial();
 	};
 
+	/** Sprite material used for antialiased lines. */
+	class BS_EXPORT SpriteLineMaterial : public SpriteMaterial
+	{
+	public:
+		SpriteLineMaterial();
+
+		/** @copydoc SpriteMaterial::getMergeHash */
+		UINT64 getMergeHash(const SpriteMaterialInfo& info) const override;
+
+		/** @copydoc SpriteMaterial::merge */
+		void merge(SpriteMaterialInfo& mergeInto, const SpriteMaterialInfo& mergeFrom) const override;
+
+		/** @copydoc SpriteMaterial::render */
+		void render(const SPtr<MeshCoreBase>& mesh, const SPtr<TextureCore>& texture,
+			const SPtr<SamplerStateCore>& sampler, const Color& tint, const Matrix4& worldTransform,
+			const Vector2& invViewportSize, const SPtr<SpriteMaterialExtraInfo>& additionalData) const override;
+	};
+
+	/** Extra data required by the SpriteLineMaterial. */
+	struct SpriteMaterialLineInfo : SpriteMaterialExtraInfo
+	{
+		/** @copydoc SpriteMaterialLineInfo::clone() */
+		SPtr<SpriteMaterialExtraInfo> clone() const override
+		{
+			SPtr<SpriteMaterialLineInfo> info = bs_shared_ptr_new<SpriteMaterialLineInfo>();
+			info->points = points;
+			info->width = width;
+
+			return info;
+		}
+
+		Vector<Vector2> points;
+		float width;
+
+		// Core thread only
+		SPtr<GpuBufferCore> pointBuffer;
+	};
+
 	/** @} */
 }

+ 7 - 0
Source/BansheeEngine/Source/BsBuiltinResources.cpp

@@ -159,6 +159,7 @@ namespace BansheeEngine
 	const WString BuiltinResources::ShaderSpriteTextFile = L"SpriteText.bsl";
 	const WString BuiltinResources::ShaderSpriteImageAlphaFile = L"SpriteImageAlpha.bsl";
 	const WString BuiltinResources::ShaderSpriteImageNoAlphaFile = L"SpriteImageNoAlpha.bsl";
+	const WString BuiltinResources::ShaderSpriteLineFile = L"SpriteLine.bsl";
 	const WString BuiltinResources::ShaderDiffuseFile = L"Diffuse.bsl";
 
 	/************************************************************************/
@@ -242,6 +243,7 @@ namespace BansheeEngine
 		mShaderSpriteText = getShader(ShaderSpriteTextFile);
 		mShaderSpriteImage = getShader(ShaderSpriteImageAlphaFile);
 		mShaderSpriteNonAlphaImage = getShader(ShaderSpriteImageNoAlphaFile);
+		mShaderSpriteLine = getShader(ShaderSpriteLineFile);
 		mShaderDiffuse = getShader(ShaderDiffuseFile);
 
 		SPtr<PixelData> dummyPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
@@ -1117,6 +1119,11 @@ namespace BansheeEngine
 		return Material::create(mShaderSpriteNonAlphaImage);
 	}
 
+	HMaterial BuiltinResources::createSpriteLineMaterial() const
+	{
+		return Material::create(mShaderSpriteLine);
+	}
+
 	void BuiltinResourcesHelper::importAssets(const Path& inputFolder, const Path& outputFolder, const SPtr<ResourceManifest>& manifest)
 	{
 		if (!FileSystem::exists(inputFolder))

+ 45 - 30
Source/BansheeEngine/Source/BsGUICanvas.cpp

@@ -7,6 +7,7 @@
 #include "BsGUITexture.h"
 #include "BsShapeMeshes2D.h"
 #include "BsSpriteManager.h"
+#include "BsSpriteMaterials.h"
 #include "BsException.h"
 
 namespace BansheeEngine
@@ -38,31 +39,12 @@ namespace BansheeEngine
 		return new (bs_alloc<GUICanvas>()) GUICanvas(getStyleName<GUICanvas>(styleName), GUIDimensions::create());
 	}
 
-	void GUICanvas::drawLine(const Vector2I& a, const Vector2I& b, const Color& color)
+	void GUICanvas::drawLine(const Vector2I& a, const Vector2I& b, float width, const Color& color)
 	{
-		mElements.push_back(CanvasElement());
-		CanvasElement& element = mElements.back();
-
-		element.type = CanvasElementType::Line;
-		element.color = color;
-		element.dataId = (UINT32)mTriangleElementData.size();
-		element.vertexStart = (UINT32)mVertexData.size();
-		element.numVertices = 2;
-
-		// TODO - Add actual triangle line data here
-		mVertexData.push_back(Vector2((float)a.x, (float)a.y));
-		mVertexData.push_back(Vector2((float)b.x, (float)b.y));
-
-		mTriangleElementData.push_back(TriangleElementData());
-		TriangleElementData& elemData = mTriangleElementData.back();
-		elemData.matInfo.groupId = 0;
-		elemData.matInfo.tint = color;
-
-		mForceTriangleBuild = true;
-		_markContentAsDirty();
+		drawPolyLine({ a, b }, width, color);
 	}
 
-	void GUICanvas::drawPolyLine(const Vector<Vector2I>& vertices, const Color& color)
+	void GUICanvas::drawPolyLine(const Vector<Vector2I>& vertices, float width, const Color& color)
 	{
 		if(vertices.size() < 2)
 		{
@@ -78,16 +60,24 @@ namespace BansheeEngine
 		element.dataId = (UINT32)mTriangleElementData.size();
 		element.vertexStart = (UINT32)mVertexData.size();
 		element.numVertices = (UINT32)vertices.size();
-
-		// TODO - Add actual triangle line data here
-		for (auto& vertex : vertices)
-			mVertexData.push_back(Vector2((float)vertex.x, (float)vertex.y));
+		element.lineWidth = width;
 
 		mTriangleElementData.push_back(TriangleElementData());
 		TriangleElementData& elemData = mTriangleElementData.back();
 		elemData.matInfo.groupId = 0;
 		elemData.matInfo.tint = color;
 
+		SPtr<SpriteMaterialLineInfo> lineInfo = bs_shared_ptr_new<SpriteMaterialLineInfo>();
+		lineInfo->width = width;
+
+		for (auto& vertex : vertices)
+		{
+			Vector2 point = Vector2((float)vertex.x, (float)vertex.y);
+
+			mVertexData.push_back(point);
+			lineInfo->points.push_back(point);
+		}
+
 		mForceTriangleBuild = true;
 		_markContentAsDirty();
 	}
@@ -124,6 +114,7 @@ namespace BansheeEngine
 		element.dataId = (UINT32)mTriangleElementData.size();
 		element.vertexStart = (UINT32)mVertexData.size();
 		element.numVertices = (UINT32)(vertices.size() - 2) * 3;
+		element.lineWidth = 0.0f; // Not used
 
 		// Convert strip to list
 		for(UINT32 i = 2; i < (UINT32)vertices.size(); i++)
@@ -158,6 +149,7 @@ namespace BansheeEngine
 		element.dataId = (UINT32)mTriangleElementData.size();
 		element.vertexStart = (UINT32)mVertexData.size();
 		element.numVertices = (UINT32)vertices.size();
+		element.lineWidth = 0.0f; // Not used
 
 		for (auto& vertex : vertices)
 			mVertexData.push_back(Vector2((float)vertex.x, (float)vertex.y));
@@ -219,7 +211,7 @@ namespace BansheeEngine
 		switch (element.type)
 		{
 		case CanvasElementType::Line:
-			*material = nullptr; // TODO - Assign line material and additionalData of .matInfo
+			*material = SpriteManager::instance().getLineMaterial();
 			return mTriangleElementData[element.dataId].matInfo;
 		case CanvasElementType::Image:
 			*material = element.imageSprite->getMaterial(0);
@@ -424,8 +416,28 @@ namespace BansheeEngine
 	{
 		assert(element.type == CanvasElementType::Triangle || element.type == CanvasElementType::Line);
 
-		UINT8* inputVertices = (UINT8*)&mVertexData[element.vertexStart];
-		UINT32 numTris = element.numVertices / 3;
+		UINT8* verticesToClip;
+		UINT32 trianglesToClip;
+		bool freeVertices;
+
+		if (element.type == CanvasElementType::Triangle)
+		{
+			verticesToClip = (UINT8*)&mVertexData[element.vertexStart];
+			trianglesToClip = element.numVertices / 3;
+			freeVertices = false;
+		}
+		else
+		{
+			UINT32 numLines = element.numVertices - 1;
+
+			verticesToClip = (UINT8*)bs_stack_alloc(sizeof(Vector2) * (numLines * 6));
+			trianglesToClip = numLines * 2;
+			freeVertices = true;
+
+			const Vector2* linePoints = &mVertexData[element.vertexStart];
+			ShapeMeshes2D::quadLineList(linePoints, element.numVertices, element.lineWidth, verticesToClip,
+				sizeof(Vector2), false);
+		}
 
 		element.clippedVertexStart = (UINT32)mClippedVertices.size();
 		element.clippedNumVertices = 0;
@@ -437,7 +449,10 @@ namespace BansheeEngine
 			element.clippedNumVertices += count;
 		};
 
-		ImageSprite::clipTrianglesToRect(inputVertices, nullptr, numTris, sizeof(Vector2), clipRect, writeCallback);
+		ImageSprite::clipTrianglesToRect(verticesToClip, nullptr, trianglesToClip, sizeof(Vector2), clipRect, writeCallback);
+
+		if(freeVertices)
+			bs_stack_free(verticesToClip);
 	}
 
 	void GUICanvas::buildAllTriangleElementsIfDirty(const Vector2& offset, const Rect2I& clipRect) const

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

@@ -571,7 +571,7 @@ namespace BansheeEngine
 						foundGroup->minDepth = elemDepth;
 						foundGroup->bounds = tfrmedBounds;
 						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
-						foundGroup->matInfo = matInfo;
+						foundGroup->matInfo = matInfo.clone();
 						foundGroup->material = spriteMaterial;
 
 						guiElem->_getMeshSize(renderElemIdx, foundGroup->numVertices, foundGroup->numIndices);

+ 90 - 62
Source/BansheeEngine/Source/BsShapeMeshes2D.cpp

@@ -74,9 +74,10 @@ namespace BansheeEngine
 	void ShapeMeshes2D::quadLineList(const Vector<Vector2>& linePoints, float width, const Color& color,
 		const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset)
 	{
-		assert(linePoints.size() >= 2 && linePoints.size() % 2 == 0);
+		UINT32 numPoints = (UINT32)linePoints.size();
+		assert(numPoints >= 2);
 
-		UINT32 numLines = (UINT32)linePoints.size() / 2;
+		UINT32 numLines = (UINT32)linePoints.size() - 1;
 		assert((vertexOffset + (numLines * 2 + 2)) <= meshData->getNumVertices());
 		assert((indexOffset + (numLines * 6)) <= meshData->getNumIndices());
 
@@ -84,8 +85,45 @@ namespace BansheeEngine
 		UINT8* outVertices = vertexOffset + meshData->getElementData(VES_POSITION);
 		UINT8* outColors = vertexOffset + meshData->getElementData(VES_COLOR);
 
+		UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
+		quadLineList(&linePoints[0], numPoints, width, outVertices, vertexStride, true);
+
 		RGBA colorValue = color.getAsRGBA();
 
+		// Colors and indices
+		for(UINT32 i = 0; i < numLines; i++)
+		{
+			memcpy(outColors, &colorValue, sizeof(colorValue));
+			outColors += vertexStride;
+
+			memcpy(outColors, &colorValue, sizeof(colorValue));
+			outColors += vertexStride;
+
+			UINT32 idxStart = i * 6;
+			outIndices[idxStart + 0] = vertexOffset + idxStart + 0;
+			outIndices[idxStart + 1] = vertexOffset + idxStart + 1;
+			outIndices[idxStart + 2] = vertexOffset + idxStart + 2;
+
+			outIndices[idxStart + 3] = vertexOffset + idxStart + 1;
+			outIndices[idxStart + 4] = vertexOffset + idxStart + 3;
+			outIndices[idxStart + 5] = vertexOffset + idxStart + 2;
+		}
+
+		memcpy(outColors, &colorValue, sizeof(colorValue));
+		outColors += vertexStride;
+
+		memcpy(outColors, &colorValue, sizeof(colorValue));
+		outColors += vertexStride;
+	}
+
+	void ShapeMeshes2D::quadLineList(const Vector2* linePoints, UINT32 numPoints, float width, UINT8* outVertices,
+		UINT32 vertexStride, bool indexed)
+	{
+		assert(numPoints >= 2);
+		UINT32 numLines = numPoints - 1;
+
+		Vector2 prevPoints[2];
+
 		// Start segment
 		{
 			Vector2 a = linePoints[0];
@@ -97,19 +135,19 @@ namespace BansheeEngine
 			// Flip 90 degrees
 			Vector2 normal(diff.y, -diff.x);
 
-			Vector2 lineA = a + normal * width;
-			Vector2 lineB = a - normal * width;
+			prevPoints[0] = a - normal * width;
+			prevPoints[1] = a + normal * width;
 
-			memcpy(outVertices, &lineA, sizeof(lineA));
-			outVertices += vertexOffset;
+			memcpy(outVertices, &prevPoints[0], sizeof(prevPoints[0]));
+			outVertices += vertexStride;
 
-			memcpy(outVertices, &lineB, sizeof(lineB));
-			outVertices += vertexOffset;
+			memcpy(outVertices, &prevPoints[1], sizeof(prevPoints[1]));
+			outVertices += vertexStride;
 		}
 
 		// Middle segments
 		{
-			for(UINT32 i = 1; i < numLines; i++)
+			for (UINT32 i = 1; i < numLines; i++)
 			{
 				Vector2 a = linePoints[i - 1];
 				Vector2 b = linePoints[i];
@@ -125,32 +163,48 @@ namespace BansheeEngine
 				Vector2 normalPrev(diffPrev.y, -diffPrev.x);
 				Vector2 normalNext(diffNext.y, -diffNext.x);
 
-				const float sign[] = { 1.0f, -1.0f };
-				for(UINT32 j = 0; j < 2; j++)
+				Vector2 curPoints[2];
+
+				const float sign[] = { -1.0f, 1.0f };
+				for (UINT32 j = 0; j < 2; j++)
 				{
 					Vector2 linePrevPoint = a + normalPrev * width * sign[j];
 					Line2 linePrev(linePrevPoint, diffPrev);
-					
+
 					Vector2 lineNextPoint = b + normalNext * width * sign[j];
 					Line2 lineNext(lineNextPoint, diffNext);
 
 					auto intersect = linePrev.intersects(lineNext);
-					Vector2 intersectPoint;
-					if(intersect.second != 0.0f) // Not parallel
-						intersectPoint = linePrev.getPoint(intersect.second);
+					if (intersect.second != 0.0f) // Not parallel
+						curPoints[j] = linePrev.getPoint(intersect.second);
 					else
-						intersectPoint = lineNextPoint;
+						curPoints[j] = lineNextPoint;
+
+					memcpy(outVertices, &curPoints[j], sizeof(curPoints[j]));
+					outVertices += vertexStride;
+				}
+
+				if (!indexed)
+				{
+					memcpy(outVertices, &prevPoints[1], sizeof(prevPoints[1]));
+					outVertices += vertexStride;
 
-					memcpy(outVertices, &intersectPoint, sizeof(intersectPoint));
-					outVertices += vertexOffset;
+					memcpy(outVertices, &curPoints[1], sizeof(curPoints[1]));
+					outVertices += vertexStride;
+
+					memcpy(outVertices, &curPoints[0], sizeof(curPoints[0]));
+					outVertices += vertexStride;
+
+					prevPoints[0] = curPoints[0];
+					prevPoints[1] = curPoints[1];
 				}
 			}
 		}
 
 		// End segment
 		{
-			Vector2 a = linePoints[linePoints.size() - 2];
-			Vector2 b = linePoints[linePoints.size() - 1];
+			Vector2 a = linePoints[numPoints - 2];
+			Vector2 b = linePoints[numPoints - 1];
 
 			Vector2 diff = b - a;
 			diff.normalize();
@@ -158,53 +212,27 @@ namespace BansheeEngine
 			// Flip 90 degrees
 			Vector2 normal(diff.y, -diff.x);
 
-			Vector2 lineA = a + normal * width;
-			Vector2 lineB = a - normal * width;
-
-			memcpy(outVertices, &lineA, sizeof(lineA));
-			outVertices += vertexOffset;
-
-			memcpy(outVertices, &lineB, sizeof(lineB));
-			outVertices += vertexOffset;
-		}
-
-		// Colors and indices
-		for(UINT32 i = 0; i < numLines; i++)
-		{
-			memcpy(outColors, &colorValue, sizeof(colorValue));
-			outColors += vertexOffset;
-
-			memcpy(outColors, &colorValue, sizeof(colorValue));
-			outColors += vertexOffset;
+			Vector2 curPoints[2];
+			curPoints[0] = a - normal * width;
+			curPoints[1] = a + normal * width;
 
-			UINT32 idxStart = i * 6;
-			outIndices[idxStart + 0] = vertexOffset + idxStart + 0;
-			outIndices[idxStart + 1] = vertexOffset + idxStart + 1;
-			outIndices[idxStart + 2] = vertexOffset + idxStart + 2;
+			memcpy(outVertices, &curPoints[0], sizeof(curPoints[0]));
+			outVertices += vertexStride;
 
-			outIndices[idxStart + 3] = vertexOffset + idxStart + 0;
-			outIndices[idxStart + 4] = vertexOffset + idxStart + 2;
-			outIndices[idxStart + 5] = vertexOffset + idxStart + 3;
-		}
+			memcpy(outVertices, &curPoints[1], sizeof(curPoints[1]));
+			outVertices += vertexStride;
 
-		memcpy(outColors, &colorValue, sizeof(colorValue));
-		outColors += vertexOffset;
+			if (!indexed)
+			{
+				memcpy(outVertices, &prevPoints[1], sizeof(prevPoints[1]));
+				outVertices += vertexStride;
 
-		memcpy(outColors, &colorValue, sizeof(colorValue));
-		outColors += vertexOffset;
-	}
+				memcpy(outVertices, &curPoints[1], sizeof(curPoints[1]));
+				outVertices += vertexStride;
 
-	void ShapeMeshes2D::pixelWirePolygon(const Vector<Vector2>& points, UINT8* outVertices, UINT8* outColors,
-		UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset)
-	{
-		INT32 numPoints = (INT32)points.size();
-		UINT32 curVertOffset = vertexOffset;
-		UINT32 curIdxOffset = indexOffset;
-		for (INT32 i = 0, j = numPoints - 1; i < numPoints; j = i++)
-		{
-			pixelLine(points[j], points[i], outVertices, curVertOffset, vertexStride, outIndices, curIdxOffset);
-			curVertOffset += 2;
-			curIdxOffset += 2;
+				memcpy(outVertices, &curPoints[0], sizeof(curPoints[0]));
+				outVertices += vertexStride;
+			}
 		}
 	}
 

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

@@ -10,10 +10,12 @@ namespace BansheeEngine
 		SpriteMaterial* imageTransparentMat = registerMaterial<SpriteImageTransparentMaterial>();
 		SpriteMaterial* imageOpaqueMat = registerMaterial<SpriteImageOpaqueMaterial>();
 		SpriteMaterial* textMat = registerMaterial<SpriteTextMaterial>();
+		SpriteMaterial* lineMat = registerMaterial<SpriteLineMaterial>();
 
 		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageTransparent] = imageTransparentMat->getId();
 		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::ImageOpaque] = imageOpaqueMat->getId();
 		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Text] = textMat->getId();
+		builtinMaterialIds[(UINT32)BuiltinSpriteMaterialType::Line] = lineMat->getId();
 	}
 
 	SpriteManager::~SpriteManager()

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

@@ -58,7 +58,7 @@ namespace BansheeEngine
 
 	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
+		const Vector2& invViewportSize, const SPtr<SpriteMaterialExtraInfo>& additionalData) const
 	{
 		mTextureParam.set(texture);
 		mSamplerParam.set(sampler);

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

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsSpriteMaterials.h"
 #include "BsBuiltinResources.h"
+#include "BsGpuBuffer.h"
+#include "BsMaterial.h"
 
 namespace BansheeEngine
 {
@@ -16,4 +18,49 @@ namespace BansheeEngine
 	SpriteTextMaterial::SpriteTextMaterial()
 		: SpriteMaterial(2, BuiltinResources::instance().createSpriteTextMaterial())
 	{ }
+
+	SpriteLineMaterial::SpriteLineMaterial()
+		: SpriteMaterial(3, BuiltinResources::instance().createSpriteLineMaterial())
+	{
+		
+	}
+
+	UINT64 SpriteLineMaterial::getMergeHash(const SpriteMaterialInfo& info) const
+	{
+		size_t hash = SpriteMaterial::getMergeHash(info);
+
+		SPtr<SpriteMaterialLineInfo> extraInfo = std::static_pointer_cast<SpriteMaterialLineInfo>(info.additionalData);
+		hash_combine(hash, extraInfo->width);
+
+		return (UINT64)hash;
+	}
+
+	void SpriteLineMaterial::merge(SpriteMaterialInfo& mergeInto, const SpriteMaterialInfo& mergeFrom) const
+	{
+		SPtr<SpriteMaterialLineInfo> extraInfoDest = std::static_pointer_cast<SpriteMaterialLineInfo>(mergeInto.additionalData);
+		SPtr<SpriteMaterialLineInfo> extraInfoSrc = std::static_pointer_cast<SpriteMaterialLineInfo>(mergeFrom.additionalData);
+
+		extraInfoDest->points.insert(extraInfoDest->points.end(), extraInfoSrc->points.begin(), extraInfoSrc->points.end());
+	}
+
+	void SpriteLineMaterial::render(const SPtr<MeshCoreBase>& mesh, const SPtr<TextureCore>& texture,
+		const SPtr<SamplerStateCore>& sampler, const Color& tint, const Matrix4& worldTransform,
+		const Vector2& invViewportSize, const SPtr<SpriteMaterialExtraInfo>& additionalData) const
+	{
+		SPtr<SpriteMaterialLineInfo> lineInfo = std::static_pointer_cast<SpriteMaterialLineInfo>(additionalData);
+		if(lineInfo->pointBuffer == nullptr)
+		{
+			UINT32 numPoints = (UINT32)lineInfo->points.size();
+			SPtr<GpuBufferCore> pointBuffer = GpuBufferCore::create(numPoints, 0, GBT_STANDARD, BF_32X2F, GBU_STATIC);
+
+			pointBuffer->writeData(0, numPoints * sizeof(Vector2), &lineInfo->points[0], BufferWriteType::Discard);
+		}
+		
+		mMaterial->setBuffer("linePoints", lineInfo->pointBuffer);
+		mMaterial->setFloat("lineWidth", lineInfo->width);
+
+		// TODO - Create a filter texture
+
+		SpriteMaterial::render(mesh, texture, sampler, tint, worldTransform, invViewportSize, additionalData);
+	}
 }