// SPDX-License-Identifier: MIT // clang-format off // http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html // https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification // // MIT License // Changes: // * Use Base::Vector3f as vec3f class // * Move global variables to a class to make the algorithm usable for multi-threading // * Comment out printf statements // * Fix compiler warnings // * Remove macros loop,i,j,k #include using vec3f = Base::Vector3f; class SymmetricMatrix { public: // Constructor SymmetricMatrix(double c=0) { for (std::size_t i=0;i<10;++i ) m[i] = c; } SymmetricMatrix(double m11, double m12, double m13, double m14, double m22, double m23, double m24, double m33, double m34, double m44) { m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14; m[4] = m22; m[5] = m23; m[6] = m24; m[7] = m33; m[8] = m34; m[9] = m44; } // Make plane SymmetricMatrix(double a,double b,double c,double d) { m[0] = a*a; m[1] = a*b; m[2] = a*c; m[3] = a*d; m[4] = b*b; m[5] = b*c; m[6] = b*d; m[7 ] =c*c; m[8 ] = c*d; m[9 ] = d*d; } double operator[](int c) const { return m[c]; } // Determinant double det(int a11, int a12, int a13, int a21, int a22, int a23, int a31, int a32, int a33) { double det = m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31] - m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32] - m[a12]*m[a21]*m[a33]; return det; } const SymmetricMatrix operator+(const SymmetricMatrix& n) const { return SymmetricMatrix( m[0]+n[0], m[1]+n[1], m[2]+n[2], m[3]+n[3], m[4]+n[4], m[5]+n[5], m[6]+n[6], m[7]+n[7], m[8]+n[8], m[9]+n[9]); } SymmetricMatrix& operator+=(const SymmetricMatrix& n) { m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3]; m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7]; m[8]+=n[8]; m[9]+=n[9]; return *this; } double m[10]; }; /////////////////////////////////////////// class Simplify { public: struct Triangle { int v[3];double err[4];int deleted,dirty;vec3f n; }; struct Vertex { vec3f p;int tstart,tcount;SymmetricMatrix q;int border;}; struct Ref { int tid,tvertex; }; std::vector triangles; std::vector vertices; std::vector refs; void simplify_mesh(int target_count, double tolerance, double aggressiveness=7); private: // Helper functions double vertex_error(const SymmetricMatrix& q, double x, double y, double z); double calculate_error(int id_v1, int id_v2, vec3f &p_result); bool flipped(vec3f p,int i0,int i1,Vertex &v0,Vertex &v1,std::vector &deleted); void update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles); void update_mesh(int iteration); void compact_mesh(); }; // // Main simplification function // // target_count : target nr. of triangles // tolerance : tolerance for the quadratic errors // aggressiveness : sharpness to increase the threshold. // 5..8 are good numbers // more iterations yield higher quality // If the passed tolerance is > 0 then this will be used to check // the quadratic error metric of all triangles. If none of them is below // the tolerance the algorithm will stop at this point. The number of the // remaining triangles usually will be higher than \a target_count // void Simplify::simplify_mesh(int target_count, double tolerance, double aggressiveness) { // init //printf("%s - start\n",__FUNCTION__); //int timeStart=timeGetTime(); for (std::size_t i=0;i deleted0,deleted1; int triangle_count=triangles.size(); for (int iteration=0;iteration<100;++iteration) { // target number of triangles reached ? Then break //printf("iteration %d - triangles %d\n",iteration,triangle_count-deleted_triangles); if (triangle_count-deleted_triangles<=target_count) break; // update mesh once in a while if (iteration%5==0) { update_mesh(iteration); } // clear dirty flag for (std::size_t i=0;i 0.0) { bool canContinue = false; for (std::size_t i=0;ithreshold) continue; if (t.deleted) continue; if (t.dirty) continue; for (std::size_t j=0;j<3;++j) { if (t.err[j] &deleted) { (void)i0; (void)v1; for (int k=0;k0.999) return true; vec3f n; n = d1.Cross(d2); n.Normalize(); deleted[k]=0; if (n.Dot(t.n)<0.2) return true; } return false; } // Update triangle connections and edge error after a edge is collapsed void Simplify::update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles) { vec3f p; for (int k=0;k0) // compact triangles { int dst=0; for (std::size_t i=0;i vcount,vids; for (std::size_t i=0;i try to find best result vec3f p1=vertices[id_v1].p; vec3f p2=vertices[id_v2].p; vec3f p3=(p1+p2)/2; double error1 = vertex_error(q, p1.x,p1.y,p1.z); double error2 = vertex_error(q, p2.x,p2.y,p2.z); double error3 = vertex_error(q, p3.x,p3.y,p3.z); error = std::min(error1, std::min(error2, error3)); if (error1 == error) p_result=p1; if (error2 == error) p_result=p2; if (error3 == error) p_result=p3; } return error; } /////////////////////////////////////////// // clang-format on