1 /**************************************************************************** 2 * VCGLib o o * 3 * Visual and Computer Graphics Library o o * 4 * _ O _ * 5 * Copyright(C) 2004-2016 \/)\/ * 6 * Visual Computing Lab /\/| * 7 * ISTI - Italian National Research Council | * 8 * \ * 9 * All rights reserved. * 10 * * 11 * This program is free software; you can redistribute it and/or modify * 12 * it under the terms of the GNU General Public License as published by * 13 * the Free Software Foundation; either version 2 of the License, or * 14 * (at your option) any later version. * 15 * * 16 * This program is distributed in the hope that it will be useful, * 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * 20 * for more details. * 21 * * 22 ****************************************************************************/ 23 24 25 26 27 #ifndef __VCG_TRIMESHCOLLAPSE_QUADRIC_TEX_ 28 #define __VCG_TRIMESHCOLLAPSE_QUADRIC_TEX_ 29 30 #include <vcg/complex/algorithms/local_optimization.h> 31 #include <vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h> 32 #include <vcg/container/simple_temporary_data.h> 33 #include <vcg/math/quadric5.h> 34 namespace vcg 35 { 36 namespace tri 37 { 38 39 40 class TriEdgeCollapseQuadricTexParameter : public BaseParameterClass 41 { 42 public: 43 double BoundaryWeight; 44 double CosineThr; 45 float ExtraTCoordWeight; 46 bool NormalCheck; 47 double NormalThrRad; 48 bool OptimalPlacement; 49 bool PreserveBoundary; 50 bool PreserveTopology; 51 double QuadricEpsilon; 52 double QualityThr; 53 bool QualityQuadric; 54 bool SafeHeapUpdate; 55 double ScaleFactor; 56 bool ScaleIndependent; 57 bool UseArea; 58 bool UseVertexWeight; 59 TriEdgeCollapseQuadricTexParameter()60 TriEdgeCollapseQuadricTexParameter() 61 { 62 SetDefaultParams(); 63 } 64 SetDefaultParams()65 void SetDefaultParams() 66 { 67 this->BoundaryWeight=.5; 68 this->CosineThr = cos(M_PI/2); 69 this->ExtraTCoordWeight=0.0; 70 this->NormalCheck=false; 71 this->NormalThrRad=M_PI/2; 72 this->OptimalPlacement=true; 73 this->PreserveBoundary = false; 74 this->PreserveTopology = false; 75 this->QuadricEpsilon =1e-15; 76 this->QualityThr=.1; 77 this->QualityQuadric = false; 78 this->SafeHeapUpdate=false; 79 this->ScaleFactor=1.0; 80 this->ScaleIndependent=true; 81 this->UseArea=true; 82 this->UseVertexWeight=false; 83 } 84 }; 85 86 87 // This is a static class that contains the references for the simple temporary data for the current mesh. 88 // for each vertex we keep a classic Quadric3D and a vector of pairs texcoord+Quadric5D 89 90 template <class MeshType> 91 class QuadricTexHelper 92 { 93 public: 94 typedef typename MeshType::VertexType VertexType; 95 96 typedef SimpleTempData<typename MeshType::VertContainer, std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > > Quadric5Temp; 97 typedef SimpleTempData<typename MeshType::VertContainer, math::Quadric<double> > QuadricTemp; 98 QuadricTexHelper()99 QuadricTexHelper(){} 100 101 Init()102 static void Init(){} 103 104 // it allocs the std::pair for the vertex relativly to the texture coord parameter Alloc(VertexType * v,vcg::TexCoord2f & coord)105 static void Alloc(VertexType *v,vcg::TexCoord2f &coord) 106 { 107 std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > &qv = Vd(v); 108 Quadric5<double> newq5; 109 newq5.Zero(); 110 vcg::TexCoord2f newcoord; 111 newcoord.u() = coord.u(); 112 newcoord.v() = coord.v(); 113 114 newq5.Sum3(Qd3(v),coord.u(),coord.v()); 115 116 qv.push_back(std::pair<vcg::TexCoord2f ,Quadric5<double> >(newcoord,newq5)); 117 } 118 SumAll(VertexType * v,vcg::TexCoord2f & coord,Quadric5<double> & q)119 static void SumAll(VertexType *v,vcg::TexCoord2f &coord, Quadric5<double>& q) 120 { 121 std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > &qv = Vd(v); 122 123 for(size_t i = 0; i < qv.size(); i++) 124 { 125 vcg::TexCoord2f &f = qv[i].first; 126 if((f.u() == coord.u()) && (f.v() == coord.v())) 127 qv[i].second += q; 128 else 129 qv[i].second.Sum3(Qd3(v),f.u(),f.v()); 130 } 131 } 132 Contains(VertexType * v,vcg::TexCoord2f & coord)133 static bool Contains(VertexType *v,vcg::TexCoord2f &coord) 134 { 135 std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > &qv = Vd(v); 136 137 for(size_t i = 0; i < qv.size(); i++) 138 { 139 vcg::TexCoord2f &f = qv[i].first; 140 if((f.u() == coord.u()) && (f.v() == coord.v())) 141 return true; 142 } 143 144 return false; 145 } 146 Qd(VertexType * v,const vcg::TexCoord2f & coord)147 static Quadric5<double> &Qd(VertexType *v,const vcg::TexCoord2f &coord) 148 { 149 std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > &qv = Vd(v); 150 //assert(coord.N()>=0); 151 for(size_t i = 0; i < qv.size(); i++) 152 { 153 vcg::TexCoord2f &f = qv[i].first; 154 if((f.u() == coord.u()) && (f.v() == coord.v())) 155 return qv[i].second; 156 } 157 158 assert(0); 159 return qv[0].second; 160 } Qd3(VertexType * v)161 static math::Quadric<double> &Qd3(VertexType *v) {return TD3()[*v];} Qd3(VertexType & v)162 static math::Quadric<double> &Qd3(VertexType &v) {return TD3()[v];} 163 Vd(VertexType * v)164 static std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > &Vd(VertexType *v){return (TD()[*v]);} W(VertexType *)165 static typename VertexType::ScalarType W(VertexType * /*v*/) {return 1.0;} W(VertexType &)166 static typename VertexType::ScalarType W(VertexType & /*v*/) {return 1.0;} Merge(VertexType &,VertexType const &)167 static void Merge(VertexType & /*v_dest*/, VertexType const & /*v_del*/){} TDp()168 static Quadric5Temp* &TDp() {static Quadric5Temp *td; return td;} TD()169 static Quadric5Temp &TD() {return *TDp();} TDp3()170 static QuadricTemp* &TDp3() {static QuadricTemp *td3; return td3;} TD3()171 static QuadricTemp &TD3() {return *TDp3();} 172 }; 173 174 175 176 177 178 179 180 template<class TriMeshType, class VertexPair, class MYTYPE, class HelperType = tri::QInfoStandard<typename TriMeshType::VertexType> > 181 class TriEdgeCollapseQuadricTex: public vcg::tri::TriEdgeCollapse< TriMeshType, VertexPair, MYTYPE> 182 { 183 typedef HelperType QH; 184 typedef typename tri::TriEdgeCollapse<TriMeshType, VertexPair, MYTYPE>::HeapType HeapType; 185 typedef typename tri::TriEdgeCollapse<TriMeshType, VertexPair, MYTYPE>::HeapElem HeapElem; 186 typedef typename TriMeshType::FaceType FaceType; 187 typedef typename TriMeshType::VertexType VertexType; 188 typedef typename TriMeshType::CoordType CoordType; 189 typedef typename TriMeshType::CoordType::ScalarType ScalarType; 190 typedef typename TriMeshType::VertexPointer VertexPointer; 191 192 193 public: 194 TriEdgeCollapseQuadricTex(const VertexPair & p,int mark,BaseParameterClass * _pp)195 inline TriEdgeCollapseQuadricTex(const VertexPair &p, int mark, BaseParameterClass *_pp) 196 { 197 TriEdgeCollapseQuadricTexParameter *pp = (TriEdgeCollapseQuadricTexParameter *)_pp; 198 this->localMark = mark; 199 this->pos=p; 200 this->_priority = ComputePriority(pp); 201 } 202 203 // puntatori ai vertici che sono stati messi non-w per preservare il boundary WV()204 static std::vector<VertexPointer> & WV(){ 205 static std::vector<VertexPointer> _WV; return _WV; 206 }; 207 208 209 Params()210 static TriEdgeCollapseQuadricTexParameter & Params(){static TriEdgeCollapseQuadricTexParameter p; return p;} 211 212 // Final Clean up after the end of the simplification process Finalize(TriMeshType & m,HeapType &,BaseParameterClass * _pp)213 static void Finalize(TriMeshType &m,HeapType & /*h_ret*/, BaseParameterClass *_pp) 214 { 215 TriEdgeCollapseQuadricTexParameter *pp = (TriEdgeCollapseQuadricTexParameter *)_pp; 216 vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m); 217 218 // If we had the boundary preservation we should clean up the writable flags 219 if(pp->PreserveBoundary) 220 { 221 typename std::vector<VertexPointer>::iterator wvi; 222 for(wvi=WV().begin();wvi!=WV().end();++wvi) 223 if(!(*wvi)->IsD()) (*wvi)->SetW(); 224 } 225 } 226 227 228 229 matchVertexID(FaceType * f,VertexType * v)230 inline static int matchVertexID(FaceType *f,VertexType *v) 231 { 232 if(f->V(0)==v) return 0; 233 if(f->V(1)==v) return 1; 234 if(f->V(2)==v) return 2; 235 236 assert(0); return -1; 237 } 238 GetTexCoords(vcg::TexCoord2f & tcoord0_1,vcg::TexCoord2f & tcoord1_1,vcg::TexCoord2f & tcoord0_2,vcg::TexCoord2f & tcoord1_2)239 inline int GetTexCoords(vcg::TexCoord2f &tcoord0_1, vcg::TexCoord2f &tcoord1_1,vcg::TexCoord2f &tcoord0_2,vcg::TexCoord2f &tcoord1_2) 240 { 241 int ncoords = 0; 242 tcoord0_1.P()=Point2f(0.5f,0.5f); 243 tcoord1_1.P()=Point2f(0.5f,0.5f); 244 tcoord0_2.P()=Point2f(0.5f,0.5f); 245 tcoord1_2.P()=Point2f(0.5f,0.5f); 246 247 vcg::face::VFIterator<FaceType> vfi(this->pos.V(0)); 248 249 for(vfi.F() = this->pos.V(0)->VFp(), vfi.I() = this->pos.V(0)->VFi(); vfi.F()!=0; ++vfi ) // for all faces in v0 250 if(vfi.F()->V(0)==this->pos.V(1) || vfi.F()->V(1)==this->pos.V(1) || vfi.F()->V(2)==this->pos.V(1) ) // and v1 251 { 252 if(ncoords == 0) 253 { 254 tcoord0_1 = vfi.F()->WT(matchVertexID(vfi.F(),this->pos.V(0))); 255 tcoord1_1 = vfi.F()->WT(matchVertexID(vfi.F(),this->pos.V(1))); 256 } 257 else 258 { 259 tcoord0_2 = vfi.F()->WT(matchVertexID(vfi.F(),this->pos.V(0))); 260 tcoord1_2 = vfi.F()->WT(matchVertexID(vfi.F(),this->pos.V(1))); 261 262 if( 263 (tcoord0_1.u() == tcoord0_2.u()) && 264 (tcoord0_1.v() == tcoord0_2.v()) && 265 (tcoord1_1.u() == tcoord1_2.u()) && 266 (tcoord1_1.v() == tcoord1_2.v()) 267 ) 268 return 1; 269 else 270 return 2; 271 } 272 ncoords++; 273 } 274 275 return ncoords; 276 } 277 278 ComputePriority(BaseParameterClass * _pp)279 ScalarType ComputePriority(BaseParameterClass *_pp) 280 { 281 TriEdgeCollapseQuadricTexParameter *pp = (TriEdgeCollapseQuadricTexParameter *)_pp; 282 Quadric5<double> qsum1; 283 Quadric5<double> qsum2; 284 double min1[5]; 285 double min2[5]; 286 vcg::TexCoord2f tcoord0_1; 287 vcg::TexCoord2f tcoord1_1; 288 vcg::TexCoord2f tcoord0_2; 289 vcg::TexCoord2f tcoord1_2; 290 int ncoords; 291 292 ncoords = GetTexCoords(tcoord0_1,tcoord1_1,tcoord0_2,tcoord1_2); 293 294 return (ScalarType)ComputeMinimalsAndPriority(min1,min2,qsum1,qsum2,tcoord0_1,tcoord1_1,tcoord0_2,tcoord1_2,ncoords,pp); 295 } 296 297 298 /* 299 * the very important function that says how good is a collapse. 300 Originally is should be just the quadric error (e.g. you should choose the collapse that make the minimal quadric error) 301 but important correcting factors has to be applyed 302 - quality of the involved triangles 303 - normal checking 304 */ ComputeTexPriority(const double vv[5],Quadric5<double> & qsum,BaseParameterClass * _pp)305 ScalarType ComputeTexPriority(const double vv[5],Quadric5<double> &qsum, BaseParameterClass *_pp) 306 { 307 TriEdgeCollapseQuadricTexParameter *pp = (TriEdgeCollapseQuadricTexParameter *)_pp; 308 VertexType * v[2]; 309 v[0] = this->pos.V(0); 310 v[1] = this->pos.V(1); 311 312 assert(!math::IsNAN(vv[0])); 313 assert(!math::IsNAN(vv[1])); 314 assert(!math::IsNAN(vv[2])); 315 assert(!math::IsNAN(vv[3])); 316 assert(!math::IsNAN(vv[4])); 317 318 //// Move the two vertexe into new position (storing the old ones) 319 CoordType OldPos0=v[0]->P(); 320 CoordType OldPos1=v[1]->P(); 321 322 v[0]->P() = CoordType(vv[0],vv[1],vv[2]); 323 //v[0]->P() = (v[0]->P()+v[1]->P())/2.0; 324 v[1]->P()=v[0]->P(); 325 326 double QuadErr = qsum.Apply(vv); 327 328 //// Rescan faces and compute quality and difference between normals 329 int i; 330 331 double qt, MinQual = 1e100; 332 vcg::face::VFIterator<FaceType> x(this->pos.V(0)); 333 334 double ndiff,MinCos = 1e100; // minimo coseno di variazione di una normale della faccia 335 // (e.g. max angle) Mincos varia da 1 (normali coincidenti) a 336 // -1 (normali opposte); 337 338 for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v0 339 if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1 340 { 341 qt= QualityFace(*x.F()); 342 if(qt<MinQual) MinQual=qt; 343 if(pp->NormalCheck){ 344 CoordType nn=TriangleNormal(*x.F()).Normalize(); 345 ndiff=nn.dot(x.F()->N()) / x.F()->N().Norm(); 346 if(ndiff<MinCos) MinCos=ndiff; 347 assert(!math::IsNAN(ndiff)); 348 } 349 } 350 for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v1 351 if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0 352 { 353 qt= QualityFace(*x.F()); 354 if(qt<MinQual) MinQual=qt; 355 if(pp->NormalCheck){ 356 CoordType nn=TriangleNormal(*x.F()).Normalize(); 357 ndiff=nn.dot(x.F()->N() / x.F()->N().Norm()); 358 if(ndiff<MinCos) MinCos=ndiff; 359 assert(!math::IsNAN(ndiff)); 360 } 361 362 } 363 364 365 // All collapses involving triangles with quality larger than <QualityThr> have no penalty; 366 if(MinQual>pp->QualityThr) MinQual=pp->QualityThr; 367 if(QuadErr<1e-15) QuadErr=1e-15; // Do not use zero quality penalties 368 369 this->_priority = (ScalarType)(QuadErr / MinQual); // the priority of collapses that create low quality triangles has a penalty (it is increased) 370 371 372 if(pp->NormalCheck){ 373 if(MinCos<pp->CosineThr) this->_priority *=1000; // gross penalty to collapses that move too much the original normals. 374 } 375 376 377 378 //Restore old position of v0 and v1 379 v[0]->P()=OldPos0; 380 v[1]->P()=OldPos1; 381 return this->_priority; 382 } 383 ComputeMinimalsAndPriority(double dest_1[5],double dest_2[5],Quadric5<double> & qsum_1,Quadric5<double> & qsum_2,const vcg::TexCoord2f & tcoord0_1,const vcg::TexCoord2f & tcoord1_1,const vcg::TexCoord2f & tcoord0_2,const vcg::TexCoord2f & tcoord1_2,int ncoords,BaseParameterClass * _pp)384 inline ScalarType ComputeMinimalsAndPriority(double dest_1[5], 385 double dest_2[5], 386 Quadric5<double> &qsum_1, 387 Quadric5<double> &qsum_2, 388 const vcg::TexCoord2f &tcoord0_1, 389 const vcg::TexCoord2f &tcoord1_1, 390 const vcg::TexCoord2f &tcoord0_2, 391 const vcg::TexCoord2f &tcoord1_2, 392 int ncoords, 393 BaseParameterClass *_pp) 394 { 395 TriEdgeCollapseQuadricTexParameter *pp = (TriEdgeCollapseQuadricTexParameter *)_pp; 396 double tmp1[5]; 397 double tmp2[5]; 398 ScalarType priority1; 399 ScalarType priority2; 400 401 assert(!math::IsNAN(tcoord0_1.u())); 402 assert(!math::IsNAN(tcoord0_1.v())); 403 assert(!math::IsNAN(tcoord1_1.u())); 404 assert(!math::IsNAN(tcoord1_1.v())); 405 assert(!math::IsNAN(tcoord0_2.u())); 406 assert(!math::IsNAN(tcoord0_2.v())); 407 assert(!math::IsNAN(tcoord1_2.u())); 408 assert(!math::IsNAN(tcoord1_2.v())); 409 410 411 tmp1[0] = this->pos.V(0)->P().X(); 412 tmp1[1] = this->pos.V(0)->P().Y(); 413 tmp1[2] = this->pos.V(0)->P().Z(); 414 tmp1[3] = tcoord0_1.u(); 415 tmp1[4] = tcoord0_1.v(); 416 417 tmp2[0] = this->pos.V(1)->P().X(); 418 tmp2[1] = this->pos.V(1)->P().Y(); 419 tmp2[2] = this->pos.V(1)->P().Z(); 420 tmp2[3] = tcoord1_1.u(); 421 tmp2[4] = tcoord1_1.v(); 422 423 assert(QH::Qd(this->pos.V(0),tcoord0_1).IsValid()); 424 assert(QH::Qd(this->pos.V(1),tcoord1_1).IsValid()); 425 426 qsum_1 = QH::Qd(this->pos.V(0),tcoord0_1); 427 qsum_1 += QH::Qd(this->pos.V(1),tcoord1_1); 428 429 ComputeMinimal(dest_1,tmp1,tmp2,qsum_1,pp); 430 priority1 = ComputeTexPriority(dest_1,qsum_1,pp); 431 432 if(ncoords < 2) 433 return priority1*(1 + (pp->ExtraTCoordWeight)*(QH::Vd(this->pos.V(0)).size()+ QH::Vd(this->pos.V(1)).size() - 2)); 434 435 436 tmp1[3] = tcoord0_2.u(); 437 tmp1[4] = tcoord0_2.v(); 438 439 tmp2[3] = tcoord1_2.u(); 440 tmp2[4] = tcoord1_2.v(); 441 442 assert(QH::Qd(this->pos.V(0),tcoord0_2).IsValid()); 443 assert(QH::Qd(this->pos.V(1),tcoord1_2).IsValid()); 444 445 qsum_2 = QH::Qd(this->pos.V(0),tcoord0_2); 446 qsum_2 += QH::Qd(this->pos.V(1),tcoord1_2); 447 448 ComputeMinimal(dest_2,tmp1,tmp2,qsum_2,pp); 449 priority2 = ComputeTexPriority(dest_2,qsum_2,pp); 450 451 if(priority1 > priority2) 452 { 453 ComputeMinimalWithGeoContraints(dest_2,tmp1,tmp2,qsum_2,dest_1,pp); 454 priority2 = ComputeTexPriority(dest_2,qsum_2,pp); 455 } 456 else 457 { 458 ComputeMinimalWithGeoContraints(dest_1,tmp1,tmp2,qsum_1,dest_2,pp); 459 priority1 = ComputeTexPriority(dest_1,qsum_1,pp); 460 } 461 462 463 this->_priority = std::max(priority1, priority2)*(1 + (pp->ExtraTCoordWeight)*(QH::Vd(this->pos.V(0)).size()+QH::Vd(this->pos.V(1)).size() - 2)); 464 465 return this->_priority; 466 } 467 ComputeMinimal(double vv[5],const double v0[5],const double v1[5],const Quadric5<double> qsum,BaseParameterClass * _pp)468 inline void ComputeMinimal(double vv[5],const double v0[5],const double v1[5], const Quadric5<double> qsum,BaseParameterClass *_pp) 469 { 470 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 471 bool rt=qsum.Minimum(vv); 472 // if the computation of the minimum fails we choose between the two edge points and the middle one. 473 // Switch to this branch also in the case of not using the optimal placement. 474 if(!rt || !pp->OptimalPlacement ) 475 { 476 vv[0] = (v0[0] + v1[0])/2; 477 vv[1] = (v0[1] + v1[1])/2; 478 vv[2] = (v0[2] + v1[2])/2; 479 vv[3] = (v0[3] + v1[3])/2; 480 vv[4] = (v0[4] + v1[4])/2; 481 482 // In the case of not using the optimal placement we have to be sure that the middle value is discarded. 483 double qvx= std::numeric_limits<float>::max(); 484 if(pp->OptimalPlacement) 485 qvx = qsum.Apply(vv); 486 487 488 double qv0=qsum.Apply(v0); 489 double qv1=qsum.Apply(v1); 490 491 492 if(qv0<qvx) 493 { 494 vv[0] = v0[0]; 495 vv[1] = v0[1]; 496 vv[2] = v0[2]; 497 vv[3] = v0[3]; 498 vv[4] = v0[4]; 499 } 500 501 if(qv1<qvx && qv1<qv0) 502 { 503 vv[0] = v1[0]; 504 vv[1] = v1[1]; 505 vv[2] = v1[2]; 506 vv[3] = v1[3]; 507 vv[4] = v1[4]; 508 } 509 } 510 511 assert(!math::IsNAN(vv[0])); 512 assert(!math::IsNAN(vv[1])); 513 assert(!math::IsNAN(vv[2])); 514 assert(!math::IsNAN(vv[3])); 515 assert(!math::IsNAN(vv[4])); 516 } 517 ComputeMinimalWithGeoContraints(double vv[5],const double v0[5],const double v1[5],const Quadric5<double> qsum,const double geo[5],BaseParameterClass * _pp)518 inline void ComputeMinimalWithGeoContraints(double vv[5],const double v0[5],const double v1[5], const Quadric5<double> qsum, const double geo[5],BaseParameterClass *_pp) 519 { 520 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 521 bool rt=qsum.MinimumWithGeoContraints(vv,geo); 522 // if the computation of the minimum fails we choose between the two edge points and the middle one. 523 // Switch to this branch also in the case of not using the optimal placement. 524 if(!rt || !pp->OptimalPlacement) { 525 double qvx = std::numeric_limits<float>::max(); 526 vv[0] = geo[0]; 527 vv[1] = geo[1]; 528 vv[2] = geo[2]; 529 if(pp->OptimalPlacement) 530 { 531 vv[3] = (v0[3] + v1[3])/2; 532 vv[4] = (v0[4] + v1[4])/2; 533 qvx=qsum.Apply(vv); 534 } 535 vv[3] = v0[3]; 536 vv[4] = v0[4]; 537 538 double qv0=qsum.Apply(vv); 539 540 vv[3] = v1[3]; 541 vv[4] = v1[4]; 542 543 double qv1=qsum.Apply(v1); 544 545 vv[3] = (v0[3] + v1[3])/2; 546 vv[4] = (v0[4] + v1[4])/2; 547 548 if(qv0<qvx) 549 { 550 vv[3] = v0[3]; 551 vv[4] = v0[4]; 552 } 553 554 if(qv1<qvx && qv1<qv0) 555 { 556 vv[3] = v1[3]; 557 vv[4] = v1[4]; 558 } 559 } 560 561 } 562 InitQuadric(TriMeshType & m,BaseParameterClass * _pp)563 static void InitQuadric(TriMeshType &m,BaseParameterClass *_pp) 564 { 565 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 566 typename TriMeshType::FaceIterator pf; 567 HelperType::Init(); 568 569 for(pf=m.face.begin();pf!=m.face.end();++pf) 570 if( !(*pf).IsD() && (*pf).IsR() ) 571 if((*pf).V(0)->IsR() &&(*pf).V(1)->IsR() &&(*pf).V(2)->IsR()) 572 { 573 Quadric5<double> q; 574 q.byFace(*pf, QH::Qd3((*pf).V(0)), QH::Qd3((*pf).V(1)), QH::Qd3((*pf).V(2)),pp->QualityQuadric,pp->BoundaryWeight); 575 576 for(int j=0;j<3;++j) 577 if( (*pf).V(j)->IsW()) 578 { 579 if(!HelperType::Contains((*pf).V(j),(*pf).WT(j))) 580 { 581 HelperType::Alloc((*pf).V(j),(*pf).WT(j)); 582 } 583 assert(!math::IsNAN((*pf).WT(j).u())); 584 assert(!math::IsNAN((*pf).WT(j).v())); 585 HelperType::SumAll((*pf).V(j),(*pf).WT(j),q); 586 } 587 588 } 589 590 } 591 Init(TriMeshType & m,HeapType & h_ret,BaseParameterClass * _pp)592 static void Init(TriMeshType &m,HeapType&h_ret,BaseParameterClass *_pp) 593 { 594 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 595 typename TriMeshType::VertexIterator vi; 596 typename TriMeshType::FaceIterator pf; 597 598 vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m); 599 vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m); 600 601 if(pp->PreserveBoundary ) 602 { 603 WV().clear(); 604 for(pf=m.face.begin();pf!=m.face.end();++pf) 605 if( !(*pf).IsD() && (*pf).IsW() ) 606 for(int j=0;j<3;++j) 607 if((*pf).IsB(j)) 608 { 609 if((*pf).V(j)->IsW()) {(*pf).V(j)->ClearW(); WV().push_back((*pf).V(j));} 610 if((*pf).V1(j)->IsW()) {(*pf).V1(j)->ClearW();WV().push_back((*pf).V1(j));} 611 } 612 } 613 614 InitQuadric(m,pp); 615 616 // Initialize the heap with all the possible collapses 617 for(vi=m.vert.begin();vi!=m.vert.end();++vi) 618 if((*vi).IsRW()) 619 { 620 vcg::face::VFIterator<FaceType> x; 621 for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x){ 622 x.V1()->ClearV(); 623 x.V2()->ClearV(); 624 } 625 for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++x ) 626 { 627 assert(x.F()->V(x.I())==&(*vi)); 628 if((x.V0()<x.V1()) && x.V1()->IsRW() && !x.V1()->IsV()){ 629 x.V1()->SetV(); 630 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(x.V0(),x.V1()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),pp ))); 631 } 632 if((x.V0()<x.V2()) && x.V2()->IsRW()&& !x.V2()->IsV()){ 633 x.V2()->SetV(); 634 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(x.V0(),x.V2()),TriEdgeCollapse< TriMeshType,VertexPair,MYTYPE>::GlobalMark(),pp ))); 635 } 636 } 637 } 638 make_heap(h_ret.begin(),h_ret.end()); 639 } 640 UpdateHeap(HeapType & h_ret,BaseParameterClass * _pp)641 inline void UpdateHeap(HeapType & h_ret,BaseParameterClass *_pp) 642 { 643 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 644 this->GlobalMark()++; 645 VertexType *v[2]; 646 v[0]= this->pos.V(0); 647 v[1]= this->pos.V(1); 648 v[1]->IMark() = this->GlobalMark(); 649 650 // First loop around the remaining vertex to unmark visited flags 651 vcg::face::VFIterator<FaceType> vfi(v[1]); 652 653 while (!vfi.End()){ 654 vfi.V1()->ClearV(); 655 vfi.V2()->ClearV(); 656 ++vfi; 657 } 658 659 // Second Loop 660 vfi = face::VFIterator<FaceType>(v[1]); 661 while (!vfi.End()) 662 { 663 assert(!vfi.F()->IsD()); 664 for (int j=0;j<3;j++) 665 { 666 667 if( !(vfi.V1()->IsV()) && vfi.V1()->IsRW()) 668 { 669 vfi.V1()->SetV(); 670 671 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(vfi.V0(),vfi.V1()), this->GlobalMark(),pp))); 672 std::push_heap(h_ret.begin(),h_ret.end()); 673 } 674 675 if( !(vfi.V2()->IsV()) && vfi.V2()->IsRW()) 676 { 677 vfi.V2()->SetV(); 678 679 h_ret.push_back(HeapElem(new MYTYPE(VertexPair(vfi.V0(),vfi.V2()),this->GlobalMark(),pp))); 680 std::push_heap(h_ret.begin(),h_ret.end()); 681 } 682 } 683 ++vfi; 684 } 685 } 686 Execute(TriMeshType & m,BaseParameterClass * _pp)687 void Execute(TriMeshType &m, BaseParameterClass *_pp) 688 { 689 tri::TriEdgeCollapseQuadricTexParameter *pp =(tri::TriEdgeCollapseQuadricTexParameter *)_pp; 690 Quadric5<double> qsum1; 691 Quadric5<double> qsum2; 692 double min1[5]; 693 double min2[5]; 694 vcg::TexCoord2f tcoord0_1; 695 vcg::TexCoord2f tcoord1_1; 696 vcg::TexCoord2f tcoord0_2; 697 vcg::TexCoord2f tcoord1_2; 698 vcg::TexCoord2f newtcoord1; 699 vcg::TexCoord2f newtcoord2; 700 std::vector<std::pair<vcg::TexCoord2f ,Quadric5<double> > > qv; 701 int ncoords; 702 VertexType * v[2]; 703 v[0] = this->pos.V(0); 704 v[1] = this->pos.V(1); 705 706 math::Quadric<double> qsum3 = QH::Qd3(v[0]); 707 qsum3 += QH::Qd3(v[1]); 708 709 ncoords = GetTexCoords(tcoord0_1,tcoord1_1,tcoord0_2,tcoord1_2); 710 711 ComputeMinimalsAndPriority(min1,min2,qsum1,qsum2,tcoord0_1,tcoord1_1,tcoord0_2,tcoord1_2,ncoords,pp); 712 713 CoordType newPos(min1[0],min1[1],min1[2]); /* it's the same as min2[0],min2[1],min2[2] since the geometrical 714 constraint has been imposed during the re-computation of the other minimal */ 715 716 717 EdgeCollapser<TriMeshType,VertexPair>::Do(m, this->pos, newPos); 718 //DoCollapse(m, this->pos, newPos ); // v0 is deleted and v1 take the new position 719 720 vcg::TexCoord2f newtcoord; 721 Quadric5<double> newq; 722 723 724 725 newtcoord.u() = (float)min1[3]; 726 newtcoord.v() = (float)min1[4]; 727 assert(!math::IsNAN(newtcoord.u())); 728 assert(!math::IsNAN(newtcoord.v())); 729 newtcoord1 = newtcoord; 730 newq = qsum1; 731 732 qv.push_back(std::pair<vcg::TexCoord2f ,Quadric5<double> >(newtcoord,newq)); 733 734 if(ncoords > 1) 735 { 736 newtcoord.u() = min2[3]; 737 newtcoord.v() = min2[4]; 738 newtcoord2 = newtcoord; 739 newq = qsum2; 740 741 qv.push_back(std::pair<vcg::TexCoord2f ,Quadric5<double> >(newtcoord2,newq)); 742 } 743 744 745 vcg::face::VFIterator<FaceType> vfi(v[1]); 746 while (!vfi.End()) 747 { 748 vcg::TexCoord2f & tcoords = vfi.F()->WT(matchVertexID(vfi.F(),v[1])); 749 750 if( 751 ((tcoords.u() == tcoord0_1.u()) && (tcoords.v() == tcoord0_1.v())) || 752 ((tcoords.u() == tcoord1_1.u()) && (tcoords.v() == tcoord1_1.v())) 753 ) 754 { 755 tcoords.u() = newtcoord1.u(); 756 tcoords.v() = newtcoord1.v(); 757 } 758 else if( 759 (ncoords > 1) && 760 ( 761 ((tcoords.u() == tcoord0_2.u()) && (tcoords.v() == tcoord0_2.v())) || 762 ((tcoords.u() == tcoord1_2.u()) && (tcoords.v() == tcoord1_2.v())) 763 ) 764 ) 765 { 766 tcoords.u()= newtcoord2.u(); 767 tcoords.v()= newtcoord2.v(); 768 } 769 else 770 { 771 newtcoord = tcoords; 772 773 if(QH::Contains(v[0],tcoords)) 774 { 775 newq = QH::Qd(v[0],tcoords); 776 newq.Sum3(QH::Qd3(v[1]),tcoords.u(),tcoords.v()); 777 } 778 else if(QH::Contains(v[1],tcoords)) 779 { 780 newq = QH::Qd(v[1],tcoords); 781 newq.Sum3(QH::Qd3(v[0]),tcoords.u(),tcoords.v()); 782 } 783 else 784 assert(0); 785 786 qv.push_back(std::pair<vcg::TexCoord2f ,Quadric5<double> >(newtcoord,newq)); 787 } 788 789 ++vfi; 790 } 791 QH::Qd3(v[1]) = qsum3; 792 QH::Vd(v[1]) = qv; 793 794 } 795 796 }; 797 798 799 800 } // namespace tri 801 } // namespace vcg 802 #endif 803