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); } } }