1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * A versatile mesh processing toolbox                             o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005                                                \/)\/    *
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 #include <vcg/math/base.h>
24 
25 #include <Eigen/Sparse>
26 
27 #include <float.h>
28 #include <stdlib.h>
29 #include "filter_texture.h"
30 #include "pushpull.h"
31 #include "rastering.h"
32 #include <vcg/complex/algorithms/update/texture.h>
33 #include<wrap/io_trimesh/export_ply.h>
34 #include <vcg/complex/algorithms/parametrization/voronoi_atlas.h>
35 
36 using namespace vcg;
37 
FilterTexturePlugin()38 FilterTexturePlugin::FilterTexturePlugin()
39 {
40     typeList << FP_VORONOI_ATLAS
41             << FP_UV_WEDGE_TO_VERTEX
42             << FP_UV_VERTEX_TO_WEDGE
43             << FP_BASIC_TRIANGLE_MAPPING
44             << FP_SET_TEXTURE
45             << FP_PLANAR_MAPPING
46             << FP_COLOR_TO_TEXTURE
47             << FP_TRANSFER_TO_TEXTURE
48             << FP_TEX_TO_VCOLOR_TRANSFER;
49 
50     foreach(FilterIDType tt , types())
51         actionList << new QAction(filterName(tt), this);
52 }
53 
filterName(FilterIDType filterId) const54 QString FilterTexturePlugin::filterName(FilterIDType filterId) const
55 {
56     switch(filterId)
57     {
58     case FP_VORONOI_ATLAS : return QString("Parametrization: Voronoi Atlas");
59     case FP_UV_WEDGE_TO_VERTEX : return QString("Convert PerWedge UV into PerVertex UV");
60     case FP_UV_VERTEX_TO_WEDGE : return QString("Convert PerVertex UV into PerWedge UV");
61     case FP_BASIC_TRIANGLE_MAPPING : return QString("Parametrization: Trivial Per-Triangle");
62     case FP_PLANAR_MAPPING : return QString("Parametrization: Flat Plane");
63     case FP_SET_TEXTURE : return QString("Set Texture");
64 	case FP_COLOR_TO_TEXTURE : return QString("Transfer: Vertex Color to Texture");
65     case FP_TRANSFER_TO_TEXTURE : return QString("Transfer: Vertex Attributes to Texture (1 or 2 meshes)");
66     case FP_TEX_TO_VCOLOR_TRANSFER : return QString("Transfer: Texture to Vertex Color (1 or 2 meshes)");
67     default : assert(0);
68     }
69     return {};
70 }
71 
72 // Info() must return the longer string describing each filtering action
73 // (this string is used in the About plugin dialog)
filterInfo(FilterIDType filterId) const74 QString FilterTexturePlugin::filterInfo(FilterIDType filterId) const
75 {
76     switch(filterId)
77     {
78     case FP_VORONOI_ATLAS :  return QString("Build an atlased parametrization based on a geodesic voronoi partitioning of the surface and parametrizing each region using Harmonic Mapping. For the  parametrization of the disk like voronoi regions the used method is: <br><b>Ulrich Pinkall, Konrad Polthier</b><br>\
79                                             <i>Computing Discrete Minimal Surfaces and Their Conjugates</i> <br>\
80                                             Experimental Mathematics, Vol 2 (1), 1993<br> .");
81     case FP_UV_WEDGE_TO_VERTEX : return QString("Converts per Wedge Texture Coordinates to per Vertex Texture Coordinates splitting vertices with not coherent Wedge coordinates.");
82     case FP_UV_VERTEX_TO_WEDGE : return QString("Converts per Vertex Texture Coordinates to per Wedge Texture Coordinates. It does not merge superfluous vertices...");
83     case FP_BASIC_TRIANGLE_MAPPING : return QString("Builds a trivial triangle-by-triangle parametrization. <br> Two methods are provided, the first maps maps all triangles into equal sized triangles, while the second one adapt the size of the triangles in texture space to their original size.");
84     case FP_PLANAR_MAPPING : return QString("Builds a trivial flat-plane parametrization.");
85     case FP_SET_TEXTURE : return QString("Set a texture associated with current mesh parametrization.<br>"
86                                          "If the texture provided exists it will be simply associated to the current mesh else a dummy texture will be created and saved in the same directory.");
87     case FP_COLOR_TO_TEXTURE : return QString("Fills the specified texture using per-vertex color data of the mesh.");
88     case FP_TRANSFER_TO_TEXTURE : return QString("Transfer texture color, vertex color or normal from one mesh the texture of another mesh. This may be useful to restore detail lost in simplification, or resample a texture in a different parametrization.");
89     case FP_TEX_TO_VCOLOR_TRANSFER : return QString("Generates Vertex Color values picking color from a texture (same mesh or another mesh).");
90     default : assert(0);
91     }
92     return QString("Unknown Filter");
93 }
94 
getPreConditions(QAction * a) const95 int FilterTexturePlugin::getPreConditions(QAction *a) const
96 {
97     switch (ID(a))
98     {
99     case FP_UV_WEDGE_TO_VERTEX : return MeshModel::MM_WEDGTEXCOORD;
100     case FP_UV_VERTEX_TO_WEDGE : return MeshModel::MM_VERTTEXCOORD;
101     case FP_BASIC_TRIANGLE_MAPPING :
102     case FP_VORONOI_ATLAS :
103     case FP_PLANAR_MAPPING : return MeshModel::MM_FACENUMBER;
104     case FP_SET_TEXTURE : return MeshModel::MM_WEDGTEXCOORD;
105     case FP_COLOR_TO_TEXTURE : return MeshModel::MM_VERTCOLOR | MeshModel::MM_WEDGTEXCOORD;
106     case FP_TRANSFER_TO_TEXTURE : return MeshModel::MM_NONE;
107     case FP_TEX_TO_VCOLOR_TRANSFER : return MeshModel::MM_NONE;
108     default: assert(0);
109     }
110     return MeshModel::MM_NONE;
111 }
112 
getRequirements(QAction * a)113 int FilterTexturePlugin::getRequirements(QAction *a)
114 {
115     switch (ID(a))
116     {
117     case FP_VORONOI_ATLAS :
118     case FP_UV_WEDGE_TO_VERTEX :
119     case FP_UV_VERTEX_TO_WEDGE :
120     case FP_BASIC_TRIANGLE_MAPPING :
121     case FP_PLANAR_MAPPING :
122     case FP_SET_TEXTURE : return MeshModel::MM_NONE;
123     case FP_COLOR_TO_TEXTURE : return MeshModel::MM_FACEFACETOPO;
124     case FP_TRANSFER_TO_TEXTURE : return MeshModel::MM_NONE;
125     case FP_TEX_TO_VCOLOR_TRANSFER : return MeshModel::MM_NONE;
126     default: assert(0);
127     }
128     return MeshModel::MM_NONE;
129 }
130 
postCondition(QAction * a) const131 int FilterTexturePlugin::postCondition( QAction *a) const
132 {
133     switch (ID(a))
134     {
135 	case FP_VORONOI_ATLAS: return MeshModel::MM_WEDGTEXCOORD;
136 	case FP_UV_WEDGE_TO_VERTEX: return MeshModel::MM_VERTTEXCOORD;
137     case FP_UV_VERTEX_TO_WEDGE : return MeshModel::MM_WEDGTEXCOORD;
138     case FP_PLANAR_MAPPING : return MeshModel::MM_WEDGTEXCOORD;
139     case FP_BASIC_TRIANGLE_MAPPING : return MeshModel::MM_WEDGTEXCOORD;
140     case FP_SET_TEXTURE : return MeshModel::MM_NONE;
141     case FP_COLOR_TO_TEXTURE : return MeshModel::MM_NONE;
142     case FP_TRANSFER_TO_TEXTURE : return MeshModel::MM_NONE;
143 	case FP_TEX_TO_VCOLOR_TRANSFER: return MeshModel::MM_VERTCOLOR;
144     default: assert(0);
145     }
146     return MeshModel::MM_NONE;
147 }
148 
149 // The FilterClass describes in which generic class of filters it fits.
150 // This choice affect the submenu in which each filter will be placed
151 // More than a single class can be chosen.
getClass(QAction * a)152 FilterTexturePlugin::FilterClass FilterTexturePlugin::getClass(QAction *a)
153 {
154     switch(ID(a))
155     {
156     case FP_VORONOI_ATLAS :
157     case FP_UV_WEDGE_TO_VERTEX :
158     case FP_UV_VERTEX_TO_WEDGE :
159     case FP_BASIC_TRIANGLE_MAPPING :
160     case FP_PLANAR_MAPPING :
161     case FP_SET_TEXTURE :
162     case FP_COLOR_TO_TEXTURE :
163     case FP_TRANSFER_TO_TEXTURE : return MeshFilterInterface::Texture;
164     case FP_TEX_TO_VCOLOR_TRANSFER : return FilterClass(MeshFilterInterface::VertexColoring + MeshFilterInterface::Texture);
165     default : assert(0);
166     }
167     return MeshFilterInterface::Generic;
168 }
169 
extractFilenameWOExt(MeshModel * mm)170 static QString extractFilenameWOExt(MeshModel* mm)
171 {
172     QFileInfo fi(mm->fullName());
173     return fi.baseName();
174 }
175 
176 // This function define the needed parameters for each filter. Return true if the filter has some parameters
177 // it is called every time, so you can set the default value of parameters according to the mesh
178 // For each parameter you need to define,
179 // - the name of the parameter,
180 // - the string shown in the dialog
181 // - the default value
182 // - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
initParameterSet(QAction * action,MeshDocument & md,RichParameterSet & parlst)183 void FilterTexturePlugin::initParameterSet(QAction *action, MeshDocument &md, RichParameterSet & parlst)
184 {
185     switch(ID(action)) {
186     case FP_VORONOI_ATLAS :
187       parlst.addParam(new RichInt("regionNum", 10, "Approx. Region Num", "An estimation of the number of regions that must be generated. Smaller regions could lead to parametrizations with smaller distortion."));
188       parlst.addParam(new RichBool("overlapFlag", false, "Overlap", "If checked the resulting parametrization will be composed by <i>overlapping</i> regions, e.g. the resulting mesh will have duplicated faces: each region will have a ring of ovelapping duplicate faces that will ensure that border regions will be parametrized in the atlas twice. This is quite useful for building mipmap robust atlases"));
189         break;
190     case FP_UV_WEDGE_TO_VERTEX :
191         break;
192     case FP_PLANAR_MAPPING :
193       parlst.addParam(new RichEnum("projectionPlane", 0, QStringList("XY") << "XZ"<<"YZ","Projection plane","Choose the projection plane"));
194       parlst.addParam(new RichBool("aspectRatio", false, "Preserve Ratio", "If checked the resulting parametrization will preserve the original apsect ratio of the model otherwise it will fill up the whole 0..1 uv space"));
195 	  parlst.addParam(new RichFloat("sideGutter", 0.0, "Side Gutter", "Leave an empty space around the parametrization area of the specified size (in texture space); accepted range [0.0 - 0.5]."));
196       break;
197     case FP_BASIC_TRIANGLE_MAPPING :
198         parlst.addParam(new RichInt("sidedim", 0, "Quads per line", "Indicates how many triangles have to be put on each line (every quad contains two triangles)\nLeave 0 for automatic calculation"));
199         parlst.addParam(new RichInt("textdim", 1024, "Texture Dimension (px)", "Gives an indication on how big the texture is"));
200         parlst.addParam(new RichInt("border", 2, "Inter-Triangle border (px)", "Specifies how many pixels to be left between triangles in parametrization domain"));
201         parlst.addParam(new RichEnum("method", 1, QStringList("Basic") << "Space-optimizing", "Method", "Choose space optimizing to map smaller faces into smaller triangles in parametrizazion domain"));
202         break;
203     case FP_SET_TEXTURE : {
204             QString fileName = extractFilenameWOExt(md.mm());
205             fileName = fileName.append(".png");
206             parlst.addParam(new RichString("textName", fileName, "Texture file", "If the file exists it will be associated to the mesh else a dummy one will be created"));
207             parlst.addParam(new RichInt("textDim", 1024, "Texture Dimension (px)", "If the named texture doesn't exists the dummy one will be squared with this size"));
208         }
209         break;
210     case FP_COLOR_TO_TEXTURE : {
211             QString fileName = extractFilenameWOExt(md.mm());
212             fileName = fileName.append("_tex.png");
213             parlst.addParam(new RichString("textName", fileName, "Texture file", "The texture file to be created"));
214             parlst.addParam(new RichInt("textW", 1024, "Texture width (px)", "The texture width"));
215             parlst.addParam(new RichInt("textH", 1024, "Texture height (px)", "The texture height"));
216             parlst.addParam(new RichBool("overwrite", false, "Overwrite texture", "if current mesh has a texture will be overwritten (with provided texture dimension)"));
217             parlst.addParam(new RichBool("assign", false, "Assign texture", "assign the newly created texture"));
218             parlst.addParam(new RichBool("pullpush", true, "Fill texture", "if enabled the unmapped texture space is colored using a pull push filling algorithm, if false is set to black"));
219         }
220         break;
221     case FP_TRANSFER_TO_TEXTURE : {
222             QString fileName = extractFilenameWOExt(md.mm());
223             fileName = fileName.append("_tex.png");
224             parlst.addParam(new RichMesh ("sourceMesh",md.mm(),&md, "Source Mesh",
225                                           "The mesh that contains the source data that we want to transfer"));
226             parlst.addParam(new RichMesh ("targetMesh",md.mm(),&md, "Target Mesh",
227                                           "The mesh whose texture will be filled according to source mesh data"));
228             parlst.addParam(new RichEnum("AttributeEnum", 0, QStringList("Vertex Color")  << "Vertex Normal" << "Vertex Quality"<< "Texture Color", "Color Data Source",
229                                          "Choose what attribute has to be transferred onto the target texture. You can choose bettween Per vertex attributes (clor,normal,quality) or to transfer color information from source mesh texture"));
230             parlst.addParam(new RichAbsPerc("upperBound", md.mm()->cm.bbox.Diag()/50.0, 0.0f, md.mm()->cm.bbox.Diag(),
231                                             tr("Max Dist Search"), tr("Sample points for which we do not find anything within this distance are rejected and not considered for recovering data")));
232             parlst.addParam(new RichString("textName", fileName, "Texture file", "The texture file to be created"));
233             parlst.addParam(new RichInt("textW", 1024, "Texture width (px)", "The texture width"));
234             parlst.addParam(new RichInt("textH", 1024, "Texture height (px)", "The texture height"));
235             parlst.addParam(new RichBool("overwrite", false, "Overwrite Target Mesh Texture", "if target mesh has a texture will be overwritten (with provided texture dimension)"));
236             parlst.addParam(new RichBool("assign", false, "Assign Texture", "assign the newly created texture to target mesh"));
237             parlst.addParam(new RichBool("pullpush", true, "Fill texture", "if enabled the unmapped texture space is colored using a pull push filling algorithm, if false is set to black"));
238         }
239         break;
240     case FP_TEX_TO_VCOLOR_TRANSFER : {
241             parlst.addParam(new RichMesh ("sourceMesh",md.mm(),&md, "Source Mesh",
242                                           "The mesh with associated texture that we want to sample from"));
243             parlst.addParam(new RichMesh ("targetMesh",md.mm(),&md, "Target Mesh",
244                                           "The mesh whose vertex color will be filled according to source mesh texture"));
245             parlst.addParam(new RichAbsPerc("upperBound", md.mm()->cm.bbox.Diag()/50.0, 0.0f, md.mm()->cm.bbox.Diag(),
246                                             tr("Max Dist Search"), tr("Sample points for which we do not find anything within this distance are rejected and not considered for recovering color")));
247         }
248         break;
249     default: break; // do not add any parameter for the other filters
250     }
251 }
252 
253 
254 /////// FUNCTIONS NEEDED BY "UV WEDGE TO VERTEX" FILTER
ExtractVertex(const CMeshO & srcMesh,const CMeshO::FaceType & f,int whichWedge,const CMeshO & dstMesh,CMeshO::VertexType & v)255 inline void ExtractVertex(const CMeshO & srcMesh, const CMeshO::FaceType & f, int whichWedge, const CMeshO & dstMesh, CMeshO::VertexType & v)
256 {
257     (void)srcMesh;
258     (void)dstMesh;
259     // This is done to preserve every single perVertex property
260     // perVextex Texture Coordinate is instead obtained from perWedge one.
261     v.ImportData(*f.cV(whichWedge));
262     v.T() = f.cWT(whichWedge);
263 }
264 
CompareVertex(const CMeshO & m,const CMeshO::VertexType & vA,const CMeshO::VertexType & vB)265 inline bool CompareVertex(const CMeshO & m, const CMeshO::VertexType & vA, const CMeshO::VertexType & vB)
266 {
267     (void)m;
268     return (vA.cT() == vB.cT());
269 }
270 
271 /////// FUNCTIONS NEEDED BY "BASIC PARAMETRIZATION" FILTER
getLongestEdge(const CMeshO::FaceType & f)272 inline int getLongestEdge(const CMeshO::FaceType & f)
273 {
274     int res=0;
275     const CMeshO::CoordType &p0=f.cP(0), &p1=f.cP(1), p2=f.cP(2);
276     double  maxd01 = SquaredDistance(p0,p1);
277     double  maxd12 = SquaredDistance(p1,p2);
278     double  maxd20 = SquaredDistance(p2,p0);
279     if(maxd01 > maxd12)
280         if(maxd01 > maxd20)     res = 0;
281     else                    res = 2;
282     else
283         if(maxd12 > maxd20)     res = 1;
284     else                    res = 2;
285     return res;
286 }
287 
288 typedef Triangle2<CMeshO::FaceType::TexCoordType::ScalarType> Tri2;
289 
buildTrianglesCache(std::vector<Tri2> & arr,int maxLevels,float border,float quadSize,int idx=-1)290 inline void buildTrianglesCache(std::vector<Tri2> &arr, int maxLevels, float border, float quadSize, int idx=-1)
291 {
292     assert(idx >= -1);
293     Tri2 &t0 = arr[2*idx+2];
294     Tri2 &t1 = arr[2*idx+3];
295     if (idx == -1)
296     {
297         // build triangle 0
298         t0.P(1).X() = quadSize - (0.5 + M_SQRT1_2)*border;
299         t0.P(0).X() = 0.5 * border;
300         t0.P(1).Y() = 1.0 - t0.P(0).X();
301         t0.P(0).Y() = 1.0 - t0.P(1).X();
302         t0.P(2).X() = t0.P(0).X();
303         t0.P(2).Y() = t0.P(1).Y();
304         // build triangle 1
305         t1.P(1).X() = (0.5 + M_SQRT1_2)*border;
306         t1.P(0).X() = quadSize - 0.5 * border;
307         t1.P(1).Y() = 1.0 - t1.P(0).X();
308         t1.P(0).Y() = 1.0 - t1.P(1).X();
309         t1.P(2).X() = t1.P(0).X();
310         t1.P(2).Y() = t1.P(1).Y();
311     }
312     else {
313         // split triangle idx in t0 t1
314         Tri2 &t = arr[idx];
315         Tri2::CoordType midPoint = (t.P(0) + t.P(1)) / 2;
316         Tri2::CoordType vec10 = (t.P(0) - t.P(1)).Normalize() * (border/2.0);
317         t0.P(1) = t.P(0);
318         t1.P(0) = t.P(1);
319         t0.P(2) = midPoint + vec10;
320         t1.P(2) = midPoint - vec10;
321         t0.P(0) = t.P(2) + ( (t.P(0)-t.P(2)).Normalize() * border / M_SQRT2 );
322         t1.P(1) = t.P(2) + ( (t.P(1)-t.P(2)).Normalize() * border / M_SQRT2 );
323     }
324     if (--maxLevels <= 0) return;
325     buildTrianglesCache (arr, maxLevels, border, quadSize, 2*idx+2);
326     buildTrianglesCache (arr, maxLevels, border, quadSize, 2*idx+3);
327 }
328 
329 // ERROR CHECKING UTILITY
330 #define CheckError(x,y); if ((x)) {this->errorMessage = (y); return false;}
331 ///////////////////////////////////////////////////////
332 
333 template<typename T>
log_2(const T num)334 T log_2(const T num)
335 {
336     return T(log(num) / log(T(2)));
337 }
338 
339 // The Real Core Function doing the actual mesh processing.
applyFilter(QAction * filter,MeshDocument & md,RichParameterSet & par,CallBackPos * cb)340 bool FilterTexturePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet &par, CallBackPos *cb)
341 {
342     MeshModel &m=*(md.mm());
343     switch(ID(filter))     {
344     case FP_VORONOI_ATLAS : {
345       MeshModel *baseModel=md.mm();
346       baseModel->updateDataMask(MeshModel::MM_FACEFACETOPO);
347       tri::UpdateTopology<CMeshO>::FaceFace(baseModel->cm);
348       int nonManifVertNum=tri::Clean<CMeshO>::CountNonManifoldVertexFF(baseModel->cm,false);
349       int nonManifEdgeNum=tri::Clean<CMeshO>::CountNonManifoldEdgeFF(baseModel->cm,false);
350 
351       if(nonManifVertNum>0 || nonManifEdgeNum>0)
352       {
353         Log("Mesh is not manifold\n:%i non manifold Vertices\n%i nonmanifold Edges\n",nonManifVertNum,nonManifEdgeNum);
354         this->errorMessage = "Mesh is not manifold. See Log for details";
355         return false;
356       }
357 
358       MeshModel *paraModel=md.addNewMesh("","VoroAtlas",false);
359       tri::VoronoiAtlas<CMeshO>::VoronoiAtlasParam pp;
360       pp.sampleNum =par.getInt("regionNum");
361       pp.overlap=par.getBool("overlapFlag");
362 
363       paraModel->updateDataMask(MeshModel::MM_WEDGTEXCOORD);
364       // Note that CMesh has ocf texcoord and the append inside VoronoiAtlas class need that.
365       // This is a design bug of the VCGLib...
366       int bitToBeCleared =0;
367       if(!baseModel->hasDataMask(MeshModel::MM_WEDGTEXCOORD)) bitToBeCleared |=MeshModel::MM_WEDGTEXCOORD;
368       if(!baseModel->hasDataMask(MeshModel::MM_VERTTEXCOORD)) bitToBeCleared |=MeshModel::MM_VERTTEXCOORD;
369       baseModel->updateDataMask(MeshModel::MM_WEDGTEXCOORD | MeshModel::MM_VERTTEXCOORD);
370       tri::VoronoiAtlas<CMeshO>::Build(baseModel->cm,paraModel->cm, pp);
371       if(pp.overlap==false)
372         tri::Clean<CMeshO>::RemoveDuplicateVertex(paraModel->cm);
373 
374       paraModel->UpdateBoxAndNormals();
375       baseModel->clearDataMask(bitToBeCleared);
376       Log("Voronoi Atlas: Completed Processing in %i iterations",pp.vas.iterNum);
377       Log("Asked %i generated %i regions",pp.sampleNum,pp.vas.regionNum);
378       Log("Unwrap Time   %6.3f s", float(pp.vas.unwrapTime) / CLOCKS_PER_SEC);
379       Log("Voronoi Time  %6.3f s", float(pp.vas.voronoiTime) / CLOCKS_PER_SEC);
380       Log("Sampling Time %6.3f s", float(pp.vas.samplingTime) / CLOCKS_PER_SEC);
381       }
382       break;
383 
384     case FP_UV_WEDGE_TO_VERTEX : {
385             int vn = m.cm.vn;
386             m.updateDataMask(MeshModel::MM_VERTTEXCOORD);
387             tri::AttributeSeam::SplitVertex(m.cm, ExtractVertex, CompareVertex);
388             if (m.cm.vn != vn)
389             {
390                 m.clearDataMask(MeshModel::MM_FACEFACETOPO);
391                 m.clearDataMask(MeshModel::MM_VERTFACETOPO);
392             }
393         }
394         break;
395 
396     case FP_UV_VERTEX_TO_WEDGE : {
397             m.updateDataMask(MeshModel::MM_WEDGTEXCOORD);
398             tri::UpdateTexture<CMeshO>::WedgeTexFromVertexTex(m.cm);
399         }
400         break;
401 
402 	case FP_PLANAR_MAPPING :
403 	{
404 		m.updateDataMask(MeshModel::MM_WEDGTEXCOORD);
405 
406 		// Get Parameters
407 		Point3m planeVec[3][2] = {
408 			{Point3m(1,0,0),Point3m(0,1,0)},  // XY
409 			{Point3m(0,0,1),Point3m(1,0,0)},  // XZ
410 			{Point3m(0,1,0),Point3m(0,0,1)}   // YZ
411 		};
412 
413 		int sideDim = par.getEnum("projectionPlane");
414 		bool aspectRatio = par.getBool("aspectRatio");
415 		float sideGutter = par.getFloat("sideGutter");
416 
417 		// the mesh has to be correctly transformed
418 		if (m.cm.Tr != Matrix44m::Identity())
419 			tri::UpdatePosition<CMeshO>::Matrix(m.cm, m.cm.Tr, true);
420 
421 		tri::UpdateTexture<CMeshO>::WedgeTexFromPlane(m.cm, planeVec[sideDim][0], planeVec[sideDim][1], aspectRatio, sideGutter);
422 
423 		// the mesh has to return to its original position
424 		if (m.cm.Tr != Matrix44m::Identity())
425 			tri::UpdatePosition<CMeshO>::Matrix(m.cm, Inverse(m.cm.Tr), true);
426 	}
427 	break;
428 
429     case FP_BASIC_TRIANGLE_MAPPING :
430     {
431             m.updateDataMask(MeshModel::MM_WEDGTEXCOORD);
432 
433             // Get Parameters
434             int sideDim = par.getInt("sidedim");
435             int textDim = par.getInt("textdim");
436             int pxBorder = par.getInt("border");
437             bool adv = false;
438             switch(par.getEnum("method")) {
439                 case 0 : adv = false; break; // Basic
440                 case 1 : adv = true; break;  // Advanced
441                 default : assert(0);
442             };
443 
444             // Pre checks
445             CheckError(textDim <= 0, "Texture Dimension has an incorrect value");
446             CheckError(pxBorder < 0,   "Inter-Triangle border has an incorrect value");
447             CheckError(sideDim < 0,  "Quads per line border has an incorrect value");
448 
449             if (adv) //ADVANCED SPACE-OPTIMIZING
450             {
451                 float border = ((float)pxBorder) / textDim;
452 
453                 // Creates a vector of double areas
454                 double maxArea = -1, minArea=DBL_MAX;
455                 std::vector<double> areas;
456                 int faceNo = 0;
457                 for (uint i=0; i<m.cm.face.size(); ++i)
458                 {
459                     if (!m.cm.face[i].IsD())
460                     {
461                         double area = DoubleArea(m.cm.face[i]);
462                         if (area == 0) area = DBL_MIN;
463                         if (area > maxArea) maxArea = area;
464                         if (area < minArea) minArea = area;
465                         areas.push_back(area);
466                         ++faceNo;
467                     } else {
468                         areas.push_back(-1.0);
469                     }
470                 }
471 
472                 // Creates buckets containing each halfening level triangles (a histogram)
473                 int    buckSize = (int)ceil(log_2(maxArea/minArea) + DBL_EPSILON);
474                 std::vector<std::vector<uint> > buckets(buckSize);
475                 for (uint i=0; i<areas.size(); ++i)
476                     if (areas[i]>=0)
477                     {
478                     int slot = (int)ceil(log_2(maxArea/areas[i]) + DBL_EPSILON) - 1;
479                     assert(slot < buckSize && slot >= 0);
480                     buckets[slot].push_back(i);
481                 }
482 
483                 // Determines correct dimension and accordingly max halfening levels
484                 int dim = 0;
485                 int halfeningLevels = 0;
486 
487                 double qn = 0., divisor = 2.0;
488                 int rest = faceNo, oneFact = 1, sqrt2Fact = 1;
489                 bool enough = false;
490                 while (halfeningLevels < buckSize)
491                 {
492                     int tmp =(int)ceil(sqrt(qn + rest/divisor));
493                     bool newenough = true;
494                     if (sideDim != 0)
495                     {
496                         newenough = sideDim>=tmp;
497                         tmp = sideDim;
498                     }
499 
500                     // this check triangles dimension limit too
501                     if (newenough && 1.0/tmp < (sqrt2Fact/M_SQRT2 + oneFact)*border +
502                         (oneFact != sqrt2Fact ? oneFact*M_SQRT2*2.0/textDim : oneFact*2.0/textDim)) break;
503 
504                     enough = newenough;
505                     rest -= buckets[halfeningLevels].size();
506                     qn += buckets[halfeningLevels].size() / divisor;
507                     divisor *= 2.0;
508 
509                     if (halfeningLevels%2)
510                         oneFact *= 2;
511                     else
512                         sqrt2Fact *= 2;
513 
514                     dim = tmp;
515                     halfeningLevels++;
516                 }
517 
518                 // Post checks
519                 CheckError(!enough && halfeningLevels==buckSize, QString("Quads per line aren't enough to obtain a correct parametrization\nTry setting at least ") + QString::number((int)ceil(sqrt(qn))));
520                 CheckError(halfeningLevels==0  || !enough, "Inter-Triangle border is too much");
521 
522                 //Create cache of possible triangles (need only translation in correct position)
523                 std::vector<Tri2> cache((1 << (halfeningLevels+1))-2);
524                 buildTrianglesCache(cache, halfeningLevels, border, 1.0/dim);
525 
526                 // Setting texture coordinates (finally)
527                 Tri2::CoordType origin;
528                 Tri2::CoordType tmp;
529                 int buckIdx=0, face=0;
530                 std::vector<uint>::iterator it = buckets[buckIdx].begin();
531                 int currLevel = 1;
532                 for (int i=0; i<dim && face<faceNo; ++i)
533                 {
534                     origin.Y() = -((float)i)/dim;
535                     for (int j=0; j<dim && face<faceNo; j++)
536                     {
537                         origin.X() = ((float)j)/dim;
538                         for (int pos=(1<<currLevel)-2; pos<(1<<(currLevel+1))-2 && face<faceNo; ++pos, ++face)
539                         {
540                             while (it == buckets[buckIdx].end()) {
541                                 if (++buckIdx < halfeningLevels)
542                                 {
543                                     ++currLevel;
544                                     pos = 2*pos+2;
545                                 }
546                                 it = buckets[buckIdx].begin();
547                             }
548                             int fidx = *it;
549                             int lEdge = getLongestEdge(m.cm.face[fidx]);
550                             Tri2 &t = cache[pos];
551                             tmp = t.P(0) + origin;
552                             m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
553                             m.cm.face[fidx].WT(lEdge).N() = 0;
554                             lEdge = (lEdge+1)%3;
555                             tmp = t.P(1) + origin;
556                             m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
557                             m.cm.face[fidx].WT(lEdge).N() = 0;
558                             lEdge = (lEdge+1)%3;
559                             tmp = t.P(2) + origin;
560                             m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
561                             m.cm.face[fidx].WT(lEdge).N() = 0;
562                             ++it;
563                             cb(face*100/faceNo, "Generating parametrization...");
564                         }
565                     }
566                 }
567                 assert(face == faceNo);
568                 assert(it == buckets[buckSize-1].end());
569                 Log( "Biggest triangle's catheti are %.2f px long", (cache[0].P(0)-cache[0].P(2)).Norm() * textDim);
570                 Log( "Smallest triangle's catheti are %.2f px long", (cache[cache.size()-1].P(0)-cache[cache.size()-1].P(2)).Norm() * textDim);
571 
572             }
573             else //BASIC
574             {
575                 //Get total faces and total undeleted face
576                 int faceNo = m.cm.face.size();
577                 int faceNotD = 0;
578                 for (CMeshO::FaceIterator fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi)
579                     if (!fi->IsD()) ++faceNotD;
580 
581                 // Minimum side dimension to get correct halfsquared triangles
582                 int optimalDim = ceilf(sqrtf(faceNotD/2.));
583                 if (sideDim == 0) sideDim = optimalDim;
584                 else {
585                     CheckError(optimalDim > sideDim, QString("Quads per line aren't enough to obtain a correct parametrization\nTry setting at least ") + QString::number(optimalDim));
586                 }
587 
588                 //Calculating border size in UV space
589                 float border = ((float)pxBorder) / textDim;
590                 CheckError(border*(1.0+M_SQRT2)+2.0/textDim > 1.0/sideDim, "Inter-Triangle border is too much");
591 
592                 float bordersq2 = border / M_SQRT2;
593                 float halfborder = border / 2;
594 
595                 bool odd = true;
596                 CFaceO::TexCoordType botl, topr;
597                 int face=0;
598                 botl.V() = 1.;
599                 for (int i=0; i<sideDim && face<faceNo; ++i)
600                 {
601                     topr.V() = botl.V();
602                     topr.U() = 0.;
603                     botl.V() = 1.0 - 1.0/sideDim*(i+1);
604                     for (int j=0; j<2*sideDim && face<faceNo; ++face)
605                     {
606                         if (!m.cm.face[face].IsD())
607                         {
608                             int lEdge = getLongestEdge(m.cm.face[face]);
609                             if (odd) {
610                                 botl.U() = topr.U();
611                                 topr.U() = 1.0/sideDim*(j/2+1);
612                                 CFaceO::TexCoordType bl(botl.U()+halfborder, botl.V()+halfborder+bordersq2);
613                                 CFaceO::TexCoordType tr(topr.U()-(halfborder+bordersq2), topr.V()-halfborder);
614                                 bl.N() = 0;
615                                 tr.N() = 0;
616                                 m.cm.face[face].WT(lEdge) = bl;
617                                 m.cm.face[face].WT((++lEdge)%3) = tr;
618                                 m.cm.face[face].WT((++lEdge)%3) = CFaceO::TexCoordType(bl.U(), tr.V());
619                                 m.cm.face[face].WT(lEdge%3).N() = 0;
620                             } else {
621                                 CFaceO::TexCoordType bl(botl.U()+(halfborder+bordersq2), botl.V()+halfborder);
622                                 CFaceO::TexCoordType tr(topr.U()-halfborder, topr.V()-(halfborder+bordersq2));
623                                 bl.N() = 0;
624                                 tr.N() = 0;
625                                 m.cm.face[face].WT(lEdge) = tr;
626                                 m.cm.face[face].WT((++lEdge)%3) = bl;
627                                 m.cm.face[face].WT((++lEdge)%3) = CFaceO::TexCoordType(tr.U(), bl.V());
628                                 m.cm.face[face].WT(lEdge%3).N() = 0;
629                             }
630                             cb(face*100/faceNo, "Generating parametrization...");
631                             odd=!odd; ++j;
632                         }
633                     }
634                 }
635                 Log( "Triangles' catheti are %.2f px long", (1.0/sideDim-border-bordersq2)*textDim);
636             }
637         }
638         break;
639 
640     case FP_SET_TEXTURE : {
641 
642 #define CHECKERDIM 64
643 
644             // Get parameters
645             QString textName = par.getString("textName");
646             int textDim = par.getInt("textDim");
647 
648             CheckError(!QFile(m.fullName()).exists(), "Save the file before setting a texture");
649             CheckError(textDim <= 0, "Texture Dimension has an incorrect value");
650             CheckError(textName.length() == 0, "Texture file not specified");
651 
652             // Check textName and eventually add .png ext
653             CheckError(std::max<int>(textName.lastIndexOf("\\"),textName.lastIndexOf("/")) != -1, "Path in Texture file not allowed");
654 			if (!textName.endsWith(".png", Qt::CaseInsensitive) && !textName.endsWith(".dds", Qt::CaseInsensitive) && !textName.endsWith(".jpg", Qt::CaseInsensitive))
655                 textName.append(".png");
656 
657             // Creates path to texture file
658             QString fileName(m.fullName());
659             fileName = fileName.left(std::max<int>(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'))+1).append(textName);
660 
661             QFile textFile(fileName);
662             if (!textFile.exists())
663             {
664                 // Create dummy checkers texture image
665                 QImage img(textDim, textDim, QImage::Format_RGB32);
666                 img.fill(qRgb(255,255,255)); // white
667                 QPainter p(&img);
668                 QBrush gray(Qt::gray);
669                 QRect rect(0,0,CHECKERDIM,CHECKERDIM);
670                 bool odd = true;
671                 for (int y=0; y<textDim; y+=CHECKERDIM, odd=!odd)
672                 {
673                     rect.moveTop(y);
674                     for (int x=odd?0:CHECKERDIM; x<textDim; x+=2*CHECKERDIM)
675                     {
676                         rect.moveLeft(x);
677                         p.fillRect(rect, gray);
678                     }
679                 }
680 
681                 // Save texture
682                 CheckError(!img.save(fileName, NULL), "Specified file cannot be saved");
683                 Log( "Dummy Texture \"%s\" Created ", fileName.toStdString().c_str());
684                 assert(textFile.exists());
685             }
686 
687             //Assign texture
688             m.cm.textures.clear();
689             m.cm.textures.push_back(textName.toStdString());
690         }
691         break;
692 
693 	case FP_COLOR_TO_TEXTURE :
694 	{
695 		QString textName = par.getString("textName");
696 		int textW = par.getInt("textW");
697 		int textH = par.getInt("textH");
698 		bool overwrite = par.getBool("overwrite");
699 		bool assign = par.getBool("assign");
700 		bool pp = par.getBool("pullpush");
701 
702 		CheckError(!QFile(m.fullName()).exists(), "Save the file before creating a texture");
703 		CheckError(textW <= 0, "Texture Width has an incorrect value");
704 		CheckError(textH <= 0, "Texture Height has an incorrect value");
705 		if (overwrite)
706 		{
707 			CheckError(m.cm.textures.empty(), "Mesh has no associated texture to overwrite");
708 		}
709 		else
710 		{
711 			CheckError(textName.length() == 0, "Texture file not specified");
712 			CheckError(std::max<int>(textName.lastIndexOf("\\"), textName.lastIndexOf("/")) != -1, "Path in Texture file not allowed");
713 		}
714 
715 		if (m.cm.textures.empty())
716 		{
717 			// Creates path to texture file
718 			QString fileName(m.fullName());
719 			fileName = fileName.left(std::max<int>(fileName.lastIndexOf('\\'), fileName.lastIndexOf('/')) + 1).append(textName);
720 
721 			QFile textFile(fileName);
722 			if (!textFile.exists())
723 			{
724 				// Create dummy checkers texture image
725 				QImage img(textW, textH, QImage::Format_RGB32);
726 				img.fill(qRgb(255, 255, 255)); // white
727 
728 				// Save texture
729 				CheckError(!img.save(fileName, "PNG"), "Specified file cannot be saved");
730 				Log("Dummy Texture \"%s\" Created ", fileName.toStdString().c_str());
731 				assert(textFile.exists());
732 			}
733 
734 			//Assign texture
735 			m.cm.textures.clear();
736 			m.cm.textures.push_back(textName.toStdString());
737 
738 
739 		}
740 
741 		QString filePath(m.fullName());
742 		filePath = filePath.left(std::max<int>(filePath.lastIndexOf('\\'),filePath.lastIndexOf('/'))+1);
743 		QString baseName(textName);
744 		if (baseName.lastIndexOf(".") != -1)
745 			if (baseName.endsWith("bmp", Qt::CaseInsensitive) || baseName.endsWith("jpg", Qt::CaseInsensitive) || baseName.endsWith("png", Qt::CaseInsensitive)
746 				|| baseName.endsWith("jpeg", Qt::CaseInsensitive) || baseName.endsWith("tif", Qt::CaseInsensitive) || baseName.endsWith("tiff", Qt::CaseInsensitive))
747 				baseName.truncate(baseName.lastIndexOf("."));
748 
749 		int texInd;
750 		int texNum;
751 		texNum = m.cm.textures.size();
752 		vector <QString> texFileNames;
753 		texFileNames.resize(texNum);
754 		vector <QImage> trgImgs;
755 		trgImgs.reserve(m.cm.textures.size());
756 
757 		// Image texture creation
758 		for (texInd = 0; texInd < texNum; texInd++)
759 		{
760 			if (overwrite)
761 			{
762 				texFileNames[texInd] = filePath + QString(m.cm.textures[texInd].c_str());
763 			}
764 			else
765 			{
766 				if (texNum==1)
767 					texFileNames[texInd] = filePath + baseName + ".png";
768 				else
769 					texFileNames[texInd] = filePath + baseName + "_" + QString::number(texInd) + ".png";
770 			}
771 
772 			trgImgs.push_back(QImage(QSize(textW, textH), QImage::Format_ARGB32));
773 			trgImgs[texInd].fill(qRgba(0, 0, 0, 0)); // transparent black
774 		}
775 
776 		// Compute (texture-space) border edges
777 		tri::UpdateTopology<CMeshO>::FaceFaceFromTexCoord(m.cm);
778 		tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm);
779 
780 		// Rasterizing triangles
781 		RasterSampler rs(trgImgs);
782 		rs.InitCallback(cb, m.cm.fn, 0, 80);
783 		tri::SurfaceSampling<CMeshO,RasterSampler>::Texture(m.cm,rs,textW,textH,true);
784 
785 		// Undo topology changes
786 		tri::UpdateTopology<CMeshO>::FaceFace(m.cm);
787 		tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm);
788 
789 		for (texInd = 0; texInd < texNum; texInd++)
790 		{
791 			// Revert alpha values for border edge pixels to 255
792 			cb(81, "Cleaning up texture ...");
793 			for (int y = 0; y<textH; ++y)
794 			for (int x = 0; x<textW; ++x)
795 			{
796 				QRgb px = trgImgs[texInd].pixel(x, y);
797 				if (qAlpha(px) < 255 && (!pp || qAlpha(px) > 0))
798 				trgImgs[texInd].setPixel(x, y, px | 0xff000000);
799 			}
800 
801 			// PullPush
802 			if (pp)
803 			{
804 				cb(85, "Filling texture holes...");
805 				PullPush(trgImgs[texInd], qRgba(0, 0, 0, 0));
806 			}
807 
808 			// Save texture
809 			cb(90, "Saving texture ...");
810 			CheckError(!trgImgs[texInd].save(texFileNames[texInd]), "Texture file cannot be saved");
811 			Log("Texture \"%s\" Created", texFileNames[texInd].toStdString().c_str());
812 			assert(QFile(texFileNames[texInd]).exists());
813 		}
814 
815 		if (assign && !overwrite)
816 		{
817 			m.cm.textures.clear();
818 			for (texInd = 0; texInd < texNum; texInd++)
819 			m.cm.textures.push_back(textName.toStdString());
820 		}
821 
822 		cb(100, "Done");
823 	}
824 	break;
825 
826 	case FP_TRANSFER_TO_TEXTURE :
827 	{
828 		MeshModel *srcMesh = par.getMesh("sourceMesh");
829 		MeshModel *trgMesh = par.getMesh("targetMesh");
830 		bool vertexSampling=false;
831 		bool textureSampling=false;
832 		int  vertexMode= -1;
833 		switch (par.getEnum("AttributeEnum"))
834 		{
835 			case 0: vertexSampling= true; vertexMode=0; break;  // Color
836 			case 1: vertexSampling= true; vertexMode=1; break;  // Normal
837 			case 2: vertexSampling= true; vertexMode=2; break;  // Quality
838 			case 3: textureSampling = true; break;
839 			default: assert(0);
840 		}
841 		float upperbound = par.getAbsPerc("upperBound"); // maximum distance to stop search
842 		QString textName = par.getString("textName");
843 		int textW = par.getInt("textW");
844 		int textH = par.getInt("textH");
845 		bool overwrite = par.getBool("overwrite");
846 		bool assign = par.getBool("assign");
847 		bool pp = par.getBool("pullpush");
848 
849 		assert (srcMesh != NULL);
850 		assert (trgMesh != NULL);
851 		CheckError(!QFile(trgMesh->fullName()).exists(), "Save the target mesh before creating a texture");
852         CheckError(trgMesh->cm.fn == 0, "Target mesh needs to have faces");
853         CheckError(!trgMesh->hasDataMask(MeshModel::MM_WEDGTEXCOORD), "Target mesh does not have Per-Wedge Texture Coordinates");
854 		CheckError(textW <= 0, "Texture Width has an incorrect value");
855 		CheckError(textH <= 0, "Texture Height has an incorrect value");
856 
857 		if (vertexSampling) {
858             if (vertexMode == 0) { CheckError(!srcMesh->hasDataMask(MeshModel::MM_VERTCOLOR), "Source mesh doesn't have Per-Vertex Color"); }
859             if (vertexMode == 1) { CheckError(!srcMesh->hasDataMask(MeshModel::MM_VERTNORMAL), "Source mesh doesn't have Per-Vertex Normal"); }
860             if (vertexMode == 2) { CheckError(!srcMesh->hasDataMask(MeshModel::MM_VERTQUALITY), "Source mesh doesn't have Per-Vertex Quality"); }
861 		}
862 		else
863 		{
864             CheckError(srcMesh->cm.fn == 0, "Source mesh needs to have faces");
865             CheckError(!srcMesh->hasDataMask(MeshModel::MM_WEDGTEXCOORD), "Source mesh does not have Per-Wedge Texture Coordinates");
866             CheckError(srcMesh->cm.textures.empty(), "Source mesh does not have any associated texture");
867 		}
868 
869 		if (overwrite)
870 		{
871 			CheckError(trgMesh->cm.textures.empty(), "Mesh has no associated texture to overwrite");
872 		}
873 		else
874 		{
875 			CheckError(textName.length() == 0, "Texture file not specified");
876 			CheckError(std::max<int>(textName.lastIndexOf("\\"), textName.lastIndexOf("/")) != -1, "Path in Texture file not allowed");
877 		}
878 
879 		if (m.cm.textures.empty())
880 		{
881 			// Creates path to texture file
882 			QString fileName(m.fullName());
883 			fileName = fileName.left(std::max<int>(fileName.lastIndexOf('\\'), fileName.lastIndexOf('/')) + 1).append(textName);
884 
885 			QFile textFile(fileName);
886 			if (!textFile.exists())
887 			{
888 				// Create dummy checkers texture image
889 				QImage img(textW, textH, QImage::Format_RGB32);
890 				img.fill(qRgb(255, 255, 255)); // white
891 
892 											   // Save texture
893 				CheckError(!img.save(fileName, "PNG"), "Specified file cannot be saved");
894 				Log("Dummy Texture \"%s\" Created ", fileName.toStdString().c_str());
895 				assert(textFile.exists());
896 			}
897 
898 			//Assign texture
899 			m.cm.textures.clear();
900 			m.cm.textures.push_back(textName.toStdString());
901 
902 
903 		}
904 
905 		// Source images (for texture to texture transfer)
906 		int numSrcTex = srcMesh->cm.textures.size();
907 		QString srcPath(srcMesh->fullName());
908 		srcPath = srcPath.left(std::max<int>(srcPath.lastIndexOf('\\'), srcPath.lastIndexOf('/')) + 1);
909 		vector <QImage> srcImgs;
910 		vector <QString> srcTextureFileNames;
911 		srcImgs.resize(numSrcTex);
912 		srcTextureFileNames.resize(numSrcTex);
913 		int srcTexInd = 0;
914 		// Target images
915 		int numTrgTex = trgMesh->cm.textures.size();
916 		QString trgPath(trgMesh->fullName());
917 		trgPath = trgPath.left(std::max<int>(trgPath.lastIndexOf('\\'), trgPath.lastIndexOf('/')) + 1);
918 		QString baseName(textName);
919 		if (baseName.lastIndexOf(".") != -1)
920 		if (baseName.endsWith("bmp", Qt::CaseInsensitive) || baseName.endsWith("jpg", Qt::CaseInsensitive) || baseName.endsWith("png", Qt::CaseInsensitive)
921 			|| baseName.endsWith("jpeg", Qt::CaseInsensitive) || baseName.endsWith("tif", Qt::CaseInsensitive) || baseName.endsWith("tiff", Qt::CaseInsensitive))
922 			baseName.truncate(baseName.lastIndexOf("."));
923 		vector <QImage> trgImgs;
924 		vector <QString> trgTextureFileNames;
925 		trgImgs.reserve(numTrgTex);
926 		trgTextureFileNames.resize(numTrgTex);
927 		int trgTexInd = 0;
928 
929 		// Check whether is possible to access source mesh textures, and load them
930 		if (textureSampling)
931 		{
932 			for (srcTexInd = 0; srcTexInd < numSrcTex; srcTexInd++)
933 			{
934 				srcTextureFileNames[srcTexInd] = srcPath + srcMesh->cm.textures[srcTexInd].c_str();
935 				CheckError(!QFile(srcTextureFileNames[srcTexInd]).exists(), QString("Source texture \"").append(srcTextureFileNames[srcTexInd]).append("\" doesn't exists"));
936 				CheckError(!srcImgs[srcTexInd].load(srcTextureFileNames[srcTexInd]), QString("Source texture \"").append(srcTextureFileNames[srcTexInd]).append("\" cannot be opened"));
937 			}
938 		}
939 
940 		// target textures naming and creation
941 		for (trgTexInd = 0; trgTexInd < numTrgTex; trgTexInd++)
942 		{
943 			if (overwrite)
944 			{
945 				trgTextureFileNames[trgTexInd] = trgPath + QString(m.cm.textures[trgTexInd].c_str());
946 			}
947 			else
948 			{
949 				if (numTrgTex == 1)
950 					trgTextureFileNames[trgTexInd] = trgPath + baseName + ".png";
951 				else
952 					trgTextureFileNames[trgTexInd] = trgPath + baseName + "_" + QString::number(trgTexInd) + ".png";
953 			}
954 
955 			trgImgs.push_back(QImage(QSize(textW, textH), QImage::Format_ARGB32));
956 			trgImgs[trgTexInd].fill(qRgba(0, 0, 0, 0)); // transparent black
957 		}
958 
959 		// Compute (texture-space) border edges
960 		trgMesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
961 		tri::UpdateTopology<CMeshO>::FaceFaceFromTexCoord(trgMesh->cm);
962 		tri::UpdateFlags<CMeshO>::FaceBorderFromFF(trgMesh->cm);
963 
964 		// the meshes have to be transformed
965 		// only if source different from target (if single mesh, it does not matter)
966 		if (srcMesh != trgMesh)
967 		{
968 			if (srcMesh->cm.Tr != Matrix44m::Identity())
969 				tri::UpdatePosition<CMeshO>::Matrix(srcMesh->cm, srcMesh->cm.Tr, true);
970 			if (trgMesh->cm.Tr != Matrix44m::Identity())
971 				tri::UpdatePosition<CMeshO>::Matrix(trgMesh->cm, trgMesh->cm.Tr, true);
972 		}
973 
974 		// Rasterizing faces
975 		srcMesh->updateDataMask(MeshModel::MM_FACEMARK);
976 		tri::UpdateNormal<CMeshO>::PerFaceNormalized(srcMesh->cm);
977 		if (vertexSampling)
978 		{
979 			TransferColorSampler sampler(srcMesh->cm, trgImgs, upperbound, vertexMode); // color sampling
980 			sampler.InitCallback(cb, trgMesh->cm.fn, 0, 80);
981 			tri::SurfaceSampling<CMeshO, TransferColorSampler>::Texture(trgMesh->cm, sampler, textW, textH, false);
982 		}
983 		else
984 		{
985 			TransferColorSampler sampler(srcMesh->cm, trgImgs, &srcImgs, upperbound); // texture sampling
986 			sampler.InitCallback(cb, trgMesh->cm.fn, 0, 80);
987 			tri::SurfaceSampling<CMeshO, TransferColorSampler>::Texture(trgMesh->cm, sampler, textW, textH, false);
988 		}
989 
990 		// the meshes have to return to their original position
991 		// only if source different from target (if single mesh, it does not matter)
992 		if (srcMesh != trgMesh)
993 		{
994 			if (srcMesh->cm.Tr != Matrix44m::Identity())
995 				tri::UpdatePosition<CMeshO>::Matrix(srcMesh->cm, Inverse(srcMesh->cm.Tr), true);
996 			if (trgMesh->cm.Tr != Matrix44m::Identity())
997 				tri::UpdatePosition<CMeshO>::Matrix(trgMesh->cm, Inverse(trgMesh->cm.Tr), true);
998 		}
999 
1000 		// Undo topology changes
1001 		tri::UpdateTopology<CMeshO>::FaceFace(trgMesh->cm);
1002 		tri::UpdateFlags<CMeshO>::FaceBorderFromFF(trgMesh->cm);
1003 
1004 		for (trgTexInd = 0; trgTexInd < numTrgTex; trgTexInd++)
1005 		{
1006 			// Revert alpha values for border edge pixels to 255
1007 			cb(81, "Cleaning up texture ...");
1008 			for (int y = 0; y<textH; ++y)
1009 			for (int x = 0; x<textW; ++x)
1010 			{
1011 				QRgb px = trgImgs[trgTexInd].pixel(x, y);
1012 				if (qAlpha(px) < 255 && (!pp || qAlpha(px) > 0))
1013 					trgImgs[trgTexInd].setPixel(x, y, px | 0xff000000);
1014 			}
1015 
1016 			// PullPush
1017 			if (pp)
1018 			{
1019 				cb(85, "Filling texture holes...");
1020 				PullPush(trgImgs[trgTexInd], qRgba(0, 0, 0, 0));
1021 			}
1022 
1023 			// Save texture
1024 			cb(90, "Saving texture ...");
1025 			CheckError(!trgImgs[trgTexInd].save(trgTextureFileNames[trgTexInd]), "Texture file cannot be saved");
1026 			Log("Texture \"%s\" Created", trgTextureFileNames[trgTexInd].toStdString().c_str());
1027 			assert(QFile(trgTextureFileNames[trgTexInd]).exists());
1028 		}
1029 
1030 		if (assign && !overwrite)
1031 		{
1032 			m.cm.textures.clear();
1033 			for (trgTexInd = 0; trgTexInd < numTrgTex; trgTexInd++)
1034 				m.cm.textures.push_back(trgTextureFileNames[trgTexInd].toStdString());
1035 		}
1036 
1037 		cb(100, "Done");
1038 	}
1039 	break;
1040 
1041 	case FP_TEX_TO_VCOLOR_TRANSFER :
1042 	{
1043 		MeshModel *srcMesh = par.getMesh("sourceMesh");
1044 		MeshModel *trgMesh = par.getMesh("targetMesh");
1045 		float upperbound = par.getAbsPerc("upperBound"); // maximum distance to stop search
1046 
1047 		assert(srcMesh!=NULL);
1048 		assert(trgMesh!=NULL);
1049 
1050 		CheckError(srcMesh->cm.fn == 0, "Source mesh requires to have faces");
1051 
1052 		// Check whether is possible to access source mesh texture
1053 		CheckError(!srcMesh->hasDataMask(MeshModel::MM_WEDGTEXCOORD), "Source mesh doesn't have Per Wedge Texture Coordinates");
1054 		CheckError(srcMesh->cm.textures.empty(), "Source mesh doesn't have any associated texture");
1055 
1056 		vector <QImage> srcImgs;
1057 		srcImgs.resize(srcMesh->cm.textures.size());
1058 		QString path;
1059 
1060 		for (size_t textInd = 0; textInd < srcMesh->cm.textures.size(); textInd++)
1061 		{
1062 			path = m.fullName();
1063 			path = path.left(std::max<int>(path.lastIndexOf('\\'), path.lastIndexOf('/')) + 1).append(srcMesh->cm.textures[textInd].c_str());
1064 			CheckError(!QFile(path).exists(), QString("Source texture \"").append(path).append("\" doesn't exists"));
1065 			CheckError(!srcImgs[textInd].load(path), QString("Source texture \"").append(path).append("\" cannot be opened"));
1066 		}
1067 
1068 		trgMesh->updateDataMask(MeshModel::MM_VERTCOLOR);
1069 		srcMesh->updateDataMask(MeshModel::MM_FACEMARK);
1070 
1071 		// the meshes have to be transformed
1072 		// only if source different from target (if single mesh, it does not matter)
1073 		if (srcMesh != trgMesh)
1074 		{
1075 			if (srcMesh->cm.Tr != Matrix44m::Identity())
1076 				tri::UpdatePosition<CMeshO>::Matrix(srcMesh->cm, srcMesh->cm.Tr, true);
1077 			if (trgMesh->cm.Tr != Matrix44m::Identity())
1078 				tri::UpdatePosition<CMeshO>::Matrix(trgMesh->cm, trgMesh->cm.Tr, true);
1079 		}
1080 
1081 		tri::UpdateNormal<CMeshO>::PerFaceNormalized(srcMesh->cm);
1082 		// Colorizing vertices
1083 		VertexSampler vs(srcMesh->cm, srcImgs, upperbound);
1084 		vs.InitCallback(cb, trgMesh->cm.vn);
1085 		tri::SurfaceSampling<CMeshO,VertexSampler>::VertexUniform(trgMesh->cm,vs,trgMesh->cm.vn);
1086 
1087 		// the meshes have to return to their original position
1088 		// only if source different from target (if single mesh, it does not matter)
1089 		if (srcMesh != trgMesh)
1090 		{
1091 			if (srcMesh->cm.Tr != Matrix44m::Identity())
1092 				tri::UpdatePosition<CMeshO>::Matrix(srcMesh->cm, Inverse(srcMesh->cm.Tr), true);
1093 			if (trgMesh->cm.Tr != Matrix44m::Identity())
1094 				tri::UpdatePosition<CMeshO>::Matrix(trgMesh->cm, Inverse(trgMesh->cm.Tr), true);
1095 		}
1096 
1097 		srcMesh->clearDataMask(MeshModel::MM_FACEMARK);
1098 	}
1099 	break;
1100 
1101     default: assert(0);
1102     }
1103 
1104     return true;
1105 }
1106 
filterArity(QAction * filter) const1107 MeshFilterInterface::FILTER_ARITY FilterTexturePlugin::filterArity( QAction * filter ) const
1108 {
1109     switch(ID(filter))
1110     {
1111     case FP_VORONOI_ATLAS :
1112     case FP_UV_WEDGE_TO_VERTEX :
1113     case FP_UV_VERTEX_TO_WEDGE :
1114     case FP_BASIC_TRIANGLE_MAPPING :
1115     case FP_PLANAR_MAPPING :
1116     case FP_SET_TEXTURE :
1117     case FP_COLOR_TO_TEXTURE :
1118         return MeshFilterInterface::SINGLE_MESH;
1119     case FP_TRANSFER_TO_TEXTURE :
1120     case FP_TEX_TO_VCOLOR_TRANSFER :
1121         return MeshFilterInterface::FIXED;
1122     }
1123     return MeshFilterInterface::NONE;
1124 }
1125 
1126 
1127 MESHLAB_PLUGIN_NAME_EXPORTER(FilterTexturePlugin)
1128