diff --git a/src/meshlabplugins/filter_texture/filter_texture.cpp b/src/meshlabplugins/filter_texture/filter_texture.cpp
index 2387353a5..fabe394c3 100644
--- a/src/meshlabplugins/filter_texture/filter_texture.cpp
+++ b/src/meshlabplugins/filter_texture/filter_texture.cpp
@@ -35,6 +35,7 @@
#include "filter_texture.h"
#include "pushpull.h"
+#include "rastering.h"
FilterTexturePlugin::FilterTexturePlugin()
@@ -43,7 +44,8 @@ FilterTexturePlugin::FilterTexturePlugin()
<< FP_UV_WEDGE_TO_VERTEX
<< FP_BASIC_TRIANGLE_MAPPING
<< FP_SET_TEXTURE
- << FP_COLOR_TO_TEXTURE;
+ << FP_COLOR_TO_TEXTURE
+ << FP_MESH_TEXCOLOR_TRANSFER;
foreach(FilterIDType tt , types())
actionList << new QAction(filterName(tt), this);
@@ -56,7 +58,8 @@ QString FilterTexturePlugin::filterName(FilterIDType filterId) const
case FP_UV_WEDGE_TO_VERTEX : return QString("Convert PerWedge UV into PerVertex UV");
case FP_BASIC_TRIANGLE_MAPPING : return QString("Basic Triangle Mapping");
case FP_SET_TEXTURE : return QString("Set Texture");
- case FP_COLOR_TO_TEXTURE : return QString("Vertex Color to Texture transfer");
+ case FP_COLOR_TO_TEXTURE : return QString("Vertex Color to Texture");
+ case FP_MESH_TEXCOLOR_TRANSFER : return QString("VertexColor/Texture to Texture transfer");
default : assert(0);
}
}
@@ -73,6 +76,7 @@ QString FilterTexturePlugin::filterInfo(FilterIDType filterId) const
case FP_SET_TEXTURE : return QString("Set a texture associated with current mesh parametrization.
"
"If the texture provided exists it will be simply set else a dummy one will be created in the same directory");
case FP_COLOR_TO_TEXTURE : return QString("Fills the specified texture accordingly to per vertex color");
+ case FP_MESH_TEXCOLOR_TRANSFER : return QString("Transfer vertex color or texture color from one mesh to another's texture.");
default : assert(0);
}
return QString("Unknown Filter");
@@ -87,6 +91,7 @@ int FilterTexturePlugin::getPreConditions(QAction *a) const
case FP_BASIC_TRIANGLE_MAPPING : return MeshFilterInterface::FP_Face;
case FP_SET_TEXTURE : return MeshFilterInterface::FP_WedgeTexCoord;
case FP_COLOR_TO_TEXTURE : return MeshFilterInterface::FP_WedgeTexCoord + MeshFilterInterface::FP_VertexColor;
+ case FP_MESH_TEXCOLOR_TRANSFER : return MeshFilterInterface::FP_WedgeTexCoord;
default: assert(0);
}
return MeshFilterInterface::FP_Generic;
@@ -101,6 +106,7 @@ int FilterTexturePlugin::getRequirements(QAction *a)
case FP_BASIC_TRIANGLE_MAPPING :
case FP_SET_TEXTURE : return MeshModel::MM_NONE;
case FP_COLOR_TO_TEXTURE : return MeshModel::MM_FACEFACETOPO;
+ case FP_MESH_TEXCOLOR_TRANSFER : return MeshModel::MM_NONE;
default: assert(0);
}
return MeshModel::MM_NONE;
@@ -115,6 +121,7 @@ int FilterTexturePlugin::postCondition( QAction *a) const
case FP_BASIC_TRIANGLE_MAPPING : return MeshModel::MM_WEDGTEXCOORD;
case FP_SET_TEXTURE : return MeshModel::MM_UNKNOWN;
case FP_COLOR_TO_TEXTURE : return MeshModel::MM_UNKNOWN;
+ case FP_MESH_TEXCOLOR_TRANSFER : return MeshModel::MM_UNKNOWN;
default: assert(0);
}
return MeshModel::MM_NONE;
@@ -132,12 +139,28 @@ FilterTexturePlugin::FilterClass FilterTexturePlugin::getClass(QAction *a)
case FP_BASIC_TRIANGLE_MAPPING :
case FP_SET_TEXTURE :
case FP_COLOR_TO_TEXTURE :
+ case FP_MESH_TEXCOLOR_TRANSFER :
return MeshFilterInterface::Texture;
default : assert(0);
}
return MeshFilterInterface::Generic;
}
+static QString extractFilenameWOExt(MeshModel* mm)
+{
+ QString fileName(mm->fileName.c_str());
+ int lastPoint = fileName.lastIndexOf(".");
+ if (lastPoint <= 0)
+ fileName.clear();
+ else {
+ fileName = fileName.left(lastPoint);
+ lastPoint = std::max(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'));
+ if (lastPoint > 0)
+ fileName = fileName.right(fileName.size() - 1 - lastPoint);
+ }
+ return fileName;
+}
+
// This function define the needed parameters for each filter. Return true if the filter has some parameters
// it is called every time, so you can set the default value of parameters according to the mesh
// For each parmeter you need to define,
@@ -145,7 +168,7 @@ FilterTexturePlugin::FilterClass FilterTexturePlugin::getClass(QAction *a)
// - the string shown in the dialog
// - the default value
// - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
-void FilterTexturePlugin::initParameterSet(QAction *action, MeshModel &m, RichParameterSet & parlst)
+void FilterTexturePlugin::initParameterSet(QAction *action, MeshDocument &md, RichParameterSet & parlst)
{
switch(ID(action)) {
case FP_UV_TO_COLOR :
@@ -156,36 +179,18 @@ void FilterTexturePlugin::initParameterSet(QAction *action, MeshModel &m, RichPa
case FP_BASIC_TRIANGLE_MAPPING :
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"));
parlst.addParam(new RichInt("textdim", 1024, "Texture Dimension (px)", "Gives an indication on how big the texture is"));
- parlst.addParam(new RichInt("border", 1, "Inter-Triangle border (px)", "Specifies how many pixels to be left between triangles in parametrization domain"));
+ parlst.addParam(new RichInt("border", 2, "Inter-Triangle border (px)", "Specifies how many pixels to be left between triangles in parametrization domain"));
parlst.addParam(new RichEnum("method", 0, QStringList("Basic") << "Space-optimizing", "Method", "Choose space optimizing to map smaller faces into smaller triangles in parametrizazion domain"));
break;
case FP_SET_TEXTURE : {
- QString fileName(m.fileName.c_str());
- int lastPoint = fileName.lastIndexOf(".");
- if (lastPoint <= 0)
- fileName = QString("");
- else {
- fileName = fileName.left(lastPoint);
- lastPoint = std::max(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'));
- if (lastPoint > 0)
- fileName = fileName.right(fileName.size() - 1 - lastPoint);
- }
+ QString fileName = extractFilenameWOExt(md.mm());
fileName = fileName.append(".png");
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"));
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"));
}
break;
case FP_COLOR_TO_TEXTURE : {
- QString fileName(m.fileName.c_str());
- int lastPoint = fileName.lastIndexOf(".");
- if (lastPoint <= 0)
- fileName = QString("");
- else {
- fileName = fileName.left(lastPoint);
- lastPoint = std::max(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'));
- if (lastPoint > 0)
- fileName = fileName.right(fileName.size() - 1 - lastPoint);
- }
+ QString fileName = extractFilenameWOExt(md.mm());
fileName = fileName.append("_color.png");
parlst.addParam(new RichString("textName", fileName, "Texture file", "The texture file to be created"));
parlst.addParam(new RichInt("textW", 1024, "Texture width (px)", "The texture width"));
@@ -194,6 +199,24 @@ void FilterTexturePlugin::initParameterSet(QAction *action, MeshModel &m, RichPa
parlst.addParam(new RichBool("assign", false, "Assign texture", "assign the newly created texture"));
}
break;
+ case FP_MESH_TEXCOLOR_TRANSFER : {
+ QString fileName = extractFilenameWOExt(md.mm());
+ fileName = fileName.append("_color.png");
+ parlst.addParam(new RichMesh ("sourceMesh",md.mm(),&md, "Source Mesh",
+ "The mesh that contains the source data that we want to transfer"));
+ parlst.addParam(new RichEnum("data", 0, QStringList("Vertex Color") << "Texture Color", "Color Data Source",
+ "Choose to transfer color information from source mesh texture or vertex color"));
+ parlst.addParam(new RichMesh ("targetMesh",md.mm(),&md, "Target Mesh",
+ "The mesh whose texture will be filled according to source mesh texture or vertex color"));
+ parlst.addParam(new RichAbsPerc("upperBound", md.mm()->cm.bbox.Diag()/50.0, 0.0f, md.mm()->cm.bbox.Diag(),
+ tr("Max Dist Search"), tr("Sample points for which we do not find anything whithin this distance are rejected and not considered for recovering color")));
+ parlst.addParam(new RichString("textName", fileName, "Texture file", "The texture file to be created"));
+ parlst.addParam(new RichInt("textW", 1024, "Texture width (px)", "The texture width"));
+ parlst.addParam(new RichInt("textH", 1024, "Texture height (px)", "The texture height"));
+ parlst.addParam(new RichBool("overwrite", false, "Overwrite Target Mesh Texture", "if target mesh has a texture will be overwritten (with provided texture dimension)"));
+ parlst.addParam(new RichBool("assign", false, "Assign Texture", "assign the newly created texture to target mesh"));
+ }
+ break;
default : assert(0);
}
}
@@ -272,41 +295,9 @@ inline void buildTrianglesCache(std::vector &arr, int maxLevels, float bor
if (--maxLevels <= 0) return;
buildTrianglesCache (arr, maxLevels, border, quadSize, 2*idx+2);
buildTrianglesCache (arr, maxLevels, border, quadSize, 2*idx+3);
-
}
/////// "COLOR TO TEXTURE" FILTER NEEDED STUFF
-class RasterSampler
-{
-public:
- RasterSampler(QImage &_img, int _th) : img(_img), texH(_th) {assert(texH>0);};
-
- QImage &img;
- int texH;
-
- void AddTextureSample(const CMeshO::FaceType &f, const CMeshO::CoordType &p, const vcg::Point2i &tp)
- {
- CMeshO::VertexType::ColorType c;
- c.lerp(f.V(0)->cC(), f.V(1)->cC(), f.V(2)->cC(), p);
- img.setPixel(tp.X(), texH - tp.Y(), qRgba(c[0], c[1], c[2], 255));
- }
-};
-
-template
-static void TextureCorrected(MetroMesh & m, VertexSampler &ps, int textureWidth, int textureHeight)
-{
- typedef typename MetroMesh::FaceIterator FaceIterator;
- FaceIterator fi;
-
- printf("Similar Triangles face sampling\n");
- for(fi=m.face.begin(); fi != m.face.end(); fi++)
- {
- vcg::Point2f ti[3];
- for(int i=0;i<3;++i)
- ti[i]=vcg::Point2f((*fi).WT(i).U() * textureWidth - 0.5, (*fi).WT(i).V() * textureHeight + 0.5);
- vcg::tri::SurfaceSampling::SingleFaceRaster(*fi, ps, ti[0],ti[1],ti[2]);
- }
-}
// ERROR CHECKING UTILITY
#define CheckError(x,y); if ((x)) {this->errorMessage = (y); return false;}
@@ -494,15 +485,17 @@ bool FilterTexturePlugin::applyFilter(QAction *filter, MeshModel &m, RichParamet
int fidx = *it;
int lEdge = getLongestEdge(m.cm.face[fidx]);
Tri2 &t = cache[pos];
- tmp = t.P0(lEdge) + origin;
- m.cm.face[fidx].WT(0) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
- m.cm.face[fidx].WT(0).N() = 0;
- tmp = t.P1(lEdge) + origin;
- m.cm.face[fidx].WT(1) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
- m.cm.face[fidx].WT(1).N() = 0;
- tmp = t.P2(lEdge) + origin;
- m.cm.face[fidx].WT(2) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
- m.cm.face[fidx].WT(2).N() = 0;
+ tmp = t.P(0) + origin;
+ m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
+ m.cm.face[fidx].WT(lEdge).N() = 0;
+ lEdge = (lEdge+1)%3;
+ tmp = t.P(1) + origin;
+ m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
+ m.cm.face[fidx].WT(lEdge).N() = 0;
+ lEdge = (lEdge+1)%3;
+ tmp = t.P(2) + origin;
+ m.cm.face[fidx].WT(lEdge) = CFaceO::TexCoordType(tmp.X(), tmp.Y());
+ m.cm.face[fidx].WT(lEdge).N() = 0;
++it;
cb(face*100/faceNo, "Generating parametrization...");
}
@@ -600,18 +593,14 @@ bool FilterTexturePlugin::applyFilter(QAction *filter, MeshModel &m, RichParamet
// Creates path to texture file
QString fileName(m.fileName.c_str());
- int lastPoint = std::max(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'));
- if (lastPoint < 0)
- fileName = textName;
- else
- fileName = fileName.left(lastPoint).append(QDir::separator()).append(textName);
+ fileName = fileName.left(std::max(fileName.lastIndexOf('\\'),fileName.lastIndexOf('/'))+1).append(textName);
QFile textFile(fileName);
if (!textFile.exists())
{
- //create dummy checkers texture image
+ // Create dummy checkers texture image
QImage img(textDim, textDim, QImage::Format_RGB32);
- img.fill(Qt::white);
+ img.fill(qRgb(255,255,255)); // white
QPainter p(&img);
QBrush gray(Qt::gray);
QRect rect(0,0,CHECKERDIM,CHECKERDIM);
@@ -625,12 +614,14 @@ bool FilterTexturePlugin::applyFilter(QAction *filter, MeshModel &m, RichParamet
p.fillRect(rect, gray);
}
}
- //save
+
+ // Save texture
CheckError(!img.save(fileName, "PNG"), "Specified file cannot be saved");
Log(GLLogStream::FILTER, "Dummy Texture \"%s\" Created ", fileName.toStdString().c_str());
assert(textFile.exists());
}
- //set
+
+ //Assign texture
m.cm.textures.clear();
m.cm.textures.push_back(textName.toStdString());
}
@@ -644,7 +635,7 @@ bool FilterTexturePlugin::applyFilter(QAction *filter, MeshModel &m, RichParamet
bool overwrite = par.getBool("overwrite");
bool assign = par.getBool("assign");
- CheckError(!QFile(m.fileName.c_str()).exists(), "Save the file before creting a texture");
+ CheckError(!QFile(m.fileName.c_str()).exists(), "Save the file before creating a texture");
QString filePath(m.fileName.c_str());
filePath = filePath.left(std::max(filePath.lastIndexOf('\\'),filePath.lastIndexOf('/'))+1);
@@ -667,66 +658,159 @@ bool FilterTexturePlugin::applyFilter(QAction *filter, MeshModel &m, RichParamet
// Image creation
QImage img(QSize(textW,textH), QImage::Format_ARGB32);
- img.fill(Qt::transparent);
+ img.fill(qRgba(0,0,0,0)); // transparent
// Compute (texture-space) border edges
vcg::tri::UpdateTopology::FaceFaceFromTexCoord(m.cm);
vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm);
-
- // Rasterizing safety buffer area along border edges
- QPainter p(&img);
- QGradientStops gs;
- QLinearGradient grad;
- QPen pen(Qt::SolidLine);
- grad.setCoordinateMode(QGradient::LogicalMode);
- pen.setCapStyle(Qt::RoundCap);
- pen.setWidthF(2.0);
- gs << QGradientStop(0.0,Qt::transparent) << QGradientStop(1.0,Qt::transparent);
-
- CMeshO::FaceIterator fi;
- for (fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi)
- for(int i=0;i<3;++i)
- if (fi->IsB(i))
- {
- QPointF t[2];
- CMeshO::VertexType::ColorType c[2];
- t[0] = QPointF(fi->cWT(i).U() * textW - 0.5, (1.0 - fi->cWT(i).V()) * textH - 0.5);
- t[1] = QPointF(fi->cWT((i+1)%3).U() * textW - 0.5, (1.0 - fi->cWT((i+1)%3).V()) * textH - 0.5);
- c[0] = fi->V(i)->cC();
- c[1] = fi->V((i+1)%3)->cC();
- grad.setStart(t[0].x()+0.5, t[0].y()+0.5);
- grad.setFinalStop(t[1].x()+0.5, t[1].y()+0.5);
- gs[0].second = QColor(c[0][0],c[0][1], c[0][2]);
- gs[1].second = QColor(c[1][0],c[1][1], c[1][2]);
- grad.setStops(gs);
- pen.setBrush(QBrush(grad));
- p.setPen(pen);
- p.drawLine(t[0], t[1]);
- }
-
+
// Rasterizing triangles
- RasterSampler rs(img, textH);
+ RasterSampler rs(img);
+ rs.InitCallback(cb, m.cm.fn, 0, 80);
TextureCorrected(m.cm,rs,textW,textH);
+ // Revert alpha values from border edge pixel to 255
+ cb(81, "Cleaning up texture ...");
+ for (int y=0; y 0)
+ img.setPixel(x,y, px | 0xff000000);
+ }
+
// PullPush
- vcg::PullPush(img, Qt::transparent);
+ cb(85, "Filling texture holes...");
+ vcg::PullPush(img, qRgba(0,0,0,0));
// Undo topology changes
vcg::tri::UpdateTopology::FaceFace(m.cm);
vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm);
- // Save
+ // Save texture
+ cb(90, "Saving texture ...");
CheckError(!img.save(filePath), "Texture file cannot be saved");
Log(GLLogStream::FILTER, "Texture \"%s\" Created", filePath.toStdString().c_str());
+ assert(QFile(filePath).exists());
- // Assign
+ // Assign texture
if (assign && !overwrite) {
m.cm.textures.clear();
m.cm.textures.push_back(textName.toStdString());
}
+ cb(100, "Done");
}
break;
+ case FP_MESH_TEXCOLOR_TRANSFER : {
+ MeshModel *srcMesh = par.getMesh("sourceMesh");
+ bool colorSampling;
+ switch (par.getEnum("data")) {
+ case 0: colorSampling = true; break;
+ case 1: colorSampling = false; break;
+ default: assert(0);
+ }
+ MeshModel *trgMesh = par.getMesh("targetMesh");
+ float upperbound = par.getAbsPerc("upperBound"); // maximum distance to stop search
+ QString textName = par.getString("textName");
+ int textW = par.getInt("textW");
+ int textH = par.getInt("textH");
+ bool overwrite = par.getBool("overwrite");
+ bool assign = par.getBool("assign");
+
+ CheckError(!QFile(trgMesh->fileName.c_str()).exists(), "Save the target mesh before creating a texture");
+ CheckError(trgMesh->cm.fn == 0 || trgMesh->cm.fn == 0, "Both meshes require to have faces");
+ CheckError(!trgMesh->hasDataMask(MeshModel::MM_WEDGTEXCOORD), "Target mesh doesn't have Per Wedge Texture Coordinates");
+
+ QImage srcImg;
+
+ if (colorSampling) {
+ CheckError(!srcMesh->hasDataMask(MeshModel::MM_VERTCOLOR), "Source mesh doesn't have Per Vertex Color");
+ } else {
+ CheckError(!srcMesh->hasDataMask(MeshModel::MM_WEDGTEXCOORD), "Source mesh doesn't have Per Wedge Texture Coordinates");
+ CheckError(srcMesh->cm.textures.empty(), "Source mesh doesn't have any associated texture");
+ QString path(srcMesh->fileName.c_str());
+ path = path.left(std::max(path.lastIndexOf('\\'),path.lastIndexOf('/'))+1).append(srcMesh->cm.textures[0].c_str());
+ CheckError(!QFile(path).exists(), QString("Source texture \"").append(path).append("\" doesn't exists"));
+ CheckError(!srcImg.load(path), QString("Source texture \"").append(path).append("\" cannot be opened"));
+ }
+
+ QString filePath(trgMesh->fileName.c_str());
+ filePath = filePath.left(std::max(filePath.lastIndexOf('\\'),filePath.lastIndexOf('/'))+1);
+
+ if (!overwrite)
+ {
+ // Check textName and eventually add .png ext
+ CheckError(textName.length() == 0, "Texture file not specified");
+ CheckError(std::max(textName.lastIndexOf("\\"),textName.lastIndexOf("/")) != -1, "Path in Texture file not allowed");
+ if (!textName.endsWith(".png", Qt::CaseInsensitive))
+ textName.append(".png");
+ filePath.append(textName);
+ } else {
+ CheckError(trgMesh->cm.textures.empty(), "Mesh has no associated texture to overwrite");
+ filePath.append(trgMesh->cm.textures[0].c_str());
+ }
+
+ CheckError(textW <= 0, "Texture Width has an incorrect value");
+ CheckError(textH <= 0, "Texture Height has an incorrect value");
+
+ // Image creation
+ QImage img(QSize(textW,textH), QImage::Format_ARGB32);
+ img.fill(qRgba(0,0,0,0));
+
+ // Compute (texture-space) border edges
+ trgMesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
+ vcg::tri::UpdateTopology::FaceFaceFromTexCoord(trgMesh->cm);
+ vcg::tri::UpdateFlags::FaceBorderFromFF(trgMesh->cm);
+
+ // Rasterizing faces
+ srcMesh->updateDataMask(MeshModel::MM_FACEMARK);
+ vcg::tri::UpdateNormals::PerFaceNormalized(srcMesh->cm);
+ vcg::tri::UpdateFlags::FaceProjection(srcMesh->cm);
+ if (colorSampling)
+ {
+ TransferColorSampler sampler(srcMesh->cm, img, upperbound); // color sampling
+ sampler.InitCallback(cb, trgMesh->cm.fn, 0, 80);
+ TextureCorrected(trgMesh->cm,sampler,img.width(),img.height());
+ } else {
+ TransferColorSampler sampler(srcMesh->cm, img, &srcImg, upperbound); // texture sampling
+ sampler.InitCallback(cb, trgMesh->cm.fn, 0, 80);
+ TextureCorrected(trgMesh->cm,sampler,img.width(),img.height());
+ }
+
+ // Revert alpha values from border edge pixel to 255
+ cb(81, "Cleaning up texture ...");
+ for (int y=0; y 0)
+ img.setPixel(x,y, px | 0xff000000);
+ }
+
+ // PullPush
+ cb(85, "Filling texture holes...");
+ vcg::PullPush(img, qRgba(0,0,0,0));
+
+ // Undo topology changes
+ vcg::tri::UpdateTopology::FaceFace(trgMesh->cm);
+ vcg::tri::UpdateFlags::FaceBorderFromFF(trgMesh->cm);
+
+ // Save texture
+ cb(90, "Saving texture ...");
+ CheckError(!img.save(filePath), "Texture file cannot be saved");
+ Log(GLLogStream::FILTER, "Texture \"%s\" Created", filePath.toStdString().c_str());
+ assert(QFile(filePath).exists());
+
+ // Assign texture
+ if (assign && !overwrite) {
+ m.cm.textures.clear();
+ m.cm.textures.push_back(textName.toStdString());
+ }
+ cb(100, "Done");
+
+ }
+ break;
default: assert(0);
}
return true;
diff --git a/src/meshlabplugins/filter_texture/filter_texture.h b/src/meshlabplugins/filter_texture/filter_texture.h
index 30a375ae7..4d948bbba 100644
--- a/src/meshlabplugins/filter_texture/filter_texture.h
+++ b/src/meshlabplugins/filter_texture/filter_texture.h
@@ -40,7 +40,8 @@ public:
FP_UV_WEDGE_TO_VERTEX,
FP_BASIC_TRIANGLE_MAPPING,
FP_SET_TEXTURE,
- FP_COLOR_TO_TEXTURE
+ FP_COLOR_TO_TEXTURE,
+ FP_MESH_TEXCOLOR_TRANSFER
};
FilterTexturePlugin();
@@ -48,7 +49,7 @@ public:
virtual QString filterName(FilterIDType filter) const;
virtual QString filterInfo(FilterIDType filter) const;
virtual bool autoDialog(QAction *) {return true;}
- virtual void initParameterSet(QAction *,MeshModel &/*m*/, RichParameterSet & /*parent*/);
+ virtual void initParameterSet(QAction *,MeshDocument &/*m*/, RichParameterSet & /*parent*/);
virtual bool applyFilter(QAction *filter, MeshModel &m, RichParameterSet & /*parent*/, vcg::CallBackPos * cb);
virtual int getRequirements(QAction *);
virtual int getPreConditions(QAction *) const;
diff --git a/src/meshlabplugins/filter_texture/filter_texture.pro b/src/meshlabplugins/filter_texture/filter_texture.pro
index c222c60de..ffb9f7455 100644
--- a/src/meshlabplugins/filter_texture/filter_texture.pro
+++ b/src/meshlabplugins/filter_texture/filter_texture.pro
@@ -1,7 +1,8 @@
include (../../sharedfilter.pri)
HEADERS += filter_texture.h \
- pushpull.h
+ pushpull.h \
+ rastering.h
SOURCES += filter_texture.cpp
diff --git a/src/meshlabplugins/filter_texture/rastering.h b/src/meshlabplugins/filter_texture/rastering.h
new file mode 100644
index 000000000..cea659ada
--- /dev/null
+++ b/src/meshlabplugins/filter_texture/rastering.h
@@ -0,0 +1,328 @@
+/****************************************************************************
+* MeshLab o o *
+* A versatile mesh processing toolbox o o *
+* _ O _ *
+* Copyright(C) 2005 \/)\/ *
+* Visual Computing Lab /\/| *
+* ISTI - Italian National Research Council | *
+* \ *
+* All rights reserved. *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
+* for more details. *
+* *
+****************************************************************************/
+
+#ifndef _RASTERING_H
+#define _RASTERING_H
+
+#include
+#include
+#include
+#include
+
+class RasterSampler
+{
+ QImage &trgImg;
+
+ // Callback stuff
+ vcg::CallBackPos *cb;
+ const CMeshO::FaceType *currFace;
+ int faceNo, faceCnt, start, offset;
+
+public:
+ RasterSampler(QImage &_img) : trgImg(_img) {}
+
+ void InitCallback(vcg::CallBackPos *_cb, int _faceNo, int _start=0, int _offset=100)
+ {
+ assert(_faceNo > 0);
+ assert(_start>=0);
+ assert(_offset>=0 && _offset <= 100-_start);
+ cb = _cb;
+ faceNo = _faceNo;
+ faceCnt = 0;
+ start = _start;
+ offset = _offset;
+ currFace = NULL;
+ }
+
+ // expects points outside face (face affecting) with baycentric coords = (bX, bY, -)
+ void AddTextureSample(const CMeshO::FaceType &f, const CMeshO::CoordType &p, const vcg::Point2i &tp)
+ {
+ CMeshO::VertexType::ColorType c;
+ CMeshO::CoordType bary = p;
+ int alpha = 255;
+ if (fabs(p[0]+p[1]+p[2]-1)>=0.00001)
+ if (p[0] <.0) {alpha = 254+p[0]*128; bary[0] = 0.;} else
+ if (p[1] <.0) {alpha = 254+p[1]*128; bary[1] = 0.;} else
+ if (p[2] <.0) {alpha = 254+p[2]*128; bary[2] = 0.;}
+ if (alpha==255 || qAlpha(trgImg.pixel(tp.X(), trgImg.height() - tp.Y())) < alpha)
+ {
+ c.lerp(f.V(0)->cC(), f.V(1)->cC(), f.V(2)->cC(), bary);
+ trgImg.setPixel(tp.X(), trgImg.height() - tp.Y(), qRgba(c[0], c[1], c[2], alpha));
+ }
+ if (cb)
+ {
+ if (&f != currFace) {currFace = &f; ++faceCnt;}
+ cb(start + faceCnt*offset/faceNo, "Rasterizing faces ...");
+ }
+ }
+};
+
+class TransferColorSampler
+{
+ typedef vcg::GridStaticPtr MetroMeshGrid;
+
+ QImage &trgImg;
+ QImage *srcImg;
+ float dist_upper_bound;
+ bool fromTexture;
+ MetroMeshGrid unifGridFace;
+
+ // Callback stuff
+ vcg::CallBackPos *cb;
+ const CMeshO::FaceType *currFace;
+ int faceNo, faceCnt, start, offset;
+
+ typedef vcg::tri::FaceTmark MarkerFace;
+ MarkerFace markerFunctor;
+
+public:
+ TransferColorSampler(CMeshO &_srcMesh, QImage &_trgImg, float upperBound)
+ : trgImg(_trgImg), dist_upper_bound(upperBound)
+ {
+ unifGridFace.Set(_srcMesh.face.begin(),_srcMesh.face.end());
+ markerFunctor.SetMesh(&_srcMesh);
+ fromTexture = false;
+ }
+
+ TransferColorSampler(CMeshO &_srcMesh, QImage &_trgImg, QImage *_srcImg, float upperBound)
+ : trgImg(_trgImg), dist_upper_bound(upperBound)
+ {
+ assert(_srcImg != NULL);
+ srcImg = _srcImg;
+ unifGridFace.Set(_srcMesh.face.begin(),_srcMesh.face.end());
+ markerFunctor.SetMesh(&_srcMesh);
+ fromTexture = true;
+ }
+
+ void InitCallback(vcg::CallBackPos *_cb, int _faceNo, int _start=0, int _offset=100)
+ {
+ assert(_faceNo > 0);
+ assert(_start>=0);
+ assert(_offset>=0 && _offset <= 100-_start);
+ cb = _cb;
+ faceNo = _faceNo;
+ faceCnt = 0;
+ start = _start;
+ offset = _offset;
+ currFace = NULL;
+ }
+ // expects points outside face (face affecting) with baycentric coords = (bX, bY, -)
+ void AddTextureSample(const CMeshO::FaceType &f, const CMeshO::CoordType &p, const vcg::Point2i &tp)
+ {
+ // Calculate correct brycentric coords
+ CMeshO::CoordType bary = p;
+ int alpha = 255;
+ if (fabs(p[0]+p[1]+p[2]-1)>=0.00001)
+ if (p[0] <.0) {alpha = 254+p[0]*128; bary[0] = 0.;} else
+ if (p[1] <.0) {alpha = 254+p[1]*128; bary[1] = 0.;} else
+ if (p[2] <.0) {alpha = 254+p[2]*128; bary[2] = 0.;}
+
+ // Get point on face
+ CMeshO::CoordType startPt;
+ startPt[0] = bary[0]*f.V(0)->P().X()+bary[1]*f.V(1)->P().X()+bary[2]*f.V(2)->P().X();
+ startPt[1] = bary[0]*f.V(0)->P().Y()+bary[1]*f.V(1)->P().Y()+bary[2]*f.V(2)->P().Y();
+ startPt[2] = bary[0]*f.V(0)->P().Z()+bary[1]*f.V(1)->P().Z()+bary[2]*f.V(2)->P().Z();
+
+ // Retrieve closest point on source mesh
+ CMeshO::CoordType closestPt;
+ vcg::face::PointDistanceBaseFunctor PDistFunct;
+ float dist=dist_upper_bound;
+ CMeshO::FaceType *nearestF;
+ nearestF = unifGridFace.GetClosest(PDistFunct, markerFunctor, startPt, dist_upper_bound, dist, closestPt);
+ if (dist == dist_upper_bound) return;
+
+ // Convert point to barycentric coords
+ vcg::Point3f interp;
+ int axis = 0;
+ float tmp = -1;
+ for (int i=0; i<3; ++i)
+ if (fabs(nearestF->cN()[i]) > tmp) {tmp = fabs(nearestF->cN()[i]); axis = i;}
+ bool ret = InterpolationParameters(*nearestF, axis, closestPt, interp);
+ assert(ret);
+ interp[2]=1.0-interp[1]-interp[0];
+
+ if (fromTexture)
+ {
+ // NOT IMPLEMENTED YET
+ }
+ else
+ {
+ // Calculate and set color
+ if (alpha==255 || qAlpha(trgImg.pixel(tp.X(), trgImg.height() - tp.Y())) < alpha)
+ {
+ CMeshO::VertexType::ColorType c;
+ c.lerp(nearestF->V(0)->cC(), nearestF->V(1)->cC(), nearestF->V(2)->cC(), interp);
+ trgImg.setPixel(tp.X(), trgImg.height() - tp.Y(), qRgba(c[0], c[1], c[2], alpha));
+ }
+ }
+
+ if (cb)
+ {
+ if (&f != currFace) {currFace = &f; ++faceCnt;}
+ cb(start + faceCnt*offset/faceNo, "Rasterizing faces ...");
+ }
+ }
+};
+
+template
+static void SingleFaceRasterWEdge(typename MetroMesh::FaceType &f, VertexSampler &ps,
+ const vcg::Point2 & v0,
+ const vcg::Point2 & v1,
+ const vcg::Point2 & v2)
+{
+ typedef typename MetroMesh::ScalarType S;
+ // Calcolo bounding box
+ vcg::Box2i bbox;
+
+ if(v0[0]int(v2[0])) bbox.min[0]=int(v2[0]);
+ else if(bbox.max[0]int(v2[1])) bbox.min[1]=int(v2[1]);
+ else if(bbox.max[1] d10 = v1 - v0;
+ vcg::Point2 d21 = v2 - v1;
+ vcg::Point2 d02 = v0 - v2;
+
+ // Preparazione prodotti scalari
+ S b0 = (bbox.min[0]-v0[0])*d10[1] - (bbox.min[1]-v0[1])*d10[0];
+ S b1 = (bbox.min[0]-v1[0])*d21[1] - (bbox.min[1]-v1[1])*d21[0];
+ S b2 = (bbox.min[0]-v2[0])*d02[1] - (bbox.min[1]-v2[1])*d02[0];
+ // Preparazione degli steps
+ S db0 = d10[1];
+ S db1 = d21[1];
+ S db2 = d02[1];
+ // Preparazione segni
+ S dn0 = -d10[0];
+ S dn1 = -d21[0];
+ S dn2 = -d02[0];
+
+ //Calcolo orientamento
+ bool flipped = d02 * vcg::Point2(-d10[1], d10[0]) >= 0;
+
+ // Precalcolo Calcolo edge di bordo
+ vcg::Segment2 borderEdges[3];
+ S edgeLength[3];
+ unsigned char edgeMask = 0;
+ if (f.IsB(0)) {
+ borderEdges[0] = vcg::Segment2(v0, v1);
+ edgeLength[0] = borderEdges[0].Length();
+ edgeMask |= 1;
+ }
+ if (f.IsB(1)) {
+ borderEdges[1] = vcg::Segment2(v1, v2);
+ edgeLength[1] = borderEdges[1].Length();
+ edgeMask |= 2;
+ }
+ if (f.IsB(2)) {
+ borderEdges[2] = vcg::Segment2(v2, v0);
+ edgeLength[2] = borderEdges[2].Length();
+ edgeMask |= 4;
+ }
+
+ // Rasterizzazione
+ double de = v0[0]*v1[1]-v0[0]*v2[1]-v1[0]*v0[1]+v1[0]*v2[1]-v2[0]*v1[1]+v2[0]*v0[1];
+
+ for(int x=bbox.min[0]-1;x<=bbox.max[0]+1;++x)
+ {
+ bool in = false;
+ S n[3] = { b0-db0-dn0, b1-db1-dn1, b2-db2-dn2};
+ for(int y=bbox.min[1]-1;y<=bbox.max[1]+1;++y)
+ {
+ if((n[0]>=0 && n[1]>=0 && n[2]>=0) || (n[0]<=0 && n[1]<=0 && n[2]<=0))
+ {
+ typename MetroMesh::CoordType baryCoord;
+ baryCoord[0] = double(-y*v1[0]+v2[0]*y+v1[1]*x-v2[0]*v1[1]+v1[0]*v2[1]-x*v2[1])/de;
+ baryCoord[1] = -double( x*v0[1]-x*v2[1]-v0[0]*y+v0[0]*v2[1]-v2[0]*v0[1]+v2[0]*y)/de;
+ baryCoord[2] = 1-baryCoord[0]-baryCoord[1];
+
+ ps.AddTextureSample(f, baryCoord, vcg::Point2i(x,y));
+ in = true;
+ } else {
+ // Check wheter a pixel outside (on a border edge side) triangle affects color inside it
+ vcg::Point2 px(x, y);
+ vcg::Point2 closePoint;
+ int closeEdge = -1;
+ S minDst = FLT_MAX;
+
+ for (int i=0, t=0; t<2 && i<3 && (edgeMask>>i)%2 ; ++i)
+ {
+ vcg::Point2 close;
+ S dst;
+ if ( (flipped && n[i]<0 || !flipped && n[i]>0) &&
+ (dst = ((close = ClosestPoint(borderEdges[i], px)) - px).Norm()) < minDst &&
+ close.X() > px.X()-1 && close.X() < px.X()+1 &&
+ close.Y() > px.Y()-1 && close.Y() < px.Y()+1)
+ {
+ minDst = dst;
+ closePoint = close;
+ closeEdge = i;
+ ++t;
+ }
+ }
+
+ if (closeEdge >= 0)
+ {
+ // Add x,y sample with closePoint barycentric coords and -min
+ typename MetroMesh::CoordType baryCoord;
+ baryCoord[closeEdge] = (closePoint - borderEdges[closeEdge].P(1)).Norm()/edgeLength[closeEdge];
+ baryCoord[(closeEdge+1)%3] = 1 - baryCoord[closeEdge];
+ baryCoord[(closeEdge+2)%3] = -minDst;
+ ps.AddTextureSample(f, baryCoord, vcg::Point2i(x,y));
+ in = true;
+ } else if (in) break;
+ }
+ n[0] += dn0;
+ n[1] += dn1;
+ n[2] += dn2;
+ }
+ b0 += db0;
+ b1 += db1;
+ b2 += db2;
+ }
+}
+
+template
+static void TextureCorrected(MetroMesh & m, VertexSampler &ps, int textureWidth, int textureHeight)
+{
+ typedef typename MetroMesh::FaceIterator FaceIterator;
+ FaceIterator fi;
+
+ printf("Similar Triangles face sampling\n");
+ for(fi=m.face.begin(); fi != m.face.end(); fi++)
+ if (!fi->IsD())
+ {
+ vcg::Point2f ti[3];
+ for(int i=0;i<3;++i)
+ ti[i]=vcg::Point2f((*fi).WT(i).U() * textureWidth - 0.5, (*fi).WT(i).V() * textureHeight + 0.5);
+ //vcg::tri::SurfaceSampling::SingleFaceRaster(*fi, ps, ti[0],ti[1],ti[2]);
+ SingleFaceRasterWEdge(*fi, ps, ti[0],ti[1],ti[2]);
+ }
+}
+
+#endif
\ No newline at end of file