| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <vector> |
| |
|
| | using vec3f = Base::Vector3f; |
| |
|
| | class SymmetricMatrix { |
| |
|
| | public: |
| |
|
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| |
|
| | 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]; } |
| |
|
| | |
| |
|
| | 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<Triangle> triangles; |
| | std::vector<Vertex> vertices; |
| | std::vector<Ref> refs; |
| |
|
| | void simplify_mesh(int target_count, double tolerance, double aggressiveness=7); |
| |
|
| | private: |
| | |
| |
|
| | 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<int> &deleted); |
| | void update_triangles(int i0,Vertex &v,std::vector<int> &deleted,int &deleted_triangles); |
| | void update_mesh(int iteration); |
| | void compact_mesh(); |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void Simplify::simplify_mesh(int target_count, double tolerance, double aggressiveness) |
| | { |
| | |
| | |
| | |
| |
|
| | for (std::size_t i=0;i<triangles.size();++i) |
| | triangles[i].deleted=0; |
| |
|
| | |
| |
|
| | int deleted_triangles=0; |
| | std::vector<int> deleted0,deleted1; |
| | int triangle_count=triangles.size(); |
| |
|
| | for (int iteration=0;iteration<100;++iteration) |
| | { |
| | |
| | |
| | if (triangle_count-deleted_triangles<=target_count) |
| | break; |
| |
|
| | |
| | if (iteration%5==0) |
| | { |
| | update_mesh(iteration); |
| | } |
| |
|
| | |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | triangles[i].dirty=0; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double threshold = 0.000000001*pow(double(iteration+3),aggressiveness); |
| | if (tolerance > 0.0) |
| | { |
| | bool canContinue = false; |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | if (t.deleted) |
| | continue; |
| | if (t.dirty) |
| | continue; |
| | if (fabs(t.err[3])<tolerance) |
| | { |
| | canContinue = true; |
| | break; |
| | } |
| | } |
| |
|
| | if (!canContinue) |
| | break; |
| | } |
| |
|
| | |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | if (t.err[3]>threshold) |
| | continue; |
| | if (t.deleted) |
| | continue; |
| | if (t.dirty) |
| | continue; |
| |
|
| | for (std::size_t j=0;j<3;++j) |
| | { |
| | if (t.err[j]<threshold) |
| | { |
| | int i0=t.v[ j ]; Vertex &v0 = vertices[i0]; |
| | int i1=t.v[(j+1)%3]; Vertex &v1 = vertices[i1]; |
| |
|
| | |
| | if (v0.border != v1.border) |
| | continue; |
| |
|
| | |
| | vec3f p; |
| | calculate_error(i0,i1,p); |
| |
|
| | deleted0.resize(v0.tcount); |
| | deleted1.resize(v1.tcount); |
| |
|
| | |
| | if (flipped(p,i0,i1,v0,v1,deleted0)) |
| | continue; |
| | if (flipped(p,i1,i0,v1,v0,deleted1)) |
| | continue; |
| |
|
| | |
| | v0.p=p; |
| | v0.q=v1.q+v0.q; |
| | int tstart=refs.size(); |
| |
|
| | update_triangles(i0,v0,deleted0,deleted_triangles); |
| | update_triangles(i0,v1,deleted1,deleted_triangles); |
| |
|
| | int tcount=refs.size()-tstart; |
| |
|
| | if (tcount<=v0.tcount) |
| | { |
| | |
| | if (tcount) |
| | memcpy(&refs[v0.tstart],&refs[tstart],tcount*sizeof(Ref)); |
| | } |
| | else |
| | { |
| | |
| | v0.tstart=tstart; |
| | } |
| |
|
| | v0.tcount=tcount; |
| | break; |
| | } |
| | } |
| |
|
| | |
| | if (triangle_count-deleted_triangles<=target_count) |
| | break; |
| | } |
| | } |
| |
|
| | |
| | compact_mesh(); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | } |
| |
|
| | |
| |
|
| | bool Simplify::flipped(vec3f p, int i0, int i1, |
| | Vertex &v0, |
| | Vertex &v1, |
| | std::vector<int> &deleted) |
| | { |
| | (void)i0; (void)v1; |
| | for (int k=0;k<v0.tcount;++k) |
| | { |
| | Triangle &t=triangles[refs[v0.tstart+k].tid]; |
| | if (t.deleted) |
| | continue; |
| |
|
| | int s=refs[v0.tstart+k].tvertex; |
| | int id1=t.v[(s+1)%3]; |
| | int id2=t.v[(s+2)%3]; |
| |
|
| | if (id1==i1 || id2==i1) |
| | { |
| | deleted[k]=1; |
| | continue; |
| | } |
| | vec3f d1 = vertices[id1].p-p; d1.Normalize(); |
| | vec3f d2 = vertices[id2].p-p; d2.Normalize(); |
| | if (fabs(d1.Dot(d2))>0.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; |
| | } |
| |
|
| | |
| |
|
| | void Simplify::update_triangles(int i0,Vertex &v,std::vector<int> &deleted,int &deleted_triangles) |
| | { |
| | vec3f p; |
| | for (int k=0;k<v.tcount;++k) |
| | { |
| | Ref &r=refs[v.tstart+k]; |
| | Triangle &t=triangles[r.tid]; |
| | if (t.deleted) |
| | continue; |
| | if (deleted[k]) |
| | { |
| | t.deleted=1; |
| | deleted_triangles++; |
| | continue; |
| | } |
| | t.v[r.tvertex]=i0; |
| | t.dirty=1; |
| | t.err[0]=calculate_error(t.v[0],t.v[1],p); |
| | t.err[1]=calculate_error(t.v[1],t.v[2],p); |
| | t.err[2]=calculate_error(t.v[2],t.v[0],p); |
| | t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2])); |
| | refs.push_back(r); |
| | } |
| | } |
| |
|
| | |
| |
|
| | void Simplify::update_mesh(int iteration) |
| | { |
| | if(iteration>0) |
| | { |
| | int dst=0; |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | if (!triangles[i].deleted) |
| | { |
| | triangles[dst++]=triangles[i]; |
| | } |
| | } |
| | triangles.resize(dst); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if (iteration == 0) |
| | { |
| | for (std::size_t i=0;i<vertices.size();++i) |
| | vertices[i].q=SymmetricMatrix(0.0); |
| |
|
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | vec3f n,p[3]; |
| | for (std::size_t j=0;j<3;++j) |
| | p[j]=vertices[t.v[j]].p; |
| | n = (p[1]-p[0]).Cross(p[2]-p[0]); |
| | n.Normalize(); |
| | t.n=n; |
| | for (std::size_t j=0;j<3;++j) |
| | vertices[t.v[j]].q = vertices[t.v[j]].q+SymmetricMatrix(n.x,n.y,n.z,-n.Dot(p[0])); |
| | } |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | |
| | Triangle &t=triangles[i];vec3f p; |
| | for (std::size_t j=0;j<3;++j) |
| | t.err[j] = calculate_error(t.v[j],t.v[(j+1)%3],p); |
| | t.err[3]=std::min(t.err[0],std::min(t.err[1],t.err[2])); |
| | } |
| | } |
| |
|
| | |
| | for (std::size_t i=0;i<vertices.size();++i) |
| | { |
| | vertices[i].tstart=0; |
| | vertices[i].tcount=0; |
| | } |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | for (std::size_t j=0;j<3;++j) |
| | vertices[t.v[j]].tcount++; |
| | } |
| | int tstart=0; |
| | for (std::size_t i=0;i<vertices.size();++i) |
| | { |
| | Vertex &v=vertices[i]; |
| | v.tstart=tstart; |
| | tstart+=v.tcount; |
| | v.tcount=0; |
| | } |
| |
|
| | |
| | refs.resize(triangles.size()*3); |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | for (std::size_t j=0;j<3;++j) |
| | { |
| | Vertex &v=vertices[t.v[j]]; |
| | refs[v.tstart+v.tcount].tid=i; |
| | refs[v.tstart+v.tcount].tvertex=j; |
| | v.tcount++; |
| | } |
| | } |
| |
|
| | |
| | if (iteration == 0) |
| | { |
| | std::vector<int> vcount,vids; |
| |
|
| | for (std::size_t i=0;i<vertices.size();++i) |
| | vertices[i].border=0; |
| |
|
| | for (std::size_t i=0;i<vertices.size();++i) |
| | { |
| | Vertex &v=vertices[i]; |
| | vcount.clear(); |
| | vids.clear(); |
| | for (int j=0; j<v.tcount; ++j) |
| | { |
| | int k=refs[v.tstart+j].tid; |
| | Triangle &t=triangles[k]; |
| | for (int k=0;k<3;++k) |
| | { |
| | std::size_t ofs=0; int id=t.v[k]; |
| | while(ofs<vcount.size()) |
| | { |
| | if (vids[ofs]==id) |
| | break; |
| | ofs++; |
| | } |
| | if(ofs==vcount.size()) |
| | { |
| | vcount.push_back(1); |
| | vids.push_back(id); |
| | } |
| | else |
| | { |
| | vcount[ofs]++; |
| | } |
| | } |
| | } |
| | for (std::size_t j=0;j<vcount.size();++j) { |
| | if (vcount[j]==1) |
| | vertices[vids[j]].border=1; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| |
|
| | void Simplify::compact_mesh() |
| | { |
| | int dst=0; |
| | for (std::size_t i=0;i<vertices.size();++i) |
| | { |
| | vertices[i].tcount=0; |
| | } |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | if (!triangles[i].deleted) |
| | { |
| | Triangle &t=triangles[i]; |
| | triangles[dst++]=t; |
| | for (std::size_t j=0;j<3;++j) |
| | vertices[t.v[j]].tcount=1; |
| | } |
| | } |
| |
|
| | triangles.resize(dst); |
| | dst=0; |
| | for (std::size_t i=0;i<vertices.size();++i) |
| | { |
| | if (vertices[i].tcount) |
| | { |
| | vertices[i].tstart=dst; |
| | vertices[dst].p=vertices[i].p; |
| | dst++; |
| | } |
| | } |
| | for (std::size_t i=0;i<triangles.size();++i) |
| | { |
| | Triangle &t=triangles[i]; |
| | for (std::size_t j=0;j<3;++j) |
| | t.v[j]=vertices[t.v[j]].tstart; |
| | } |
| | vertices.resize(dst); |
| | } |
| |
|
| | |
| |
|
| | double Simplify::vertex_error(const SymmetricMatrix& q, double x, double y, double z) |
| | { |
| | return q[0]*x*x + 2*q[1]*x*y + 2*q[2]*x*z + 2*q[3]*x + q[4]*y*y |
| | + 2*q[5]*y*z + 2*q[6]*y + q[7]*z*z + 2*q[8]*z + q[9]; |
| | } |
| |
|
| | |
| |
|
| | double Simplify::calculate_error(int id_v1, int id_v2, vec3f &p_result) |
| | { |
| | |
| |
|
| | SymmetricMatrix q = vertices[id_v1].q + vertices[id_v2].q; |
| | bool border = vertices[id_v1].border & vertices[id_v2].border; |
| | double error=0; |
| | double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); |
| |
|
| | if (det != 0 && !border) |
| | { |
| | |
| | p_result.x = -1/det*(q.det(1, 2, 3, 4, 5, 6, 5, 7 , 8)); |
| | p_result.y = 1/det*(q.det(0, 2, 3, 1, 5, 6, 2, 7 , 8)); |
| | p_result.z = -1/det*(q.det(0, 1, 3, 1, 4, 6, 2, 5, 8)); |
| | error = vertex_error(q, p_result.x, p_result.y, p_result.z); |
| | } |
| | else |
| | { |
| | |
| | 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; |
| | } |
| |
|
| | |
| | |
| |
|