فهرست منبع

Added tangent and normal generation for meshes
Added a missing file from previous commit

Marko Pintera 10 سال پیش
والد
کامیت
4347837545

+ 2 - 0
BansheeCore/BansheeCore.vcxproj

@@ -282,6 +282,7 @@
     <ClInclude Include="Include\BsDrawList.h" />
     <ClInclude Include="Include\BsDrawList.h" />
     <ClInclude Include="Include\BsMeshImportOptions.h" />
     <ClInclude Include="Include\BsMeshImportOptions.h" />
     <ClInclude Include="Include\BsMeshImportOptionsRTTI.h" />
     <ClInclude Include="Include\BsMeshImportOptionsRTTI.h" />
+    <ClInclude Include="Include\BsMeshUtility.h" />
     <ClInclude Include="Include\BsPrefab.h" />
     <ClInclude Include="Include\BsPrefab.h" />
     <ClInclude Include="Include\BsPrefabDiff.h" />
     <ClInclude Include="Include\BsPrefabDiff.h" />
     <ClInclude Include="Include\BsPrefabDiffRTTI.h" />
     <ClInclude Include="Include\BsPrefabDiffRTTI.h" />
@@ -438,6 +439,7 @@
     <ClCompile Include="Source\BsIResourceListener.cpp" />
     <ClCompile Include="Source\BsIResourceListener.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsMeshImportOptions.cpp" />
     <ClCompile Include="Source\BsMeshImportOptions.cpp" />
+    <ClCompile Include="Source\BsMeshUtility.cpp" />
     <ClCompile Include="Source\BsPrefab.cpp" />
     <ClCompile Include="Source\BsPrefab.cpp" />
     <ClCompile Include="Source\BsPrefabDiff.cpp" />
     <ClCompile Include="Source\BsPrefabDiff.cpp" />
     <ClCompile Include="Source\BsProfilerCPU.cpp" />
     <ClCompile Include="Source\BsProfilerCPU.cpp" />

+ 6 - 0
BansheeCore/BansheeCore.vcxproj.filters

@@ -542,6 +542,9 @@
     <ClInclude Include="Include\BsPrefabDiffRTTI.h">
     <ClInclude Include="Include\BsPrefabDiffRTTI.h">
       <Filter>Header Files\RTTI</Filter>
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsMeshUtility.h">
+      <Filter>Header Files\Utility</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCoreApplication.cpp">
     <ClCompile Include="Source\BsCoreApplication.cpp">
@@ -856,5 +859,8 @@
     <ClCompile Include="Source\BsPrefabDiff.cpp">
     <ClCompile Include="Source\BsPrefabDiff.cpp">
       <Filter>Source Files\Scene</Filter>
       <Filter>Source Files\Scene</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsMeshUtility.cpp">
+      <Filter>Source Files\Utility</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 79 - 0
BansheeCore/Include/BsMeshUtility.h

@@ -0,0 +1,79 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Performs various operations on mesh geometry.
+	 */
+	class BS_CORE_EXPORT MeshUtility
+	{
+	public:
+		/**
+		 * @brief	Calculates per-vertex normals based on the provided vertices and indices.
+		 *
+		 * @param	vertices	Set of vertices containing vertex positions.
+		 * @param	indices		Set of indices containing indexes into vertex array for each triangle.
+		 * @param	numVertices	Number of vertices in the "vertices" array.
+		 * @param	numIndices	Number of indices in the "indices" array. Must be a multiple of three.
+		 * @param	normals		Pre-allocated buffer that will contain the calculated normals. Must be the same size
+		 *						as the vertex array.
+		 * @param	indexSize	Size of a single index in the indices array, in bytes.
+		 *
+		 * @note	Vertices should be split before calling this method if there are any discontinuities. (e.g. a vertex
+		 *			on a corner of a cube should be split into three vertices used by three triangles in order for the normals
+		 *			to be valid.)
+		 */
+		static void calculateNormals(Vector3* vertices, UINT8* indices, UINT32 numVertices, 
+			UINT32 numIndices, Vector3* normals, UINT32 indexSize = 4);
+
+		/**
+		 * @brief	Calculates per-vertex tangents and bitangents based on the provided vertices, uv coordinates and indices.
+		 *
+		 * @param	vertices	Set of vertices containing vertex positions.
+		 * @param	normals		Set of normals to use when calculating tangents. Must the the same length
+		 *						as the number of vertices.
+		 * @param	uv			Set of UV coordinates to use when calculating tangents. Must the the same length
+		 *						as the number of vertices.
+		 * @param	indices		Set of indices containing indexes into vertex array for each triangle.
+		 * @param	numVertices	Number of vertices in the "vertices", "normals" and "uv" arrays.
+		 * @param	numIndices	Number of indices in the "indices" array. Must be a multiple of three.
+		 * @param	tangents	Pre-allocated buffer that will contain the calculated tangents. Must be the same size
+		 *						as the vertex array.
+		 * @param	bitangents	Pre-allocated buffer that will contain the calculated bitangents. Must be the same size
+		 *						as the vertex array.
+		 * @param	indexSize	Size of a single index in the indices array, in bytes.
+		 *
+		 * @note	Vertices should be split before calling this method if there are any discontinuities. (e.g. a vertex
+		 *			on a corner of a cube should be split into three vertices used by three triangles in order for the normals
+		 *			to be valid.)
+		 */
+		static void calculateTangents(Vector3* vertices, Vector3* normals, Vector2* uv, UINT8* indices, UINT32 numVertices, 
+			UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize = 4);
+
+		/**
+		 * @brief	Calculates per-vertex tangent space (normal, tangent, bitangent) based on the provided 
+		 *			vertices, uv coordinates and indices.
+		 *
+		 * @param	vertices	Set of vertices containing vertex positions.
+		 * @param	uv			Set of UV coordinates to use when calculating tangents.
+		 * @param	indices		Set of indices containing indexes into vertex array for each triangle.
+		 * @param	numVertices	Number of vertices in the "vertices" array.
+		 * @param	numIndices	Number of indices in the "indices" array. Must be a multiple of three.
+		 * @param	normals		Pre-allocated buffer that will contain the calculated normals. Must be the same size
+		 *						as the vertex array.
+		 * @param	tangents	Pre-allocated buffer that will contain the calculated tangents. Must be the same size
+		 *						as the vertex array.
+		 * @param	bitangents	Pre-allocated buffer that will contain the calculated bitangents. Must be the same size
+		 *						as the vertex array.
+		 * @param	indexSize	Size of a single index in the indices array, in bytes.
+		 *
+		 * @note	Vertices should be split before calling this method if there are any discontinuities. (e.g. a vertex
+		 *			on a corner of a cube should be split into three vertices used by three triangles in order for the normals
+		 *			to be valid.)
+		 */
+		static void calculateTangentSpace(Vector3* vertices, Vector2* uv, UINT8* indices, UINT32 numVertices, 
+			UINT32 numIndices, Vector3* normals, Vector3* tangents, Vector3* bitangents, UINT32 indexSize = 4);
+	};
+}

+ 66 - 0
BansheeCore/Include/BsPrefab.h

@@ -0,0 +1,66 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsGameObject.h"
+#include "BsResource.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Prefab is a saveable hierarchy of scene objects. In general it
+	 *			can serve as any grouping of scene objects (e.g. a level) or be used
+	 *			as a form of a template instantiated and reused throughout the scene.
+	 */
+	class BS_CORE_EXPORT Prefab : public Resource
+	{
+	public:
+		Prefab();
+
+		/**
+		 * @brief	Creates a new prefab from the provided scene object. The scene object
+		 *			must not have a prefab link already. After the prefab is created the
+		 *			scene object will be automatically linked to it.
+		 */
+		static HPrefab create(const HSceneObject& sceneObject);
+
+		/**
+		 * @brief	Instantiates a prefab by creating an instance of the prefab's
+		 *			scene object hierarchy. The returned hierarchy will be parented
+		 *			to world root by default.
+		 */
+		HSceneObject instantiate();
+
+		/**
+		 * @brief	Returns a reference to the internal prefab hierarchy.
+		 */
+		HSceneObject getRoot() const { return mRoot; }
+
+	private:
+		/**
+		 * @brief	Initializes the internal prefab hierarchy. Must be called druing creation.
+		 */
+		void initialize(const HSceneObject& sceneObject);
+
+		/**
+		 * @brief	Generates prefab "link" ID that can be used for tracking which game object
+		 *			in a prefab instance corresponds to an object in the prefab.
+		 */
+		void generatePrefabIds(const HSceneObject& sceneObject);
+
+		/**
+		 * @brief	Creates an empty and uninitialized prefab.
+		 */
+		static PrefabPtr createEmpty();
+
+		HSceneObject mRoot;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		friend class PrefabRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
+}

+ 203 - 0
BansheeCore/Source/BsMeshUtility.cpp

@@ -0,0 +1,203 @@
+#include "BsMeshUtility.h"
+#include "BsVector3.h"
+#include "BsVector2.h"
+
+namespace BansheeEngine
+{
+	struct VertexFaces
+	{
+		UINT32* faces;
+		UINT32 numFaces = 0;
+	};
+
+	struct VertexConnectivity
+	{
+		VertexConnectivity(UINT8* indices, UINT32 numVertices, UINT32 numFaces, UINT32 indexSize)
+			:vertexFaces(nullptr), mMaxFacesPerVertex(0), mFaces(nullptr), mNumVertices(numVertices)
+		{
+			vertexFaces = bs_newN<VertexFaces>(numVertices);
+
+			resizeFaceArray(10);
+
+			for (UINT32 i = 0; i < numFaces; i++)
+			{
+				for (UINT32 j = 0; j < 3; j++)
+				{
+					UINT32 idx = i * 3 + j;
+					UINT32 vertexIdx = 0;
+					memcpy(&vertexIdx, indices + idx * indexSize, indexSize);
+
+					VertexFaces& faces = vertexFaces[vertexIdx];
+					if (faces.numFaces >= mMaxFacesPerVertex)
+						resizeFaceArray(mMaxFacesPerVertex * 2);
+
+					faces.faces[faces.numFaces] = i;
+					faces.numFaces++;
+				}
+			}
+		}
+
+		~VertexConnectivity()
+		{
+			if (vertexFaces != nullptr)
+				bs_deleteN(vertexFaces, mNumVertices);
+
+			if (mFaces != nullptr)
+				bs_free(mFaces);
+		}
+
+		VertexFaces* vertexFaces;
+
+	private:
+		void resizeFaceArray(UINT32 numFaces)
+		{
+			UINT32* newFaces = (UINT32*)bs_alloc(numFaces * mNumVertices);
+
+			if (mFaces != nullptr)
+			{
+				for (UINT32 i = 0; i < mNumVertices; i++)
+					memcpy(newFaces + (i * numFaces), mFaces + (i * mMaxFacesPerVertex), mMaxFacesPerVertex * sizeof(UINT32));
+
+				bs_free(mFaces);
+			}
+
+			for (UINT32 i = 0; i < mNumVertices; i++)
+				vertexFaces[i].faces = newFaces + (i * numFaces);
+
+			mFaces = newFaces;
+			mMaxFacesPerVertex = numFaces;
+		}
+
+		UINT32 mMaxFacesPerVertex;
+		UINT32 mNumVertices;
+		UINT32* mFaces;
+	};
+
+	void MeshUtility::calculateNormals(Vector3* vertices, UINT8* indices, UINT32 numVertices,
+		UINT32 numIndices, Vector3* normals, UINT32 indexSize)
+	{
+		UINT32 numFaces = numIndices / 3;
+
+		Vector3* faceNormals = bs_newN<Vector3>(numFaces);
+		for (UINT32 i = 0; i < numFaces; i++)
+		{
+			UINT32 triangle[3];
+			memcpy(&triangle[0], indices + (i * 3 + 0) * indexSize, indexSize);
+			memcpy(&triangle[1], indices + (i * 3 + 1) * indexSize, indexSize);
+			memcpy(&triangle[2], indices + (i * 3 + 2) * indexSize, indexSize);
+
+			Vector3 edgeA = vertices[triangle[1]] - vertices[triangle[0]];
+			Vector3 edgeB = vertices[triangle[2]] - vertices[triangle[0]];
+			faceNormals[i] = Vector3::normalize(Vector3::cross(edgeA, edgeB));
+
+			// Note: Potentially don't normalize here in order to weigh the normals
+			// by triangle size
+		}
+
+		VertexConnectivity connectivity(indices, numVertices, numFaces, indexSize);
+		for (UINT32 i = 0; i < numVertices; i++)
+		{
+			VertexFaces& faces = connectivity.vertexFaces[i];
+
+			for (UINT32 j = 0; j < faces.numFaces; j++)
+			{
+				UINT32 faceIdx = faces.faces[j];
+				normals[i] += faceNormals[faceIdx];
+			}
+
+			normals[i].normalize();
+		}
+
+		bs_deleteN(faceNormals, numFaces);
+	}
+
+	void MeshUtility::calculateTangents(Vector3* vertices, Vector3* normals, Vector2* uv, UINT8* indices, UINT32 numVertices,
+		UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize)
+	{
+		UINT32 numFaces = numIndices / 3;
+
+		Vector3* faceTangents = bs_newN<Vector3>(numFaces);
+		Vector3* faceBitangents = bs_newN<Vector3>(numFaces);
+		for (UINT32 i = 0; i < numFaces; i++)
+		{
+			UINT32 triangle[3];
+			memcpy(&triangle[0], indices + (i * 3 + 0) * indexSize, indexSize);
+			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]];
+
+			Vector2 uv0 = uv[triangle[0]];
+			Vector2 uv1 = uv[triangle[1]];
+			Vector2 uv2 = uv[triangle[2]];
+
+			Vector3 q0 = p1 - p0;
+			Vector3 q1 = p2 - p0;
+
+			Vector2 s;
+			s.x = uv1.x - uv0.x;
+			s.y = uv2.x - uv0.x;
+
+			Vector2 t;
+			t.x = uv1.y - uv0.y;
+			t.y = uv2.y - uv0.y;
+
+			float denom = s.x*t.y - s.y * t.x;
+			if (fabs(denom) >= 0e-8f)
+			{
+				float r = 1.0f / denom;
+				s *= r;
+				t *= r;
+
+				faceTangents[i] = t.y * q0 - t.x * q1;
+				faceBitangents[i] = s.x * q0 - s.y * q1;
+
+				faceTangents[i].normalize();
+				faceBitangents[i].normalize();
+			}
+
+			// Note: Potentially don't normalize here in order to weigh the normals
+			// by triangle size
+		}
+
+		VertexConnectivity connectivity(indices, numVertices, numFaces, indexSize);
+		for (UINT32 i = 0; i < numVertices; i++)
+		{
+			VertexFaces& faces = connectivity.vertexFaces[i];
+
+			for (UINT32 j = 0; j < faces.numFaces; j++)
+			{
+				UINT32 faceIdx = faces.faces[j];
+				tangents[i] += faceTangents[faceIdx];
+				bitangents[i] += faceBitangents[faceIdx];
+			}
+
+			tangents[i].normalize();
+			bitangents[i].normalize();
+
+			// Orthonormalize
+			float dot0 = normals[i].dot(tangents[i]);
+			tangents[i] -= dot0*normals[i];
+			tangents[i].normalize();
+
+			float dot1 = tangents[i].dot(bitangents[i]);
+			dot0 = normals[i].dot(bitangents[i]);
+			bitangents[i] -= dot0*normals[i] + dot1*tangents[i];
+			bitangents[i].normalize();
+		}
+
+		bs_deleteN(faceTangents, numFaces);
+		bs_deleteN(faceBitangents, numFaces);
+
+		// TODO - Consider weighing tangents by triangle size and/or edge angles
+	}
+
+	void MeshUtility::calculateTangentSpace(Vector3* vertices, Vector2* uv, UINT8* indices, UINT32 numVertices,
+		UINT32 numIndices, Vector3* normals, Vector3* tangents, Vector3* bitangents, UINT32 indexSize)
+	{
+		calculateNormals(vertices, indices, numVertices, numIndices, normals, indexSize);
+		calculateTangents(vertices, normals, uv, indices, numVertices, numIndices, tangents, bitangents, indexSize);
+	}
+}

+ 8 - 8
BansheeFBXImporter/Include/BsFBXImportData.h

@@ -43,10 +43,10 @@ namespace BansheeEngine
 	 */
 	 */
 	struct FBXBlendShapeFrame
 	struct FBXBlendShapeFrame
 	{
 	{
-		Vector<Vector4> positions;
-		Vector<Vector4> normals;
-		Vector<Vector4> tangents;
-		Vector<Vector4> bitangents;
+		Vector<Vector3> positions;
+		Vector<Vector3> normals;
+		Vector<Vector3> tangents;
+		Vector<Vector3> bitangents;
 
 
 		float weight;
 		float weight;
 	};
 	};
@@ -155,10 +155,10 @@ namespace BansheeEngine
 		FbxMesh* fbxMesh;
 		FbxMesh* fbxMesh;
 
 
 		Vector<int> indices;
 		Vector<int> indices;
-		Vector<Vector4> positions;
-		Vector<Vector4> normals;
-		Vector<Vector4> tangents;
-		Vector<Vector4> bitangents;
+		Vector<Vector3> positions;
+		Vector<Vector3> normals;
+		Vector<Vector3> tangents;
+		Vector<Vector3> bitangents;
 		Vector<RGBA> colors;
 		Vector<RGBA> colors;
 		Vector<Vector2> UV[FBX_IMPORT_MAX_UV_LAYERS];
 		Vector<Vector2> UV[FBX_IMPORT_MAX_UV_LAYERS];
 		Vector<int> materials;
 		Vector<int> materials;

+ 7 - 0
BansheeFBXImporter/Include/BsFBXImporter.h

@@ -124,6 +124,13 @@ namespace BansheeEngine
 		 */
 		 */
 		void splitMeshVertices(FBXImportScene& scene);
 		void splitMeshVertices(FBXImportScene& scene);
 
 
+		/**
+		 * @brief	Traverses over all meshes in the scene and generates normals, tangents and bitangents if they're missing.
+		 *
+		 * @note	This assumes vertices have already been split and shouldn't be called on pre-split meshes.
+		 */
+		void generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options);
+
 		/**
 		/**
 		 * @brief	Converts the mesh data from the imported FBX scene into mesh data that can be used
 		 * @brief	Converts the mesh data from the imported FBX scene into mesh data that can be used
 		 *			for initializing a mesh.
 		 *			for initializing a mesh.

+ 2 - 2
BansheeFBXImporter/Include/BsFBXUtility.h

@@ -14,8 +14,8 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @brief	Calculates per-index normals based on the provided smoothing groups.
 		 * @brief	Calculates per-index normals based on the provided smoothing groups.
 		 */
 		 */
-		static void normalsFromSmoothing(const Vector<Vector4>& positions, const Vector<int>& indices,
-			const Vector<int>& smoothing, Vector<Vector4>& normals);
+		static void normalsFromSmoothing(const Vector<Vector3>& positions, const Vector<int>& indices,
+			const Vector<int>& smoothing, Vector<Vector3>& normals);
 
 
 		/**
 		/**
 		 * @brief	Find vertices in the source mesh that have different attributes but have the same
 		 * @brief	Find vertices in the source mesh that have different attributes but have the same

+ 52 - 22
BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -11,6 +11,7 @@
 #include "BsQuaternion.h"
 #include "BsQuaternion.h"
 #include "BsVertexDataDesc.h"
 #include "BsVertexDataDesc.h"
 #include "BsFBXUtility.h"
 #include "BsFBXUtility.h"
+#include "BsMeshUtility.h"
 #include <BsMeshImportOptions.h>
 #include <BsMeshImportOptions.h>
 
 
 namespace BansheeEngine
 namespace BansheeEngine
@@ -25,13 +26,12 @@ namespace BansheeEngine
 		return native;
 		return native;
 	}
 	}
 
 
-	Vector4 FBXToNativeType(const FbxVector4& value)
+	Vector3 FBXToNativeType(const FbxVector4& value)
 	{
 	{
-		Vector4 native;
+		Vector3 native;
 		native.x = (float)value[0];
 		native.x = (float)value[0];
 		native.y = (float)value[1];
 		native.y = (float)value[1];
 		native.z = (float)value[2];
 		native.z = (float)value[2];
-		native.w = (float)value[3];
 
 
 		return native;
 		return native;
 	}
 	}
@@ -137,11 +137,12 @@ namespace BansheeEngine
 			importAnimations(fbxScene, fbxImportOptions, importedScene);
 			importAnimations(fbxScene, fbxImportOptions, importedScene);
 
 
 		splitMeshVertices(importedScene);
 		splitMeshVertices(importedScene);
+		generateMissingTangentSpace(importedScene, fbxImportOptions);
 		
 		
 		Vector<SubMesh> subMeshes;
 		Vector<SubMesh> subMeshes;
 		MeshDataPtr meshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
 		MeshDataPtr meshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
 
 
-		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, optimize for vertex cache
+		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
 
 
 		shutDownSdk();
 		shutDownSdk();
 
 
@@ -784,14 +785,8 @@ namespace BansheeEngine
 						{
 						{
 							FBXUtility::normalsFromSmoothing(importMesh->positions, importMesh->indices, 
 							FBXUtility::normalsFromSmoothing(importMesh->positions, importMesh->indices, 
 								importMesh->smoothingGroups, importMesh->normals);
 								importMesh->smoothingGroups, importMesh->normals);
-							hasNormals = true;
 						}
 						}
 					}
 					}
-
-					if (!hasNormals)
-					{
-						// TODO - Calculate normals
-					}
 				}
 				}
 				else
 				else
 					readLayerData(*mainLayer->GetNormals(), importMesh->normals, importMesh->indices);
 					readLayerData(*mainLayer->GetNormals(), importMesh->normals, importMesh->indices);
@@ -813,10 +808,6 @@ namespace BansheeEngine
 					readLayerData(*mainLayer->GetTangents(), importMesh->tangents, importMesh->indices);
 					readLayerData(*mainLayer->GetTangents(), importMesh->tangents, importMesh->indices);
 					readLayerData(*mainLayer->GetBinormals(), importMesh->bitangents, importMesh->indices);
 					readLayerData(*mainLayer->GetBinormals(), importMesh->bitangents, importMesh->indices);
 				}
 				}
-				else
-				{
-					// TODO - Calculate tangent frame
-				}
 			}
 			}
 
 
 			// Import material indexes
 			// Import material indexes
@@ -908,10 +899,6 @@ namespace BansheeEngine
 					FBXUtility::normalsFromSmoothing(outFrame.positions, mesh.indices,
 					FBXUtility::normalsFromSmoothing(outFrame.positions, mesh.indices,
 						mesh.smoothingGroups, outFrame.normals);
 						mesh.smoothingGroups, outFrame.normals);
 				}
 				}
-				else
-				{
-					// TODO - Calculate normals
-				}
 			}
 			}
 			else
 			else
 				readLayerData(*mainLayer->GetNormals(), outFrame.normals, mesh.indices);
 				readLayerData(*mainLayer->GetNormals(), outFrame.normals, mesh.indices);
@@ -926,10 +913,6 @@ namespace BansheeEngine
 				readLayerData(*mainLayer->GetTangents(), outFrame.tangents, mesh.indices);
 				readLayerData(*mainLayer->GetTangents(), outFrame.tangents, mesh.indices);
 				readLayerData(*mainLayer->GetBinormals(), outFrame.bitangents, mesh.indices);
 				readLayerData(*mainLayer->GetBinormals(), outFrame.bitangents, mesh.indices);
 			}
 			}
-			else
-			{
-				// TODO - Calculate tangent frame
-			}
 		}
 		}
 	}
 	}
 
 
@@ -1052,6 +1035,53 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void FBXImporter::generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options)
+	{
+		for (auto& mesh : scene.meshes)
+		{
+			UINT32 numVertices = (UINT32)mesh->positions.size();
+			UINT32 numIndices = (UINT32)mesh->indices.size();
+
+			if ((options.importNormals || options.importTangents) && mesh->normals.empty())
+			{
+				mesh->normals.resize(numVertices);
+
+				MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, mesh->normals.data());
+			}
+
+			if (options.importTangents && !mesh->UV[0].empty() && (mesh->tangents.empty() || mesh->bitangents.empty()))
+			{
+				mesh->tangents.resize(numVertices);
+				mesh->bitangents.resize(numVertices);
+
+				MeshUtility::calculateTangents(mesh->positions.data(), mesh->normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(), 
+					numVertices, numIndices, mesh->tangents.data(), mesh->bitangents.data());
+			}
+
+			for (auto& shape : mesh->blendShapes)
+			{
+				for (auto& frame : shape.frames)
+				{
+					if ((options.importNormals || options.importTangents) && frame.normals.empty())
+					{
+						frame.normals.resize(numVertices);
+
+						MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, frame.normals.data());
+					}
+
+					if (options.importTangents && !mesh->UV[0].empty() && (frame.tangents.empty() || frame.bitangents.empty()))
+					{
+						mesh->tangents.resize(numVertices);
+						mesh->bitangents.resize(numVertices);
+
+						MeshUtility::calculateTangents(mesh->positions.data(), frame.normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(),
+							numVertices, numIndices, frame.tangents.data(), frame.bitangents.data());
+					}
+				}
+			}
+		}
+	}
+
 	void FBXImporter::importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene)
 	void FBXImporter::importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene)
 	{
 	{
 		FbxNode* root = scene->GetRootNode();
 		FbxNode* root = scene->GetRootNode();

+ 4 - 4
BansheeFBXImporter/Source/BsFBXUtility.cpp

@@ -89,13 +89,13 @@ namespace BansheeEngine
 		}
 		}
 	};
 	};
 
 
-	void FBXUtility::normalsFromSmoothing(const Vector<Vector4>& positions, const Vector<int>& indices,
-		const Vector<int>& smoothing, Vector<Vector4>& normals)
+	void FBXUtility::normalsFromSmoothing(const Vector<Vector3>& positions, const Vector<int>& indices,
+		const Vector<int>& smoothing, Vector<Vector3>& normals)
 	{
 	{
 		std::vector<SmoothVertex> smoothNormals;
 		std::vector<SmoothVertex> smoothNormals;
 		smoothNormals.resize(positions.size());
 		smoothNormals.resize(positions.size());
 
 
-		normals.resize(indices.size(), Vector4::ZERO);
+		normals.resize(indices.size(), Vector3::ZERO);
 
 
 		UINT32 numPolygons = (UINT32)(indices.size() / 3);
 		UINT32 numPolygons = (UINT32)(indices.size() / 3);
 
 
@@ -134,7 +134,7 @@ namespace BansheeEngine
 				{
 				{
 					int current = indices[idx + i];
 					int current = indices[idx + i];
 
 
-					normals[idx + i] = (Vector4)smoothNormals[current].getNormal(smoothing[idx + i]);
+					normals[idx + i] = smoothNormals[current].getNormal(smoothing[idx + i]);
 				}
 				}
 			}
 			}
 
 

+ 0 - 2
TODOExperimentation.txt

@@ -4,6 +4,4 @@ Next:
  - DefaultMeshData is defined in BansheeEngine but FBXImporter only includes Banshee Core
  - DefaultMeshData is defined in BansheeEngine but FBXImporter only includes Banshee Core
   - Therefore I am not using it in FBXImporter but I should
   - Therefore I am not using it in FBXImporter but I should
  - When import animation generate slopes for quaternion rotation curves
  - When import animation generate slopes for quaternion rotation curves
- - Add methods for calculating normals and tangent frame. This is especially needed for blend shapes as I believe they
-   come with tangent frame or normal information. I've marked points in code where this is needed with a TODO.