How to combine normal orientation

I tried to implement a grid that has all the face normals pointing out. To realize this, I load the grid from the * .ctm file, then look through all the triangles to determine the normal using the cross product, and if the normal points to the negative z direction, I flip v1 and v2 (thus the normal orientation). After that, I save the result in a * .ctm file and view it using Meshlab.

The result in Meshlab still shows that the normals indicate both positive and negative z directions (seen from black triangles). Also, when viewing the normals in Meshlab, they do point back.

Can someone give me some tips on how to solve this?

The source code for the normalization part is:

pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ()); pcl::fromROSMsg (meshFixed.cloud,*cloud1);for(std::vector<pcl::Vertices>::iterator it = meshFixed.polygons.begin(); it != meshFixed.polygons.end(); ++it) { alglib::real_2d_array v0; double _v0[] = {cloud1->points[it->vertices[0]].x,cloud1->points[it->vertices[0]].y,cloud1->points[it->vertices[0]].z}; v0.setcontent(3,1,_v0); //3 rows, 1col alglib::real_2d_array v1; double _v1[] = {cloud1->points[it->vertices[1]].x,cloud1->points[it->vertices[1]].y,cloud1->points[it->vertices[1]].z}; v1.setcontent(3,1,_v1); //3 rows, 1col alglib::real_2d_array v2; double _v2[] = {cloud1->points[it->vertices[2]].x,cloud1->points[it->vertices[2]].y,cloud1->points[it->vertices[2]].z}; v2.setcontent(1,3,_v2); //3 rows, 1col alglib::real_2d_array normal; normal = cross(v1-v0,v2-v0); //if z<0 change indices order v1->v2 and v2->v1 alglib::real_2d_array normalizedNormal; if(normal[2][0]<0) { int index1,index2; index1 = it->vertices[1]; index2 = it->vertices[2]; it->vertices[1] = index2; it->vertices[2] = index1; //make normal of length 1 double normalScaling = 1.0/sqrt(dot(normal,normal)); normal[0][0] = -1*normal[0][0]; normal[1][0] = -1*normal[1][0]; normal[2][0] = -1*normal[2][0]; normalizedNormal = normalScaling * normal; } else { //make normal of length 1 double normalScaling = 1.0/sqrt(dot(normal,normal)); normalizedNormal = normalScaling * normal; } //add to normal cloud pcl::Normal pclNormalizedNormal; pclNormalizedNormal.normal_x = normalizedNormal[0][0]; pclNormalizedNormal.normal_y = normalizedNormal[1][0]; pclNormalizedNormal.normal_z = normalizedNormal[2][0]; normalsFixed.push_back(pclNormalizedNormal); } 

Result of this code:
enter image description here

I found the code in the VCG library to orient the normals of the face and vertices. After use, most of the mesh has the correct face normals, but not all.

New code:

 // VCG library implementation MyMesh m; // Convert pcl::PolygonMesh to VCG MyMesh m.Clear(); // Create temporary cloud in to have handy struct object pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ()); pcl::fromROSMsg (meshFixed.cloud,*cloud1); // Now convert the vertices to VCG MyMesh int vertCount = cloud1->width*cloud1->height; vcg::tri::Allocator<MyMesh>::AddVertices(m, vertCount); for(unsigned int i=0;i<vertCount;++i) m.vert[i].P()=vcg::Point3f(cloud1->points[i].x,cloud1->points[i].y,cloud1->points[i].z); // Now convert the polygon indices to VCG MyMesh => make VCG faces.. int triCount = meshFixed.polygons.size(); if(triCount==1) { if(meshFixed.polygons[0].vertices[0]==0 && meshFixed.polygons[0].vertices[1]==0 && meshFixed.polygons[0].vertices[2]==0) triCount=0; } Allocator<MyMesh>::AddFaces(m, triCount); for(unsigned int i=0;i<triCount;++i) { m.face[i].V(0)=&m.vert[meshFixed.polygons[i].vertices[0]]; m.face[i].V(1)=&m.vert[meshFixed.polygons[i].vertices[1]]; m.face[i].V(2)=&m.vert[meshFixed.polygons[i].vertices[2]]; } vcg::tri::UpdateBounding<MyMesh>::Box(m); vcg::tri::UpdateNormal<MyMesh>::PerFace(m); vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m); printf("Input mesh vn:%i fn:%i\n",m.VN(),m.FN()); // Start to flip all normals to outside vcg::face::FFAdj<MyMesh>::FFAdj(); vcg::tri::UpdateTopology<MyMesh>::FaceFace(m); bool oriented, orientable; if ( vcg::tri::Clean<MyMesh>::CountNonManifoldEdgeFF(m)>0 ) { std::cout << "Mesh has some not 2-manifold faces, Orientability requires manifoldness" << std::endl; // text return; // can't continue, mesh can't be processed } vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable); vcg::tri::Clean<MyMesh>::FlipNormalOutside(m); vcg::tri::Clean<MyMesh>::FlipMesh(m); //vcg::tri::UpdateTopology<MyMesh>::FaceFace(m); //vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m); vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m); vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m); // now convert VCG back to pcl::PolygonMesh pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGBA>); cloud->is_dense = false; cloud->width = vertCount; cloud->height = 1; cloud->points.resize (vertCount); // Now fill the pointcloud of the mesh for(int i=0; i<vertCount; i++) { cloud->points[i].x = m.vert[i].P()[0]; cloud->points[i].y = m.vert[i].P()[1]; cloud->points[i].z = m.vert[i].P()[2]; } pcl::toROSMsg(*cloud,meshFixed.cloud); std::vector<pcl::Vertices> polygons; // Now fill the indices of the triangles/faces of the mesh for(int i=0; i<triCount; i++) { pcl::Vertices vertices; vertices.vertices.push_back(m.face[i].V(0)-&*m.vert.begin()); vertices.vertices.push_back(m.face[i].V(1)-&*m.vert.begin()); vertices.vertices.push_back(m.face[i].V(2)-&*m.vert.begin()); polygons.push_back(vertices); } meshFixed.polygons = polygons; 

The result is: (Meshlab still shows that the normals are facing both sides)
enter image description here

+7
c ++ 3d normals
source share
1 answer

I finally solved the problem. Therefore, I still use the VCG library. From the above new code, I slightly updated the following section:

 vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable); //vcg::tri::Clean<MyMesh>::FlipNormalOutside(m); //vcg::tri::Clean<MyMesh>::FlipMesh(m); //vcg::tri::UpdateTopology<MyMesh>::FaceFace(m); //vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m); vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m); vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m); 

Now I updated the function vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh() in clean.h . Here, the update should correctly orient the first polygon of the group. Also, after replacing the edge, the face normal is calculated and updated.

 static void OrientCoherentlyMesh(MeshType &m, bool &Oriented, bool &Orientable) { RequireFFAdjacency(m); assert(&Oriented != &Orientable); assert(m.face.back().FFp(0)); // This algorithms require FF topology initialized Orientable = true; Oriented = true; tri::UpdateSelection<MeshType>::FaceClear(m); std::stack<FacePointer> faces; for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) { if (!fi->IsD() && !fi->IsS()) { // each face put in the stack is selected (and oriented) fi->SetS(); // New section of code to orient the initial face correctly if(fi->N()[2]>0.0) { face::SwapEdge<FaceType,true>(*fi, 0); face::ComputeNormal(*fi); } // End of new code section. faces.push(&(*fi)); // empty the stack while (!faces.empty()) { FacePointer fp = faces.top(); faces.pop(); // make consistently oriented the adjacent faces for (int j = 0; j < 3; j++) { //get one of the adjacent face FacePointer fpaux = fp->FFp(j); int iaux = fp->FFi(j); if (!fpaux->IsD() && fpaux != fp && face::IsManifold<FaceType>(*fp, j)) { if (!CheckOrientation(*fpaux, iaux)) { Oriented = false; if (!fpaux->IsS()) { face::SwapEdge<FaceType,true>(*fpaux, iaux); // New line to update face normal face::ComputeNormal(*fpaux); // end of new section. assert(CheckOrientation(*fpaux, iaux)); } else { Orientable = false; break; } } // put the oriented face into the stack if (!fpaux->IsS()) { fpaux->SetS(); faces.push(fpaux); } } } } } if (!Orientable) break; } } 

In addition, I also updated the bool CheckOrientation(FaceType &f, int z) function bool CheckOrientation(FaceType &f, int z) to perform the calculation based on the normal z-direction.

 template <class FaceType> bool CheckOrientation(FaceType &f, int z) { // Added next section to calculate the difference between normal z-directions FaceType *original = f.FFp(z); double nf2,ng2; nf2=fN()[2]; ng2=original->N()[2]; // End of additional section if (IsBorder(f, z)) return true; else { FaceType *g = f.FFp(z); int gi = f.FFi(z); // changed if statement from: if (f.V0(z) == g->V1(gi)) if (nf2/abs(nf2)==ng2/abs(ng2)) return true; else return false; } } 

The result is what I expect and wish from the algorithm:

enter image description here

+6
source share

All Articles