void TriangleMeshSmoother::computeVertexNormals() {
    osg::Vec3Array* normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX,
                                                    _geometry.getVertexArray()->getNumElements());
    addArray(normals);

    for(unsigned int i = 0 ; i < normals->getNumElements() ; ++ i) {
        (*normals)[i].set(0.f, 0.f, 0.f);
    }

    for(VertexIterator uniqueIndex = _graph->begin() ; uniqueIndex != _graph->end() ; ++ uniqueIndex) {
        unsigned int index = uniqueIndex->_index;
        std::set<unsigned int> processed;

        std::vector<IndexVector> oneRing = _graph->vertexOneRing(index, _creaseAngle);
        for(std::vector<IndexVector>::iterator cluster = oneRing.begin() ; cluster != oneRing.end() ; ++ cluster) {
            osg::Vec3f clusterNormal = cumulateTriangleNormals(*cluster);
            clusterNormal.normalize();

            std::set<unsigned int> duplicates;
            for(IndexVector::const_iterator tri = cluster->begin() ; tri != cluster->end() ; ++ tri) {
                const Triangle& triangle = _graph->triangle(*tri);

                if(_graph->unify(triangle.v1()) == index) {
                    duplicates.insert(triangle.v1());
                }
                else if(_graph->unify(triangle.v2()) == index) {
                    duplicates.insert(triangle.v2());
                }
                else if(_graph->unify(triangle.v3()) == index) {
                    duplicates.insert(triangle.v3());
                }
            }

            for(std::set<unsigned int>::iterator vertex = duplicates.begin() ; vertex != duplicates.end() ; ++ vertex) {
                if(processed.find(*vertex) == processed.end()) {
                    // vertex not yet processed
                    (*normals)[*vertex] = clusterNormal;
                    processed.insert(*vertex);
                }
                else {
                    // vertex already processed in a previous cluster: need to duplicate
                    unsigned int duplicate = duplicateVertex(*vertex);
                    replaceVertexIndexInTriangles(*cluster, *vertex, duplicate);
                    (*normals)[duplicate] = clusterNormal;

                    processed.insert(duplicate);
                }
            }
        }
    }

    _geometry.setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
    updateGeometryPrimitives();

    OSG_WARN << std::endl <<"Warning: [computeVertexNormals] [[normals]] Geometry '" << _geometry.getName()
                << "' normals have been recomputed" << std::endl;
    OSG_WARN << "Monitor: normal.recompute" << std::endl;
}
// TODO can bone indeces/weights be set by triangle corner vertex? If yes then these should also be split where they differ.
// FIXME this can be improved as it is currently potentially wasteful - we only compare to the first added vertex at an index - so if there are 4 more verts at this index which are different from this one but the same as each other they will all be duplicated.
void BatchProcessor::CreateBatchesInternal(
	mesh::MeshNode* meshNode,
	render::PerNodeBatchList &perNodeRenderBatches // Batch vector to fill in
	)
{
	for(meshNode; meshNode != NULL; meshNode = meshNode->m_next)
	{
		render::AppearanceTable& appearances = meshNode->GetAppearanceTable();

		render::BatchList renderBatches; // Each node needs a new set of batches (due to each node having a different matrix transform)
		renderBatches.resize(appearances.size());//Worst case scenario - should usually be less than this.

		mesh::MeshTriangleArrayPtr triangleArray = meshNode->GetTriangles();
		int numTriangles = meshNode->GetNumTriangles();
		if(numTriangles == 0)
		{
			continue;
		}
		mesh::MeshVertexArrayPtr vertexArray = meshNode->GetVertices();
		int numVertices = meshNode->GetNumVertices();
		if(numVertices == 0)
		{
			continue;
		}
		// Map containing the material id and a vector array containing the new index for vector[oldindex] in the array
		// TODO uh shorter name?
		std::vector<int>previouslyAssignedVertexIndexMap(numVertices, -1);
		std::vector<std::vector<int>> perMaterialPreviouslyAssignedVertexIndexMap(appearances.size(), previouslyAssignedVertexIndexMap);

		for(int triangleIndex = 0; triangleIndex < numTriangles; triangleIndex++)
		{
			int materialId = triangleArray[triangleIndex].GetMaterialId();
			assert(materialId != -1); // Every triangle must have a material
			std::vector<int> previouslyAssignedVertexIndexMap = perMaterialPreviouslyAssignedVertexIndexMap[materialId];

			if(!renderBatches[materialId]) // If a batch for this material does not already exist then create it
			{
				AddNewBatch(renderBatches, meshNode, appearances, materialId);
			}

			// Assign the mesh information to batches. Reassigning the indices as the data is processed to account for duplicated vertices being added.
			for(int triangleCornerIndex = 0; triangleCornerIndex < 3; triangleCornerIndex++)
			{
				unsigned int duplicateVertexIndex = triangleArray[triangleIndex].GetVertexIndex(triangleCornerIndex);

				// Here we create the per vertex data from the mesh triangles

				// First create a vertex which contains the vertex data from the various mesh data arrays.
				FbxVector4 fbxPosition = vertexArray[duplicateVertexIndex].GetPosition();
				glm::vec3 position = glm::vec3(static_cast<float>(fbxPosition[0]), static_cast<float>(fbxPosition[1]), static_cast<float>(fbxPosition[2]));
				glm::vec3 colour = triangleArray[triangleIndex].GetColour(triangleCornerIndex);
				glm::vec3 normal = glm::vec3(triangleArray[triangleIndex].GetNormal(triangleCornerIndex));
				glm::vec2 uv = triangleArray[triangleIndex].GetUV(triangleCornerIndex);

				render::TexturedSkinnedVertex duplicateVertex(position, colour, normal, uv, vertexArray[duplicateVertexIndex].GetBoneWeights(), vertexArray[duplicateVertexIndex].GetBoneInfluences());

				int previouslyCreatedIndex = previouslyAssignedVertexIndexMap[duplicateVertexIndex]; // FIXME this should loop to compare all vertices - see comment by function declaration

				// If a vertex at this index has not been added previously then we can just add the vertex to the correct render batch and store the index in the map
				if(previouslyCreatedIndex == -1)
				{
					AddVertexToBatch(duplicateVertexIndex, duplicateVertex, *renderBatches[materialId], previouslyAssignedVertexIndexMap);
					continue;
				}

				// Otherwise get the previous vertex to compare with the new one
				render::TexturedSkinnedVertex previouslyCreatedVertex = renderBatches[materialId]->GetVertices()[previouslyCreatedIndex];
				
				// If we have seen this vertex before, but this triangle corner has a different colour, uv coord or normal 
				// then duplicate the vertex to preserve the extra info (this will increase the vertex count in the mesh but will allow for
				// hard edges and texture seams which will otherwise have to be averaged for opengl).
				if(duplicateVertex.m_colour != previouslyCreatedVertex.m_colour)
				{
					AddVertexToBatch(duplicateVertexIndex, duplicateVertex, *renderBatches[materialId], previouslyAssignedVertexIndexMap);
				}
				else if(glm::dot(duplicateVertex.m_normal, previouslyCreatedVertex.m_normal) < DOT_THESHOLD) // The two normals are not the same - add a duplicate vertex
				{
					AddVertexToBatch(duplicateVertexIndex, duplicateVertex, *renderBatches[materialId], previouslyAssignedVertexIndexMap);
				}
				else if(duplicateVertex.m_uv != previouslyCreatedVertex.m_uv) // dupicate verts for texture coordinates on texture seams
				{
					AddVertexToBatch(duplicateVertexIndex, duplicateVertex, *renderBatches[materialId], previouslyAssignedVertexIndexMap);
				}
				else // An identical vertex was already added so don't add this one - just add the index and we refer to the existing vertex when rendering.
				{
					renderBatches[materialId]->AddIndex(previouslyCreatedIndex);
				}
			}
		}

		std::pair<unsigned int, render::BatchList> renderBatchWithMeshId(meshNode->GetId(), renderBatches);
		perNodeRenderBatches.insert(renderBatchWithMeshId);

		for(mesh::MeshNode *childNode = meshNode->m_firstChild; childNode != NULL; childNode = childNode->m_next)
		{
			CreateBatchesInternal(childNode, perNodeRenderBatches);
		}
	}
}