Browse Source

Added UV coordinates and tangent frame to built-in mesh types

BearishSun 8 years ago
parent
commit
d611dbe10a

+ 22 - 18
Source/BansheeCore/Include/BsMeshUtility.h

@@ -50,19 +50,21 @@ namespace bs
 		/**
 		 * Calculates per-vertex tangents and bitangents based on the provided vertices, uv coordinates and indices.
 		 *
-		 * @param[in]	vertices	Set of vertices containing vertex positions.
-		 * @param[in]	normals		Set of normals to use when calculating tangents. Must the the same length as the number
-		 *							of vertices.
-		 * @param[in]	uv			Set of UV coordinates to use when calculating tangents. Must the the same length as the
-		 *							number of vertices.
-		 * @param[in]	indices		Set of indices containing indexes into vertex array for each triangle.
-		 * @param[in]	numVertices	Number of vertices in the @p vertices, @p normals and @p uv arrays.
-		 * @param[in]	numIndices	Number of indices in the @p indices array. Must be a multiple of three.
-		 * @param[out]	tangents	Pre-allocated buffer that will contain the calculated tangents. Must be the same size
-		 *							as the vertex array.
-		 * @param[out]	bitangents	Pre-allocated buffer that will contain the calculated bitangents. Must be the same size
-		 *							as the vertex array.
-		 * @param[in]	indexSize	Size of a single index in the indices array, in bytes.
+		 * @param[in]	vertices		Set of vertices containing vertex positions.
+		 * @param[in]	normals			Set of normals to use when calculating tangents. Must the the same length as the
+		 *								number of vertices.
+		 * @param[in]	uv				Set of UV coordinates to use when calculating tangents. Must the the same length as
+		 *								the number of vertices.
+		 * @param[in]	indices			Set of indices containing indexes into vertex array for each triangle.
+		 * @param[in]	numVertices		Number of vertices in the @p vertices, @p normals and @p uv arrays.
+		 * @param[in]	numIndices		Number of indices in the @p indices array. Must be a multiple of three.
+		 * @param[out]	tangents		Pre-allocated buffer that will contain the calculated tangents. Must be the same
+		 *								size as the vertex array.
+		 * @param[out]	bitangents		Pre-allocated buffer that will contain the calculated bitangents. Must be the same
+		 *								size as the vertex array.
+		 * @param[in]	indexSize		Size of a single index in the indices array, in bytes.
+		 * @param[in]	vertexStride	Number of bytes to advance the @p vertices, @p normals and @p uv arrays with each
+		 *								vertex. If set to zero them each array is advanced according to its own size.
 		 *
 		 * @note	
 		 * Vertices should be split before calling this method if there are any discontinuities. (for example a vertex on a
@@ -70,7 +72,7 @@ namespace bs
 		 * valid.)
 		 */
 		static void calculateTangents(Vector3* vertices, Vector3* normals, Vector2* uv, UINT8* indices, UINT32 numVertices, 
-			UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize = 4);
+			UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize = 4, UINT32 vertexStride = 0);
 
 		/**
 		 * Calculates per-vertex tangent space (normal, tangent, bitangent) based on the provided vertices, uv coordinates
@@ -140,9 +142,10 @@ namespace bs
 		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
 		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
 		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
+		 * @param[in]	inStride		Distance between two entries in the @p source buffer, in bytes.
+		 * @param[in]	outStride		Distance between two entries in the @p destination buffer, in bytes.
 		 */
-		static void packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride);
+		static void packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride);
 
 		/** 
 		 * Encodes normals from 32-bit float format into 4D 8-bit packed format. 
@@ -150,9 +153,10 @@ namespace bs
 		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
 		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
 		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
+		 * @param[in]	inStride		Distance between two entries in the @p source buffer, in bytes.
+		 * @param[in]	outStride		Distance between two entries in the @p destination buffer, in bytes.
 		 */
-		static void packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride);
+		static void packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride);
 
 		/** 
 		 * Decodes normals from 4D 8-bit packed format into a 32-bit float format. 

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

@@ -218,6 +218,9 @@ namespace bs
 		/**	Creates a vertex descriptor from a vertex layout enum. */
 		static SPtr<VertexDataDesc> vertexLayoutVertexDesc(VertexLayout type);
 
+		/** Converts a generic mesh data into mesh data format expected by the renderer. */
+		static SPtr<MeshData> convert(const SPtr<MeshData>& meshData);
+
 	private:
 		friend class ct::Renderer;
 

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

@@ -64,6 +64,9 @@ namespace bs
 		/**	Returns the vertex element at the specified index. */
 		const VertexElement& getElement(UINT32 idx) const { return mVertexElements[idx]; }
 
+		/**	Returns the vertex element with the specified semantic. */
+		const VertexElement* getElement(VertexElementSemantic semantic, UINT32 semanticIdx = 0, UINT32 streamIdx = 0) const;
+
 		/**	Creates a list of vertex elements from internal data. */
 		List<VertexElement> createElements() const;
 

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

@@ -440,7 +440,7 @@ namespace bs
 							Vector3 normal = tempNormals[i] / accumulatedWeight[i];
 							normal /= 2.0f; // Accumulated normal is in range [-2, 2] but our normal packing method assumes [-1, 1] range
 
-							MeshUtility::packNormals(&normal, (UINT8*)destNrm, 1, stride);
+							MeshUtility::packNormals(&normal, (UINT8*)destNrm, 1, sizeof(Vector3), stride);
 							destNrm->w = (UINT8)(std::min(1.0f, accumulatedWeight[i]) * 255.999f);
 						}
 						else

+ 42 - 26
Source/BansheeCore/Source/BsMeshUtility.cpp

@@ -764,9 +764,15 @@ namespace bs
 	}
 
 	void MeshUtility::calculateTangents(Vector3* vertices, Vector3* normals, Vector2* uv, UINT8* indices, UINT32 numVertices,
-		UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize)
+		UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize, UINT32 vertexStride)
 	{
 		UINT32 numFaces = numIndices / 3;
+		UINT32 vec2Stride = vertexStride == 0 ? sizeof(Vector2) : vertexStride;
+		UINT32 vec3Stride = vertexStride == 0 ? sizeof(Vector3) : vertexStride;
+
+		UINT8* positionBytes = (UINT8*)vertices;
+		UINT8* normalBytes = (UINT8*)normals;
+		UINT8* uvBytes = (UINT8*)uv;
 
 		Vector3* faceTangents = bs_newN<Vector3>(numFaces);
 		Vector3* faceBitangents = bs_newN<Vector3>(numFaces);
@@ -777,13 +783,13 @@ namespace bs
 			memcpy(&triangle[1], indices + (i * 3 + 1) * indexSize, indexSize);
 			memcpy(&triangle[2], indices + (i * 3 + 2) * indexSize, indexSize);
 
-			Vector3 p0 = vertices[triangle[0]];
-			Vector3 p1 = vertices[triangle[1]];
-			Vector3 p2 = vertices[triangle[2]];
+			Vector3 p0 = *(Vector3*)&positionBytes[triangle[0] * vec3Stride];
+			Vector3 p1 = *(Vector3*)&positionBytes[triangle[1] * vec3Stride];
+			Vector3 p2 = *(Vector3*)&positionBytes[triangle[2] * vec3Stride];
 
-			Vector2 uv0 = uv[triangle[0]];
-			Vector2 uv1 = uv[triangle[1]];
-			Vector2 uv2 = uv[triangle[2]];
+			Vector2 uv0 = *(Vector2*)&uvBytes[triangle[0] * vec2Stride];
+			Vector2 uv1 = *(Vector2*)&uvBytes[triangle[1] * vec2Stride];
+			Vector2 uv2 = *(Vector2*)&uvBytes[triangle[2] * vec2Stride];
 
 			Vector3 q0 = p1 - p0;
 			Vector3 q1 = p2 - p0;
@@ -831,14 +837,16 @@ namespace bs
 			tangents[i].normalize();
 			bitangents[i].normalize();
 
+			Vector3 normal = *(Vector3*)&normalBytes[i * vec3Stride];
+
 			// Orthonormalize
-			float dot0 = normals[i].dot(tangents[i]);
-			tangents[i] -= dot0*normals[i];
+			float dot0 = normal.dot(tangents[i]);
+			tangents[i] -= dot0*normal;
 			tangents[i].normalize();
 
 			float dot1 = tangents[i].dot(bitangents[i]);
-			dot0 = normals[i].dot(bitangents[i]);
-			bitangents[i] -= dot0*normals[i] + dot1*tangents[i];
+			dot0 = normal.dot(bitangents[i]);
+			bitangents[i] -= dot0*normal + dot1*tangents[i];
 			bitangents[i].normalize();
 		}
 
@@ -869,33 +877,41 @@ namespace bs
 		clipper.clip(vertices, uvs, numTris, vertexStride, clipPlanes, writeCallback);
 	}
 
-	void MeshUtility::packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride)
+	void MeshUtility::packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride)
 	{
-		UINT8* ptr = destination;
+		UINT8* srcPtr = (UINT8*)source;
+		UINT8* dstPtr = destination;
 		for (UINT32 i = 0; i < count; i++)
 		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
-			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
-			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
+			Vector3 src = *(Vector3*)srcPtr;
+
+			PackedNormal& packed = *(PackedNormal*)dstPtr;
+			packed.x = Math::clamp((int)(src.x * 127.5f + 127.5f), 0, 255);
+			packed.y = Math::clamp((int)(src.y * 127.5f + 127.5f), 0, 255);
+			packed.z = Math::clamp((int)(src.z * 127.5f + 127.5f), 0, 255);
 			packed.w = 128;
 
-			ptr += stride;
+			srcPtr += inStride;
+			dstPtr += outStride;
 		}
 	}
 
-	void MeshUtility::packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride)
+	void MeshUtility::packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride)
 	{
-		UINT8* ptr = destination;
+		UINT8* srcPtr = (UINT8*)source;
+		UINT8* dstPtr = destination;
 		for (UINT32 i = 0; i < count; i++)
 		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
-			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
-			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
-			packed.w = Math::clamp((int)(source[i].w * 127.5f + 127.5f), 0, 255);
+			Vector4 src = *(Vector4*)srcPtr;
+			PackedNormal& packed = *(PackedNormal*)dstPtr;
 
-			ptr += stride;
+			packed.x = Math::clamp((int)(src.x * 127.5f + 127.5f), 0, 255);
+			packed.y = Math::clamp((int)(src.y * 127.5f + 127.5f), 0, 255);
+			packed.z = Math::clamp((int)(src.z * 127.5f + 127.5f), 0, 255);
+			packed.w = Math::clamp((int)(src.w * 127.5f + 127.5f), 0, 255);
+
+			srcPtr += inStride;
+			dstPtr += outStride;
 		}
 	}
 

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

@@ -73,7 +73,7 @@ namespace bs
 		UINT8* normalDst = mMeshData->getElementData(VES_NORMAL);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		MeshUtility::packNormals(buffer, normalDst, numElements, stride);
+		MeshUtility::packNormals(buffer, normalDst, numElements, sizeof(Vector3), stride);
 	}
 
 	void RendererMeshData::getTangents(Vector4* buffer, UINT32 size)
@@ -101,7 +101,7 @@ namespace bs
 		UINT8* tangentDst = mMeshData->getElementData(VES_TANGENT);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		MeshUtility::packNormals(buffer, tangentDst, numElements, stride);
+		MeshUtility::packNormals(buffer, tangentDst, numElements, sizeof(Vector4), stride);
 	}
 
 	void RendererMeshData::getColors(Color* buffer, UINT32 size)
@@ -365,7 +365,7 @@ namespace bs
 			vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD, 1);
 
 		if ((intType & (INT32)VertexLayout::Color) != 0)
-			vertexDesc->addVertElem(VET_FLOAT4, VES_COLOR);
+			vertexDesc->addVertElem(VET_COLOR, VES_COLOR);
 
 		if ((intType & (INT32)VertexLayout::BoneWeights) != 0)
 		{
@@ -375,4 +375,161 @@ namespace bs
 
 		return vertexDesc;
 	}
+
+	SPtr<MeshData> RendererMeshData::convert(const SPtr<MeshData>& meshData)
+	{
+		// Note: Only converting between packed normals/tangents for now
+		SPtr<VertexDataDesc> vertexDesc = meshData->getVertexDesc();
+
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 inputStride = vertexDesc->getVertexStride();
+
+		INT32 type = 0;
+
+		const VertexElement* posElem = vertexDesc->getElement(VES_POSITION);
+		if (posElem != nullptr && posElem->getType() == VET_FLOAT3)
+			type = (INT32)VertexLayout::Position;
+
+		const VertexElement* normalElem = vertexDesc->getElement(VES_NORMAL);
+		bool packNormals = false;
+		if(normalElem != nullptr)
+		{
+			if (normalElem->getType() == VET_FLOAT3)
+			{
+				packNormals = true;
+				type |= (INT32)VertexLayout::Normal;
+			}
+			else if(normalElem->getType() == VET_UBYTE4_NORM)
+				type |= (INT32)VertexLayout::Normal;
+		}
+
+		const VertexElement* tanElem = vertexDesc->getElement(VES_TANGENT);
+		bool packTangents = false;
+		if (tanElem != nullptr)
+		{
+			if (tanElem->getType() == VET_FLOAT4)
+			{
+				packTangents = true;
+				type |= (INT32)VertexLayout::Tangent;
+			}
+			else if (normalElem->getType() == VET_UBYTE4_NORM)
+				type |= (INT32)VertexLayout::Tangent;
+		}
+
+		const VertexElement* uv0Elem = vertexDesc->getElement(VES_TEXCOORD, 0);
+		if (uv0Elem != nullptr && uv0Elem->getType() == VET_FLOAT2)
+			type |= (INT32)VertexLayout::UV0;
+
+		const VertexElement* uv1Elem = vertexDesc->getElement(VES_TEXCOORD, 1);
+		if (uv1Elem != nullptr && uv1Elem->getType() == VET_FLOAT2)
+			type |= (INT32)VertexLayout::UV1;
+
+		const VertexElement* colorElem = vertexDesc->getElement(VES_COLOR);
+		if (colorElem != nullptr && colorElem->getType() == VET_COLOR)
+			type |= (INT32)VertexLayout::Color;
+
+		const VertexElement* blendIndicesElem = vertexDesc->getElement(VES_BLEND_INDICES);
+		const VertexElement* blendWeightsElem = vertexDesc->getElement(VES_BLEND_WEIGHTS);
+		if (blendIndicesElem != nullptr && blendIndicesElem->getType() == VET_UBYTE4 && 
+			blendWeightsElem != nullptr && blendWeightsElem->getType() == VET_FLOAT4)
+			type |= (INT32)VertexLayout::BoneWeights;
+
+		SPtr<RendererMeshData> rendererMeshData = create(numVertices, numIndices, (VertexLayout)type, 
+			meshData->getIndexType());
+
+		SPtr<MeshData> output = rendererMeshData->mMeshData;
+		SPtr<VertexDataDesc> outputVertexDesc = output->getVertexDesc();
+		UINT32 outputStride = outputVertexDesc->getVertexStride();
+
+		if((type & (INT32)VertexLayout::Position) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_POSITION);
+			UINT8* outData = output->getElementData(VES_POSITION);
+			for(UINT32 i = 0; i < numVertices; i++)
+				memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(Vector3));
+		}
+
+		if ((type & (INT32)VertexLayout::Normal) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_NORMAL);
+			UINT8* outData = output->getElementData(VES_NORMAL);
+
+			if (packNormals)
+				MeshUtility::packNormals((Vector3*)inData, outData, numVertices, inputStride, outputStride);
+			else
+			{
+				for (UINT32 i = 0; i < numVertices; i++)
+					memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(UINT32));
+			}
+		}
+
+		if ((type & (INT32)VertexLayout::Tangent) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_TANGENT);
+			UINT8* outData = output->getElementData(VES_TANGENT);
+
+			if (packTangents)
+				MeshUtility::packNormals((Vector4*)inData, outData, numVertices, inputStride, outputStride);
+			else
+			{
+				for (UINT32 i = 0; i < numVertices; i++)
+					memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(UINT32));
+			}
+		}
+
+		if ((type & (INT32)VertexLayout::UV0) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_TEXCOORD, 0);
+			UINT8* outData = output->getElementData(VES_TEXCOORD, 0);
+			for (UINT32 i = 0; i < numVertices; i++)
+				memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(Vector2));
+		}
+
+		if ((type & (INT32)VertexLayout::UV1) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_TEXCOORD, 1);
+			UINT8* outData = output->getElementData(VES_TEXCOORD, 1);
+			for (UINT32 i = 0; i < numVertices; i++)
+				memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(Vector2));
+		}
+
+		if ((type & (INT32)VertexLayout::Color) != 0)
+		{
+			UINT8* inData = meshData->getElementData(VES_COLOR, 0);
+			UINT8* outData = output->getElementData(VES_COLOR, 0);
+			for (UINT32 i = 0; i < numVertices; i++)
+				memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(UINT32));
+		}
+
+		if ((type & (INT32)VertexLayout::BoneWeights) != 0)
+		{
+			{
+				UINT8* inData = meshData->getElementData(VES_BLEND_INDICES, 0);
+				UINT8* outData = output->getElementData(VES_BLEND_INDICES, 0);
+				for (UINT32 i = 0; i < numVertices; i++)
+					memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(UINT32));
+			}
+
+			{
+				UINT8* inData = meshData->getElementData(VES_BLEND_WEIGHTS, 0);
+				UINT8* outData = output->getElementData(VES_BLEND_WEIGHTS, 0);
+				for (UINT32 i = 0; i < numVertices; i++)
+					memcpy(outData + i * outputStride, inData + i * inputStride, sizeof(Vector4));
+			}
+		}
+
+		if(meshData->getIndexType() == IT_32BIT)
+		{
+			UINT32* dst = output->getIndices32();
+			memcpy(dst, meshData->getIndices32(), numIndices * sizeof(UINT32));
+		}
+		else
+		{
+			UINT16* dst = output->getIndices16();
+			memcpy(dst, meshData->getIndices16(), numIndices * sizeof(UINT16));
+		}
+
+		return output;
+	}
 }

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

@@ -162,6 +162,20 @@ namespace bs
 		return streamOffset;
 	}
 
+	const VertexElement* VertexDataDesc::getElement(VertexElementSemantic semantic, UINT32 semanticIdx, UINT32 streamIdx) const
+	{
+		auto findIter = std::find_if(mVertexElements.begin(), mVertexElements.end(),
+									 [semantic, semanticIdx, streamIdx](const VertexElement& x)
+		{
+			return x.getSemantic() == semantic && x.getSemanticIdx() == semanticIdx && x.getStreamIdx() == streamIdx;
+		});
+
+		if (findIter != mVertexElements.end())
+			return &(*findIter);
+
+		return nullptr;
+	}
+
 	void VertexDataDesc::clearIfItExists(VertexElementType type, VertexElementSemantic semantic, UINT32 semanticIdx, UINT32 streamIdx)
 	{
 		auto findIter = std::find_if(mVertexElements.begin(), mVertexElements.end(), 

+ 54 - 9
Source/BansheeEngine/Include/BsShapeMeshes3D.h

@@ -49,6 +49,9 @@ namespace bs
 		 *	Vector3 VES_NORMAL
 		 * 	32bit index buffer
 		 * 	Enough space for 24 vertices and 36 indices
+		 * Optionally it may also have:
+		 *  Vector2 VES_TEXCOORD
+		 *  Vector4 VES_TANGENT
 		 * @note
 		 * Primitives are output in the form of a triangle list.
 		 */
@@ -93,6 +96,9 @@ namespace bs
 		 * 	32bit index buffer
 		 * 	Enough space for 20 * (4 * (3 ^ quality)) vertices 
 		 *	Enough space for 20 * (4 * (3 ^ quality)) indices
+		 * Optionally it may also have:
+		 *  Vector2 VES_TEXCOORD
+		 *  Vector4 VES_TANGENT
 		 * @note
 		 * Primitives are output in the form of a triangle list.
 		 */
@@ -148,6 +154,9 @@ namespace bs
 		 * 	32bit index buffer
 		 * 	Enough space for ((quality + 1) * 5 + 1) * 2 vertices 
 		 *	Enough space for (((quality + 1) * 5 - 1) * 6) indices
+		 * Optionally it may also have:
+		 *  Vector2 VES_TEXCOORD
+		 *  Vector4 VES_TANGENT
 		 * @note
 		 * Primitives are output in the form of a triangle list.
 		 */
@@ -276,6 +285,9 @@ namespace bs
 		 * 	32bit index buffer
 		 * 	Enough space for ((quality + 1) * 4) * 3 + 1 vertices 
 		 *	Enough space for (((quality + 1) * 4) * 6) indices
+		 * Optionally it may also have:
+		 *  Vector2 VES_TEXCOORD
+		 *  Vector4 VES_TANGENT
 		 * @note
 		 * Primitives are output in the form of a triangle list.
 		 */
@@ -296,6 +308,9 @@ namespace bs
 		 *	Vector3 VES_NORMAL
 		 * 	32bit index buffer
 		 * 	Enough space for 8 vertices and 12 indices
+		 * Optionally it may also have:
+		 *  Vector2 VES_TEXCOORD
+		 *  Vector4 VES_TANGENT
 		 * @note
 		 * Primitives are output in the form of a triangle list.
 		 */
@@ -410,13 +425,15 @@ namespace bs
 		 * @param[in]	box				Box to create geometry for.
 		 * @param[out]	outVertices		Pre-allocated output buffer that will store the vertex position data.
 		 * @param[out]	outNormals		Pre-allocated output buffer that will store the vertex normal data.
+		 * @param[out]	outUV			Pre-allocated output buffer that will store the vertex UV data. Set to null if not
+		 *								required.
 		 * @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 normal buffer)
 		 * @param[out]	outIndices		Pre-allocated 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 solidAABox(const AABox& box, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride,
-			UINT32* outIndices, UINT32 indexOffset);
+		static void solidAABox(const AABox& box, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, 
+			UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset);
 
 		/**
 		 * Fills the provided buffers with position and index data representing a sphere. Use getNumElementsSphere() to
@@ -426,6 +443,8 @@ namespace bs
 		 * @param[out]	outVertices		Pre-allocated output buffer that will store the vertex position data.
 		 * @param[out]	outNormals		Pre-allocated output buffer that will store the vertex normal data. Can be null if
 		 *								normals aren't needed.
+		 * @param[out]	outUV			Pre-allocated output buffer that will store the vertex UV data. Set to null if not
+		 *								required.
 		 * @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 normal buffer)
 		 * @param[out]	outIndices		Pre-allocated output buffer that will store the index data. Indices are 32bit.
@@ -433,8 +452,8 @@ namespace bs
 		 * @param[in]	quality			Represents the level of tessellation the sphere will have. Higher level means higher
 		 *								quality but also more vertices and primitives.
 		 */
-		static void solidSphere(const Sphere& sphere, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride,
-			UINT32* outIndices, UINT32 indexOffset, UINT32 quality);
+		static void solidSphere(const Sphere& sphere, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, 
+			UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality);
 
 		/**
 		 * Fills the provided buffers with position and index data representing an outline of an arc. Use
@@ -469,6 +488,8 @@ namespace bs
 		 * @param[in]	amountAngle		Angle that the arc spans.
 		 * @param[out]	outVertices		Pre-allocated output buffer that will store the vertex position data.
 		 * @param[out]	outNormals		Pre-allocated output buffer that will store the vertex normal data.
+		 * @param[out]	outUV			Pre-allocated output buffer that will store the vertex UV data. Set to null if not
+		 *								required.	
 		 * @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 normal buffer)
 		 * @param[out]	outIndices		Pre-allocated output buffer that will store the index data. Indices are 32bit.
@@ -476,8 +497,9 @@ namespace bs
 		 * @param[in]	quality			Represents the level of tessellation the arc will have. Higher level means higher
 		 *								quality but also more vertices and primitives.
 		 */
-		static void solidArc(const Vector3& center, float radius, const Vector3& normal, Degree startAngle, Degree amountAngle,
-			UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality);
+		static void solidArc(const Vector3& center, float radius, const Vector3& normal, Degree startAngle, 
+			Degree amountAngle, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, 
+			UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality);
 
 		/**
 		 * Fills the provided buffers with position and index data representing an outline of a camera frustum. Use
@@ -509,6 +531,8 @@ namespace bs
 		 * @param[out]	outVertices		Pre-allocated output buffer that will store the vertex position data.
 		 * @param[out]	outNormals		Pre-allocated output buffer that will store the vertex normal data. Can be null if
 		 *								normals aren't needed.
+		 * @param[out]	outUV			Pre-allocated output buffer that will store the vertex UV data. Set to null if not
+		 *								required.	
 		 * @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 normal buffer)
 		 * @param[out]	outIndices		Pre-allocated output buffer that will store the index data. Indices are 32bit.
@@ -517,8 +541,8 @@ namespace bs
 		 *								quality but also more vertices and primitives.
 		 */
 		static void solidCone(const Vector3& base, const Vector3& normal, float height, float radius, Vector2 scale,
-			UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, 
-			UINT32 indexOffset, UINT32 quality);
+			UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, UINT32 vertexStride, 
+			UINT32* outIndices, UINT32 indexOffset, UINT32 quality);
 
 		/**
 		 * Fills the provided buffers with position and index data representing a wire cone. Use getNumElementsWireCone() to
@@ -548,12 +572,15 @@ namespace bs
 		 * @param[in]	area			Area covered by the quad.
 		 * @param[out]	outVertices		Pre-allocated output buffer that will store the vertex position data.
 		 * @param[out]	outNormals		Pre-allocated output buffer that will store the vertex normal data.
+		 * @param[out]	outUV			Pre-allocated output buffer that will store the vertex UV data. Set to null if not
+		 *								required.
 		 * @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 normal buffer)
 		 * @param[out]	outIndices		Pre-allocated 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 solidQuad(const Rect3& area, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset);
+		static void solidQuad(const Rect3& area, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, 
+			UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset);
 
 		/**	Calculates number of vertices and indices required for geometry of a solid axis aligned box. */
 		static void getNumElementsAABox(UINT32& numVertices, UINT32& numIndices);
@@ -721,6 +748,24 @@ namespace bs
 		 */
 		static void generateArcVertices(const Vector3& center, const Vector3& up, float radius, Degree startAngle, 
 			Degree angleAmount, Vector2 scale, UINT32 numVertices, UINT8* outvertices, UINT32 vertexOffset, UINT32 vertexStride);
+
+		/**
+		 * Calculates per-vertex tangents and bitangents based on the provided vertices, uv coordinates and indices.
+		 *
+		 * @param[in]	positions		Pointer to an array of vertex positions.
+		 * @param[in]	normals			Pointer to an array of vertex normals.
+		 * @param[in]	uv				Pointer to an array of vertex UV coordinates.
+		 * @param[in]	indices			Set of 32-bit indices containing indexes into vertex array for each triangle.
+		 * @param[in]	numVertices		Number of vertices in the @p vertices, @p normals and @p uv arrays.
+		 * @param[in]	numIndices		Number of indices in the @p indices array. Must be a multiple of three.
+		 * @param[in]	vertexStride	Number of bytes to advance the @p vertices, @p normals and @p uv arrays with each
+		 *								vertex. If set to zero them each array is advanced according to its own size.
+		 * @param[out]	tangents		Pre-allocated buffer that will contain the calculated tangents & bitangents packed
+		 *								into 4D vector where first three components are the tangent, and 4th is the sign of
+		 *								the bitangent. Must be the same length as the vertex array.
+		 */
+		static void generateTangents(UINT8* positions, UINT8* normals, UINT8* uv, UINT32* indices, UINT32 numVertices,
+									 UINT32 numIndices, UINT32 vertexStride, UINT8* tangents);
 	};
 
 	/** @} */

+ 9 - 6
Source/BansheeEngine/Source/BsBuiltinResources.cpp

@@ -27,6 +27,7 @@
 #include "BsFileSerializer.h"
 #include "BsTextureImportOptions.h"
 #include "BsBuiltinResourcesHelper.h"
+#include "BsRendererMeshData.h"
 #include "BsGUISlider.h"
 #include "BsGUIScrollBar.h"
 
@@ -1100,17 +1101,19 @@ namespace bs
 	{
 		SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
 		vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
 		vertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
+		vertexDesc->addVertElem(VET_FLOAT4, VES_TANGENT);
 		vertexDesc->addVertElem(VET_COLOR, VES_COLOR);
 
 		UINT32 boxNumVertices = 0;
 		UINT32 boxNumIndices = 0;
 		ShapeMeshes3D::getNumElementsAABox(boxNumVertices, boxNumIndices);
-		SPtr<MeshData> boxMeshData = bs_shared_ptr_new<MeshData>(boxNumVertices, boxNumIndices, vertexDesc);
+		SPtr<MeshData> boxMeshData = MeshData::create(boxNumVertices, boxNumIndices, vertexDesc);
 		AABox box(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f));
 
 		ShapeMeshes3D::solidAABox(box, boxMeshData, 0, 0);
-		SPtr<Mesh> boxMesh = Mesh::_createPtr(boxMeshData);
+		SPtr<Mesh> boxMesh = Mesh::_createPtr(RendererMeshData::convert(boxMeshData));
 
 		UINT32 sphereNumVertices = 0;
 		UINT32 sphereNumIndices = 0;
@@ -1118,7 +1121,7 @@ namespace bs
 		SPtr<MeshData> sphereMeshData = bs_shared_ptr_new<MeshData>(sphereNumVertices, sphereNumIndices, vertexDesc);
 
 		ShapeMeshes3D::solidSphere(Sphere(Vector3::ZERO, 1.0f), sphereMeshData, 0, 0, 3);
-		SPtr<Mesh> sphereMesh = Mesh::_createPtr(sphereMeshData);
+		SPtr<Mesh> sphereMesh = Mesh::_createPtr(RendererMeshData::convert(sphereMeshData));
 
 		UINT32 coneNumVertices = 0;
 		UINT32 coneNumIndices = 0;
@@ -1126,7 +1129,7 @@ namespace bs
 		SPtr<MeshData> coneMeshData = bs_shared_ptr_new<MeshData>(coneNumVertices, coneNumIndices, vertexDesc);
 
 		ShapeMeshes3D::solidCone(Vector3::ZERO, Vector3::UNIT_Y, 1.0f, 1.0f, Vector2::ONE, coneMeshData, 0, 0);
-		SPtr<Mesh> coneMesh = Mesh::_createPtr(coneMeshData);
+		SPtr<Mesh> coneMesh = Mesh::_createPtr(RendererMeshData::convert(coneMeshData));
 
 		UINT32 quadNumVertices = 8;
 		UINT32 quadNumIndices = 12;
@@ -1137,7 +1140,7 @@ namespace bs
 		std::array<float, 2> sizes = {{ 1.0f, 1.0f }};
 		Rect3 rect(Vector3::ZERO, axes, sizes);
 		ShapeMeshes3D::solidQuad(rect, quadMeshData, 0, 0);
-		SPtr<Mesh> quadMesh = Mesh::_createPtr(quadMeshData);
+		SPtr<Mesh> quadMesh = Mesh::_createPtr(RendererMeshData::convert(quadMeshData));
 
 		UINT32 discNumVertices = 0;
 		UINT32 discNumIndices = 0;
@@ -1145,7 +1148,7 @@ namespace bs
 		SPtr<MeshData> discMeshData = bs_shared_ptr_new<MeshData>(discNumVertices, discNumIndices, vertexDesc);
 
 		ShapeMeshes3D::solidDisc(Vector3::ZERO, 1.0f, Vector3::UNIT_Y, discMeshData, 0, 0);
-		SPtr<Mesh> discMesh = Mesh::_createPtr(discMeshData);
+		SPtr<Mesh> discMesh = Mesh::_createPtr(RendererMeshData::convert(discMeshData));
 
 		// Save all meshes
 		Path outputDir = mEngineMeshFolder;

+ 2 - 2
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -43,7 +43,7 @@ namespace bs { namespace ct
 			UINT8* positionData = meshData->getElementData(VES_POSITION);
 
 			Sphere localSphere(Vector3::ZERO, 1.0f);
-			ShapeMeshes3D::solidSphere(localSphere, positionData, nullptr, 0,
+			ShapeMeshes3D::solidSphere(localSphere, positionData, nullptr, nullptr, 0,
 				vertexDesc->getVertexStride(), indexData, 0, 3);
 
 			mPointLightStencilMesh = Mesh::create(meshData);
@@ -121,7 +121,7 @@ namespace bs { namespace ct
 			UINT8* positionData = meshData->getElementData(VES_POSITION);
 
 			AABox localBox(-Vector3::ONE * 1500.0f, Vector3::ONE * 1500.0f);
-			ShapeMeshes3D::solidAABox(localBox, positionData, nullptr, 0,
+			ShapeMeshes3D::solidAABox(localBox, positionData, nullptr, nullptr, 0,
 									   vertexDesc->getVertexStride(), indexData, 0);
 
 			mSkyBoxMesh = Mesh::create(meshData);

+ 291 - 37
Source/BansheeEngine/Source/BsShapeMeshes3D.cpp

@@ -3,21 +3,13 @@
 #include "BsShapeMeshes3D.h"
 #include "BsRect2.h"
 #include "BsMesh.h"
-#include "BsTime.h"
 #include "BsVector2.h"
 #include "BsQuaternion.h"
 #include "BsSphere.h"
-#include "BsMaterial.h"
 #include "BsPass.h"
-#include "BsCoreApplication.h"
-#include "BsRenderQueue.h"
-#include "BsException.h"
 #include "BsCCamera.h"
-#include "BsBuiltinResources.h"
 #include "BsVertexDataDesc.h"
-
-// DEBUG ONLY
-#include "BsDebug.h"
+#include "BsMeshUtility.h"
 
 namespace bs
 {
@@ -30,6 +22,12 @@ namespace bs
 		return buffer + stride;
 	}
 
+	inline UINT8* writeVector2(UINT8* buffer, UINT32 stride, const Vector2& value)
+	{
+		*(Vector2*)buffer = value;
+		return buffer + stride;
+	}
+
 	void ShapeMeshes3D::wireAABox(const AABox& box, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset)
 	{
 		UINT32* indexData = meshData->getIndices32();
@@ -43,14 +41,31 @@ namespace bs
 
 	void ShapeMeshes3D::solidAABox(const AABox& box, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset)
 	{
+		const SPtr<VertexDataDesc>& desc = meshData->getVertexDesc();
+
 		UINT32* indexData = meshData->getIndices32();
 		UINT8* positionData = meshData->getElementData(VES_POSITION);
 		UINT8* normalData = meshData->getElementData(VES_NORMAL);
 
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 vertexStride = desc->getVertexStride();
+
 		assert((vertexOffset + 24) <= meshData->getNumVertices());
 		assert((indexOffset + 36) <= meshData->getNumIndices());
 
-		solidAABox(box, positionData, normalData, vertexOffset, meshData->getVertexDesc()->getVertexStride(), indexData, indexOffset);
+		UINT8* uvData = nullptr;
+		if (desc->hasElement(VES_TEXCOORD))
+			uvData = meshData->getElementData(VES_TEXCOORD);
+
+		solidAABox(box, positionData, normalData, uvData, vertexOffset, vertexStride, indexData, indexOffset);
+
+		if (uvData != nullptr && desc->hasElement(VES_TANGENT))
+		{
+			UINT8* tangentData = meshData->getElementData(VES_TANGENT);
+			generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, vertexStride,
+							 tangentData);
+		}
 	}
 
 	void ShapeMeshes3D::wireSphere(const Sphere& sphere, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset, UINT32 quality)
@@ -74,20 +89,37 @@ namespace bs
 			vertexOffset + verticesPerArc * 2, indexOffset + indicesPerArc * 2, quality);
 	}
 
-	void ShapeMeshes3D::solidSphere(const Sphere& sphere, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset, UINT32 quality)
+	void ShapeMeshes3D::solidSphere(const Sphere& sphere, const SPtr<MeshData>& meshData, UINT32 vertexOffset, 
+		UINT32 indexOffset, UINT32 quality)
 	{
+		const SPtr<VertexDataDesc>& desc = meshData->getVertexDesc();
+
 		UINT32* indexData = meshData->getIndices32();
 		UINT8* positionData = meshData->getElementData(VES_POSITION);
 		UINT8* normalData = meshData->getElementData(VES_NORMAL);
 
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 vertexStride = desc->getVertexStride();
+
 		UINT32 requiredNumVertices, requiredNumIndices;
 		getNumElementsSphere(quality, requiredNumVertices, requiredNumIndices);
 
-		assert((vertexOffset + requiredNumVertices) <= meshData->getNumVertices());
-		assert((indexOffset + requiredNumIndices) <= meshData->getNumIndices());
+		assert((vertexOffset + requiredNumVertices) <= numVertices);
+		assert((indexOffset + requiredNumIndices) <= numIndices);
 
-		solidSphere(sphere, positionData, normalData, vertexOffset, 
-			meshData->getVertexDesc()->getVertexStride(), indexData, indexOffset, quality);
+		UINT8* uvData = nullptr;
+		if (desc->hasElement(VES_TEXCOORD))
+			uvData = meshData->getElementData(VES_TEXCOORD);
+
+		solidSphere(sphere, positionData, normalData, uvData, vertexOffset, vertexStride, indexData, indexOffset, quality);
+
+		if (uvData != nullptr && desc->hasElement(VES_TANGENT))
+		{
+			UINT8* tangentData = meshData->getElementData(VES_TANGENT);
+			generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, vertexStride,
+							 tangentData);
+		}
 	}
 
 	void ShapeMeshes3D::wireDisc(const Vector3& center, float radius, const Vector3& normal, const SPtr<MeshData>& meshData,
@@ -121,18 +153,35 @@ namespace bs
 	void ShapeMeshes3D::solidArc(const Vector3& center, float radius, const Vector3& normal, Degree startAngle, Degree amountAngle,
 		const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset, UINT32 quality)
 	{
+		const SPtr<VertexDataDesc>& desc = meshData->getVertexDesc();
+
 		UINT32* indexData = meshData->getIndices32();
 		UINT8* positionData = meshData->getElementData(VES_POSITION);
 		UINT8* normalData = meshData->getElementData(VES_NORMAL);
 
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 vertexStride = desc->getVertexStride();
+
 		UINT32 requiredNumVertices, requiredNumIndices;
 		getNumElementsArc(quality, requiredNumVertices, requiredNumIndices);
 
 		assert((vertexOffset + requiredNumVertices) <= meshData->getNumVertices());
 		assert((indexOffset + requiredNumIndices) <= meshData->getNumIndices());
 
-		solidArc(center, radius, normal, startAngle, amountAngle, positionData, normalData, vertexOffset,
-			meshData->getVertexDesc()->getVertexStride(), indexData, indexOffset, quality);
+		UINT8* uvData = nullptr;
+		if (desc->hasElement(VES_TEXCOORD))
+			uvData = meshData->getElementData(VES_TEXCOORD);
+
+		solidArc(center, radius, normal, startAngle, amountAngle, positionData, normalData, uvData, vertexOffset,
+			vertexStride, indexData, indexOffset, quality);
+
+		if (uvData != nullptr && desc->hasElement(VES_TANGENT))
+		{
+			UINT8* tangentData = meshData->getElementData(VES_TANGENT);
+			generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, vertexStride,
+							 tangentData);
+		}
 	}
 
 	void ShapeMeshes3D::wireFrustum(const Vector3& position, float aspect, Degree FOV, float near, float far,
@@ -150,18 +199,35 @@ namespace bs
 	void ShapeMeshes3D::solidCone(const Vector3& base, const Vector3& normal, float height, float radius, Vector2 scale,
 		const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset, UINT32 quality)
 	{
+		const SPtr<VertexDataDesc>& desc = meshData->getVertexDesc();
+
 		UINT32* indexData = meshData->getIndices32();
 		UINT8* positionData = meshData->getElementData(VES_POSITION);
 		UINT8* normalData = meshData->getElementData(VES_NORMAL);
 
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 vertexStride = desc->getVertexStride();
+
 		UINT32 requiredNumVertices, requiredNumIndices;
 		getNumElementsCone(quality, requiredNumVertices, requiredNumIndices);
 
 		assert((vertexOffset + requiredNumVertices) <= meshData->getNumVertices());
 		assert((indexOffset + requiredNumIndices) <= meshData->getNumIndices());
 
-		solidCone(base, normal, height, radius, scale, positionData, normalData, vertexOffset,
-			meshData->getVertexDesc()->getVertexStride(), indexData, indexOffset, quality);
+		UINT8* uvData = nullptr;
+		if (desc->hasElement(VES_TEXCOORD))
+			uvData = meshData->getElementData(VES_TEXCOORD);
+
+		solidCone(base, normal, height, radius, scale, positionData, normalData, uvData, vertexOffset,
+			vertexStride, indexData, indexOffset, quality);
+
+		if (uvData != nullptr && desc->hasElement(VES_TANGENT))
+		{
+			UINT8* tangentData = meshData->getElementData(VES_TANGENT);
+			generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, vertexStride,
+							 tangentData);
+		}
 	}
 
 	void ShapeMeshes3D::wireCone(const Vector3& base, const Vector3& normal, float height, float radius, Vector2 scale,
@@ -182,15 +248,32 @@ namespace bs
 
 	void ShapeMeshes3D::solidQuad(const Rect3& area, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset)
 	{
+		const SPtr<VertexDataDesc>& desc = meshData->getVertexDesc();
+
 		UINT32* indexData = meshData->getIndices32();
 		UINT8* positionData = meshData->getElementData(VES_POSITION);
 		UINT8* normalData = meshData->getElementData(VES_NORMAL);
 
-		assert((vertexOffset + 8) <= meshData->getNumVertices());
-		assert((indexOffset + 12) <= meshData->getNumIndices());
+		UINT32 numVertices = meshData->getNumVertices();
+		UINT32 numIndices = meshData->getNumIndices();
+		UINT32 vertexStride = desc->getVertexStride();
+
+		assert((vertexOffset + 8) <= numVertices);
+		assert((indexOffset + 12) <= numIndices);
 
-		solidQuad(area, positionData, normalData, vertexOffset,
+		UINT8* uvData = nullptr;
+		if (desc->hasElement(VES_TEXCOORD))
+			uvData = meshData->getElementData(VES_TEXCOORD);
+
+		solidQuad(area, positionData, normalData, uvData, vertexOffset,
 			meshData->getVertexDesc()->getVertexStride(), indexData, indexOffset);
+
+		if(uvData != nullptr && desc->hasElement(VES_TANGENT))
+		{
+			UINT8* tangentData = meshData->getElementData(VES_TANGENT);
+			generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, vertexStride, 
+				tangentData);
+		}
 	}
 
 	void ShapeMeshes3D::pixelLine(const Vector3& a, const Vector3& b, const SPtr<MeshData>& meshData, UINT32 vertexOffset, UINT32 indexOffset)
@@ -297,7 +380,7 @@ namespace bs
 	void ShapeMeshes3D::getNumElementsArc(UINT32 quality, UINT32& numVertices, UINT32& numIndices)
 	{
 		numVertices = ((quality + 1) * 5 + 1) * 2;
-		numIndices = ((quality + 1) * 5 - 1) * 6;
+		numIndices = ((quality + 1) * 5) * 6;
 	}
 
 	void ShapeMeshes3D::getNumElementsWireArc(UINT32 quality, UINT32& numVertices, UINT32& numIndices)
@@ -396,8 +479,8 @@ namespace bs
 		outIndices[23] = vertexOffset + 4;
 	}
 
-	void ShapeMeshes3D::solidAABox(const AABox& box, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride,
-		UINT32* outIndices, UINT32 indexOffset)
+	void ShapeMeshes3D::solidAABox(const AABox& box, UINT8* outVertices, UINT8* outNormals, UINT8* outUVs, 
+		UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset)
 	{
 		outVertices += (vertexOffset * vertexStride);
 
@@ -437,6 +520,7 @@ namespace bs
 		outVertices = writeVector3(outVertices, vertexStride, box.getCorner(AABox::NEAR_RIGHT_BOTTOM));
 		outVertices = writeVector3(outVertices, vertexStride, box.getCorner(AABox::NEAR_LEFT_BOTTOM));
 
+		// Normals
 		static const Vector3 faceNormals[6] = 
 		{
 			Vector3(0, 0, 1),
@@ -450,7 +534,7 @@ namespace bs
 		if (outNormals != nullptr)
 		{
 			outNormals += (vertexOffset * vertexStride);
-			for (UINT32 face = 0; face < 6; face++)
+			for (UINT32 face = 0; face < 4; face++)
 			{
 				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
 				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
@@ -459,6 +543,20 @@ namespace bs
 			}
 		}
 
+		// UV
+		if(outUVs != nullptr)
+		{
+			outUVs += (vertexOffset * vertexStride);
+			for (UINT32 face = 0; face < 6; face++)
+			{
+				outUVs = writeVector2(outUVs, vertexStride, Vector2(0.0f, 1.0f));
+				outUVs = writeVector2(outUVs, vertexStride, Vector2(1.0f, 1.0f));
+				outUVs = writeVector2(outUVs, vertexStride, Vector2(1.0f, 0.0f));
+				outUVs = writeVector2(outUVs, vertexStride, Vector2(0.0f, 0.0f));
+			}
+		}
+
+		// Indices
 		UINT32* indices = outIndices + indexOffset;
 		for (UINT32 face = 0; face < 6; face++)
 		{
@@ -473,8 +571,8 @@ namespace bs
 		}
 	}
 
-	void ShapeMeshes3D::solidSphere(const Sphere& sphere, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, 
-		UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality)
+	void ShapeMeshes3D::solidSphere(const Sphere& sphere, UINT8* outVertices, UINT8* outNormals, UINT8 *outUV,
+		UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality)
 	{
 		// Create icosahedron
 		static const float x = 0.525731112119133606f;
@@ -514,6 +612,25 @@ namespace bs
 				outVertices, outNormals, curVertOffset, vertexStride);
 		}
 
+		// Create UV if required
+		if (outUV != nullptr)
+		{
+			UINT32 numVertices = curVertOffset - vertexOffset;
+
+			outUV += (vertexOffset * vertexStride);
+			for(UINT32 i = 0; i < numVertices; i++)
+			{
+				Vector3 position = *(Vector3*)&outVertices[(vertexOffset + i) * vertexStride];
+				Vector3 normal = Vector3::normalize(position);
+
+				Vector2 uv;
+				uv.x = atan2(normal.x, normal.z) / Math::TWO_PI + 0.5f;
+				uv.y = normal.y * 0.5f + 0.5f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+			}
+		}
+
 		// Create indices
 		outIndices += indexOffset;
 
@@ -543,8 +660,9 @@ namespace bs
 		}
 	}
 
-	void ShapeMeshes3D::solidArc(const Vector3& center, float radius, const Vector3& normal, Degree startAngle, Degree amountAngle, 
-		UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality)
+	void ShapeMeshes3D::solidArc(const Vector3& center, float radius, const Vector3& normal, Degree startAngle, 
+		Degree amountAngle, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, UINT32 vertexStride, 
+		UINT32* outIndices, UINT32 indexOffset, UINT32 quality)
 	{
 		outVertices += vertexOffset * vertexStride;
 		outNormals += vertexOffset * vertexStride;
@@ -568,14 +686,53 @@ namespace bs
 
 		for (UINT32 i = 0; i < numArcVertices; i++)
 		{
-			otherSideVertices = writeVector3(otherSideVertices, vertexStride, *(Vector3*)outVertices);
-			outVertices += vertexStride;
+			otherSideVertices = writeVector3(otherSideVertices, vertexStride, *(Vector3*)&outVertices[i * vertexStride]);
 
 			outNormals = writeVector3(outNormals, vertexStride, visibleNormal);
 			otherSideNormals = writeVector3(otherSideNormals, vertexStride, -visibleNormal);
 		}
 
-		UINT32 numTriangles = numArcVertices - 1;
+		// UV
+		if(outUV != nullptr)
+		{
+			outUV += vertexOffset * vertexStride;
+
+			// Center
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.5f, 0.5f));
+
+			Vector3 arcNormal = normal;
+			Vector3 right, top;
+			arcNormal.orthogonalComplement(right, top);
+
+			for (UINT32 i = 0; i < numArcVertices; i++)
+			{
+				Vector3 vec = *(Vector3*)&outVertices[i * vertexStride];
+				Vector3 diff = Vector3::normalize(vec - center);
+
+				Vector2 uv;
+				uv.x = Vector3::dot(diff, right) * 0.5f + 0.5f;
+				uv.y = Vector3::dot(diff, top) * 0.5f + 0.5f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+			}
+
+			// Reverse
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.5f, 0.5f));
+
+			for (UINT32 i = 0; i < numArcVertices; i++)
+			{
+				Vector3 vec = *(Vector3*)&outVertices[(numArcVertices + i + 1) * vertexStride];
+				Vector3 diff = Vector3::normalize(vec - center);
+
+				Vector2 uv;
+				uv.x = Vector3::dot(diff, -right) * 0.5f + 0.5f;
+				uv.y = Vector3::dot(diff, -top) * 0.5f + 0.5f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+			}
+		}
+
+		UINT32 numTriangles = numArcVertices;
 
 		// If angle is negative the order of vertices is reversed so we need to reverse the indexes too
 		UINT32 frontSideOffset = vertexOffset + (reverseOrder ? (numArcVertices + 1) : 0);
@@ -645,11 +802,15 @@ namespace bs
 	}
 
 	void ShapeMeshes3D::solidCone(const Vector3& base, const Vector3& normal, float height, float radius, Vector2 scale,
-		UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset, UINT32 quality)
+		UINT8* outVertices, UINT8* outNormals, UINT8* outUV, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices,
+		UINT32 indexOffset, UINT32 quality)
 	{
 		outVertices += vertexOffset * vertexStride;
 		outIndices += indexOffset;
 
+		if (outUV != nullptr)
+			outUV += vertexOffset * vertexStride;
+
 		if (outNormals != nullptr)
 			outNormals += vertexOffset * vertexStride;
 
@@ -659,6 +820,28 @@ namespace bs
 		generateArcVertices(base, normal, radius, Degree(0), Degree(360), scale,
 			numArcVertices + 1, outVertices, 0, vertexStride);
 
+		if (outUV != nullptr)
+		{
+			Vector3 arcNormal = normal;
+			Vector3 right, top;
+			arcNormal.orthogonalComplement(right, top);
+
+			for (UINT32 i = 0; i < numArcVertices; i++)
+			{
+				Vector3 vec = *(Vector3*)&outVertices[i * vertexStride];
+				Vector3 diff = Vector3::normalize(vec - base);
+
+				Vector2 uv;
+				uv.x = Vector3::dot(diff, right) * 0.5f + 0.5f;
+				uv.y = Vector3::dot(diff, top) * 0.5f + 0.5f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+			}
+
+			// Center base
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.5f, 0.5f));
+		}
+
 		outVertices += numArcVertices * vertexStride;
 		outVertices = writeVector3(outVertices, vertexStride, base); // Write base vertex
 
@@ -723,7 +906,37 @@ namespace bs
 			}
 		}
 
-		// Top vertices (All same position, but need them separate because of different normals)
+		// UV
+		if(outUV != nullptr)
+		{
+			float angle = 0.0f;
+			float angleIncrement = Math::TWO_PI / numArcVertices;
+
+			// Bottom
+			for (UINT32 i = 0; i < numArcVertices; i++)
+			{
+				Vector2 uv;
+				uv.x = angle / Math::TWO_PI;
+				uv.y = 1.0f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+				angle += angleIncrement;
+			}
+
+			// Top
+			angle = 0.0f;
+			for (UINT32 i = 0; i < numArcVertices; i++)
+			{
+				Vector2 uv;
+				uv.x = angle / Math::TWO_PI;
+				uv.y = 0.0f;
+
+				outUV = writeVector2(outUV, vertexStride, uv);
+				angle += angleIncrement;
+			}
+		}
+
+		// Top vertices (All same position, but need them separate because of different normals & uv)
 		outVertices += numArcVertices * vertexStride;
 
 		for (UINT32 i = 0; i < numArcVertices; i++)
@@ -787,7 +1000,8 @@ namespace bs
 		}
 	}
 
-	void ShapeMeshes3D::solidQuad(const Rect3& area, UINT8* outVertices, UINT8* outNormals, UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset)
+	void ShapeMeshes3D::solidQuad(const Rect3& area, UINT8* outVertices, UINT8* outNormals, UINT8* outUV, 
+		UINT32 vertexOffset, UINT32 vertexStride, UINT32* outIndices, UINT32 indexOffset)
 	{
 		outVertices += (vertexOffset * vertexStride);
 
@@ -820,6 +1034,20 @@ namespace bs
 		outNormals = writeVector3(outNormals, vertexStride, reverseNormal);
 		outNormals = writeVector3(outNormals, vertexStride, reverseNormal);
 
+		if(outUV != nullptr)
+		{
+			outUV += (vertexOffset * vertexStride);
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.0f, 0.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(1.0f, 0.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(1.0f, 1.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.0f, 1.0f));
+
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.0f, 0.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(1.0f, 0.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(1.0f, 1.0f));
+			outUV = writeVector2(outUV, vertexStride, Vector2(0.0f, 1.0f));
+		}
+
 		outIndices += indexOffset;
 		outIndices[0] = vertexOffset;
 		outIndices[1] = vertexOffset + 1;
@@ -1101,4 +1329,30 @@ namespace bs
 			curDirection = increment.rotate(curDirection);
 		}
 	}
-}
+
+	void ShapeMeshes3D::generateTangents(UINT8* positions, UINT8* normals, UINT8* uv, UINT32* indices,
+		UINT32 numVertices, UINT32 numIndices, UINT32 vertexStride, UINT8* tangents)
+	{
+		Vector3* tempTangents = bs_stack_alloc<Vector3>(numVertices);
+		Vector3* tempBitangents = bs_stack_alloc<Vector3>(numVertices);
+
+		MeshUtility::calculateTangents((Vector3*)positions, (Vector3*)normals, (Vector2*)uv, (UINT8*)indices, numVertices,
+			 numIndices, tempTangents, tempBitangents, 4, vertexStride);
+
+		for (UINT32 i = 0; i < (UINT32)numVertices; i++)
+		{
+			Vector3 normal = *(Vector3*)&normals[i * vertexStride];
+			Vector3 tangent = tempTangents[i];
+			Vector3 bitangent = tempBitangents[i];
+
+			Vector3 engineBitangent = Vector3::cross(normal, tangent);
+			float sign = Vector3::dot(engineBitangent, bitangent);
+
+			Vector4 packedTangent(tangent.x, tangent.y, tangent.z, sign > 0 ? 1.0f : -1.0f);
+			memcpy(tangents + i * vertexStride, &packedTangent, sizeof(Vector4));
+		}
+
+		bs_stack_free(tempBitangents);
+		bs_stack_free(tempTangents);
+	}
+}