// THIS IS ONLY A PORTION OF THE FILE
// WILL NOT COMPILE OUT OF THE BOX!



void OrthogonalizeTangent (TangentInfo& tangentInfo, Vector3f normalf, Vector4f& outputTangent)
{
	TangentInfo::Vector3d normal   = { normalf.x, normalf.y, normalf.z };
	TangentInfo::Vector3d tangent  = tangentInfo.tangent;
	TangentInfo::Vector3d binormal = tangentInfo.binormal;

	// Try Gram-Schmidt orthonormalize.
	// This might fail in degenerate cases which we all handle seperately.

	double NdotT = TangentInfo::Vector3d::Dot (normal, tangent);
	TangentInfo::Vector3d newTangent =
	{
		tangent.x - NdotT * normal.x, tangent.y - NdotT * normal.y, tangent.z - NdotT * normal.z
	};

	double magT  = TangentInfo::Vector3d::Magnitude (newTangent);
	newTangent   = TangentInfo::Vector3d::Normalize (newTangent, magT);

	double NdotB = TangentInfo::Vector3d::Dot (normal, binormal);
	double TdotB = TangentInfo::Vector3d::Dot (newTangent, binormal) * magT;

	TangentInfo::Vector3d newBinormal =
	{
		binormal.x - NdotB * normal.x - TdotB * newTangent.x,
		binormal.y - NdotB * normal.y - TdotB * newTangent.y,
		binormal.z - NdotB * normal.z - TdotB * newTangent.z
	};

	double magB  = TangentInfo::Vector3d::Magnitude (newBinormal);
	newBinormal  = TangentInfo::Vector3d::Normalize (newBinormal, magB);


	Vector3f tangentf  = Vector3f( (float)newTangent.x,  (float)newTangent.y,  (float)newTangent.z );
	Vector3f binormalf = Vector3f( (float)newBinormal.x, (float)newBinormal.y, (float)newBinormal.z );


	const double kNormalizeEpsilon = 1e-6;

	if (magT <= kNormalizeEpsilon || magB <= kNormalizeEpsilon)
	{
		// Create tangent basis from scratch - we can safely use Vector3f here - no computations ;-)

		Vector3f axis1, axis2;

		float dpXN = Abs( Dot( Vector3f::xAxis, normalf ) );
		float dpYN = Abs( Dot( Vector3f::yAxis, normalf ) );
		float dpZN = Abs( Dot( Vector3f::zAxis, normalf ) );

		if ( dpXN <= dpYN && dpXN <= dpZN )
		{
			axis1 = Vector3f::xAxis;
			if( dpYN <= dpZN )	axis2 = Vector3f::yAxis;
			else				axis2 = Vector3f::zAxis;


		}
		else if ( dpYN <= dpXN && dpYN <= dpZN )
		{
			axis1 = Vector3f::yAxis;
			if( dpXN <= dpZN )	axis2 = Vector3f::xAxis;
			else				axis2 = Vector3f::zAxis;
		}
		else
		{
			axis1 = Vector3f::zAxis;
			if( dpXN <= dpYN )	axis2 = Vector3f::xAxis;
			else				axis2 = Vector3f::yAxis;
		}



		tangentf  = axis1 - Dot (normalf, axis1) * normalf;
		binormalf = axis2 - Dot (normalf, axis2) * normalf - Dot(tangentf,axis2)*NormalizeSafe(tangentf);

		tangentf  = NormalizeSafe(tangentf);
		binormalf = NormalizeSafe(binormalf);
	}

	outputTangent = Vector4f( tangentf.x, tangentf.y, tangentf.z, 0.0F);

	float dp = Dot (Cross (normalf, tangentf), binormalf);
	if ( dp > 0.0F)
		outputTangent.w = 1.0F;
	else
		outputTangent.w = -1.0F;
}


void CreateTangentSpaceTangentsUnsplit (const ImportMesh& mesh, Vector4f* outTangents, float normalSmoothingAngle)
{
	float smoothingAngle = max(89.0F, normalSmoothingAngle);

	const int vertexCount = mesh.vertices.size();
	const int faceCount = mesh.polygonSizes.size();
	const int indexCount = mesh.polygons.size();
	const Vector3f* vertices = &mesh.vertices[0];
	const Vector3f* normals = &mesh.normals[0];
	const Vector2f* tex = &mesh.uvs[0][0];
	const UInt32* indices = &mesh.polygons[0];

	// Calculate the tangent and binormal vectors of each face,
	// also compute face start offsets in the index buffer
	vector<TangentInfo> faceTangents(indexCount);
	vector<UInt32> faceOffsets(faceCount);
	for (int i = 0, idx = 0; i < faceCount; ++i)
	{
		const int fs = mesh.polygonSizes[i];
		ComputeTriangleTangentBasis (vertices, tex + idx, indices + idx, &faceTangents[idx]);
		///@TODO: is this good enough?
		if (fs == 4)
			ComputeTriangleTangentBasis (vertices, tex + idx+1, indices + idx+1, &faceTangents[idx+1]);
		faceOffsets[i] = idx;
		idx += fs;
	}

	// Average the tangents/binormals but only if they are within the smoothing angle
	vector<TangentInfo> avgTangents(indexCount);
	ConnectedMesh meshConnection (vertexCount, indices, mesh.polygons.size(), &mesh.polygonSizes[0], faceCount);
	float hardDot = cos (Deg2Rad (smoothingAngle)) - 0.001F; // NOTE: subtract is for more consistent results across platforms

	// Go through all faces, average with the connected faces
	for (int fi = 0, idx = 0; fi < faceCount; ++fi)
	{
		const int fs = mesh.polygonSizes[fi];
		for (int e = 0; e < fs; ++e)
		{
			TangentInfo::Vector3d faceTangent  = TangentInfo::Vector3d::Normalize (faceTangents[idx+e].tangent);
			TangentInfo::Vector3d faceBinormal = TangentInfo::Vector3d::Normalize (faceTangents[idx+e].binormal);

			TangentInfo::Vector3d averagedTangent  = {0,0,0};
			TangentInfo::Vector3d averagedBinormal = {0,0,0};

			ConnectedMesh::Vertex connected = meshConnection.vertices[indices[idx + e]];
			for (int i=0;i<connected.faceCount;i++)
			{
				int faceI = -1;
				const UInt32 connectedFaceOffset = faceOffsets[connected.faces[i]];
				const int connectedFaceSize = mesh.polygonSizes[connected.faces[i]];
				for (int k = 0; k < connectedFaceSize; ++k)
				{
					if (indices[connectedFaceOffset+k] == indices[idx+e])
					{
						faceI = connectedFaceOffset+k;
						break;
					}
				}
				Assert(faceI != -1);

				TangentInfo::Vector3d connectedTangent  = faceTangents[faceI].tangent;
				TangentInfo::Vector3d connectedBinormal = faceTangents[faceI].binormal;

				if ( TangentInfo::Vector3d::Dot(TangentInfo::Vector3d::Normalize(connectedTangent), faceTangent) > (double)hardDot )
				{
					averagedTangent.x += connectedTangent.x;
					averagedTangent.y += connectedTangent.y;
					averagedTangent.z += connectedTangent.z;
				}

				if ( TangentInfo::Vector3d::Dot(TangentInfo::Vector3d::Normalize(connectedBinormal), faceBinormal) > (double)hardDot )
				{
					averagedBinormal.x += connectedBinormal.x;
					averagedBinormal.y += connectedBinormal.y;
					averagedBinormal.z += connectedBinormal.z;
				}
			}

			avgTangents[idx+e].tangent  = TangentInfo::Vector3d::Normalize (averagedTangent);
			avgTangents[idx+e].binormal = TangentInfo::Vector3d::Normalize (averagedBinormal);
		}
		idx += fs;
	}

	// Orthogonalize the tangents/binormals and output them into the tangents array
	for (int i=0;i<avgTangents.size();i++)
	{
		OrthogonalizeTangent (avgTangents[i], normals[i], outTangents[i]);
	}
}


// THIS IS ONLY A PORTION OF THE FILE
// WILL NOT COMPILE OUT OF THE BOX!