Meshlab .stl文件转化.u3d文件&渲染 功能截取

背景

由于工作需要,需要将.stl文件转化为.u3d文件,经过调研发现,开源软件Meshlab有import mesh导入.stl和export mesh导出.u3d文件的功能,于是尝试将这个功能截取。

一、环境信息

系统环境:
Windows 10 64bits
软件环境:
Microsoft Visual Studio 2017 professional
Meshlab 源代码
Vcg 库
Qt5.9.1-mscv2017 源代码

二、大致步骤

1.安装并准备所需要的源码以及库文件
·安装QT库:由于我们只需要QT的VS2017支持库,所以其他什么也不要,只选择msvc2017 64-bit

选择msvc2017 64-bit安装

2.将Meshlab源码全部加载到VS2017中,通过Meshlab的ui信息,找到所需要的加载.stl、输出.u3d格式文件功能的源文件。Meshlab源码从https://github.com/cnr-isti-vclab/meshlab可以下载。
1)将Meshlab源文件加入项目中
导入后样子

2)在解决方案中寻找u3d相关代码,容易发现:
查找到和u3d相关的功能文件

3)从中发现了依赖外部的代码:
注:此时发现由于Meshlab需要依赖于外部的Vcglib以及Qt的库函数,所以此时有很多报错。
发现的代码

4)可以发现,这是调用vcglib中的函数。由于当前环境没有加入vcg库,所以给项目配置相关环境。(配置环境需要一下一下寻找)
附加库路径

3.从获取到的Meshlab中找到的代码中挖掘,发现Meshlab实际上是利用了Vcglib中的函数:

vcg::tri::io::ImporterSTL::Open()
vcg::tri::io::ExporterU3D::Save()

4.阅读Meshlab和Vcglib相关的源码,将上述的代码相关的部分——包括参数结构的构建,函数输出构建理清楚,准备开始编写程序。

三、编写程序

变量定义:
typedef float MESHLAB_SCALAR;
typedef vcg::Box3     Box3m;
typedef vcg::Matrix44 Matrix44m;
typedef bool CallBackPos(const int pos, const char * str);
typedef vcg::Point3   Point3m;

要使用Open与Save函数,需要构建相关类。经过阅读代码后,发现需要构建以下类:

class CVertexO;
class CEdgeO;
class CFaceO;
class CUsedTypesO
class CMeshO;
namespace vcg
{
    namespace vertex
    {

        template  class Coord3m : public Coord, T> {
        public: static int Name(std::vector& name) { name.push_back(std::string("Coord3m")); T::Name(name); }
        };

        template  class Normal3m : public Normal, T> {
        public: static int Name(std::vector& name) { name.push_back(std::string("Normal3m")); T::Name(name); }
        };

        template  class CurvatureDirmOcf : public CurvatureDirOcf, T> {
        public: static int Name(std::vector& name) { name.push_back(std::string("CurvatureDirmOcf")); T::Name(name); }
        };

        template  class RadiusmOcf : public RadiusOcf {
        public: static int Name(std::vector& name) { name.push_back(std::string("RadiusmOcf")); T::Name(name); }
        };

    }//end namespace vertex
    namespace face
    {
        template  class Normal3m : public NormalAbs, T> {
        public:  static int Name(std::vector& name) { name.push_back(std::string("Normal3m")); T::Name(name); }
        };

        template  class CurvatureDirmOcf : public CurvatureDirOcf, T> {
        public: static int Name(std::vector& name) { name.push_back(std::string("CurvatureDirdOcf")); T::Name(name); }
        };

    }//end namespace face

}//end namespace vcg

// Forward declarations needed for creating the used types

class CVertexO;
class CEdgeO;
class CFaceO;

// Declaration of the semantic of the used types
class CUsedTypesO : public vcg::UsedTypes < vcg::Use::AsVertexType,
    vcg::Use::AsEdgeType,
    vcg::Use::AsFaceType > {};

// The Main Vertex Class
// Most of the attributes are optional and must be enabled before use.
// Each vertex needs 40 byte, on 32bit arch. and 44 byte on 64bit arch.

class CVertexO : public vcg::Vertex< CUsedTypesO,
    vcg::vertex::InfoOcf,           /*  4b */
    vcg::vertex::Coord3m,           /* 12b */
    vcg::vertex::BitFlags,          /*  4b */
    vcg::vertex::Normal3m,          /* 12b */
    vcg::vertex::Qualityf,          /*  4b */
    vcg::vertex::Color4b,           /*  4b */
    vcg::vertex::VFAdjOcf,          /*  0b */
    vcg::vertex::MarkOcf,           /*  0b */
    vcg::vertex::TexCoordfOcf,      /*  0b */
    vcg::vertex::CurvaturefOcf,     /*  0b */
    vcg::vertex::CurvatureDirmOcf,  /*  0b */
    vcg::vertex::RadiusmOcf         /*  0b */
> {
};


// The Main Edge Class
class CEdgeO : public vcg::Edge {
};

// Each face needs 32 byte, on 32bit arch. and 48 byte on 64bit arch.
class CFaceO : public vcg::Face<  CUsedTypesO,
    vcg::face::InfoOcf,              /* 4b */
    vcg::face::VertexRef,            /*12b */
    vcg::face::BitFlags,             /* 4b */
    vcg::face::Normal3m,             /*12b */
    vcg::face::QualityfOcf,          /* 0b */
    vcg::face::MarkOcf,              /* 0b */
    vcg::face::Color4bOcf,           /* 0b */
    vcg::face::FFAdjOcf,             /* 0b */
    vcg::face::VFAdjOcf,             /* 0b */
    vcg::face::CurvatureDirmOcf,     /* 0b */
    vcg::face::WedgeTexCoordfOcf     /* 0b */
> {};

class CMeshO : public vcg::tri::TriMesh< vcg::vertex::vector_ocf, vcg::face::vector_ocf >
{
public:
    int sfn;    //The number of selected faces.
    int svn;    //The number of selected vertices.

    int pvn; //the number of the polygonal vertices
    int pfn; //the number of the polygonal faces 

    Matrix44m Tr; // Usually it is the identity. It is applied in rendering and filters can or cannot use it. (most of the filter will ignore this)

    const Box3m& trBB()
    {
        static Box3m bb;
        bb.SetNull();
        bb.Add(Tr, bbox);
        return bb;
    }
};
vcg::tri::io::u3dparametersclasses::Movie15Parameters _param;
typedef vcg::Color4  Color4b;

void getSurroundingFacesVF(CFaceO* fac, int vert_pos, std::vector* surround) {
    CVertexO* vert = fac->V(vert_pos);
    int pos = vert->cVFi();
    CFaceO* first_fac = vert->cVFp();
    CFaceO* curr_f = first_fac;
    do {
        CFaceO* temp = curr_f->VFp(pos);
        if (curr_f != 0 && !curr_f->IsD()) {
            surround->push_back(curr_f);
            pos = curr_f->VFi(pos);
        }
        curr_f = temp;
    } while (curr_f != first_fac && curr_f != 0);
}

void applyColor(CVertexO* vertex, const vcg::Color4b& newcol, int opac)
{
    vcg::Color4b orig = vertex->C();

    opac *= ((float)newcol[3] / 255.0);

    for (int i = 0; i < 3; i++)
        orig[i] = std::min(255, ((newcol[i] - orig[i]) * opac + orig[i] * 100) / 100);

    vertex->C() = orig;
}

Main函数为:

int main(int argc, char *argv[]) {
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
    char buff[1000];
    _getcwd(buff, 1000);

    argc = 3;
    string in_file = argv[1];
    string out_file = argv[2];

    CMeshO m;
    int loadmask = vcg::tri::io::Mask::IOM_VERTCOORD | vcg::tri::io::Mask::IOM_FACEINDEX;
    const int &b = loadmask;
    //open a mesh
    vcg::tri::io::ImporterSTL::Open(m, in_file.c_str());
    vcg::tri::io::u3dparametersclasses::Movie15Parameters _param;
    _param._campar = new vcg::tri::io::u3dparametersclasses::Movie15Parameters::CameraParameters(m.bbox.Center(), m.bbox.Diag());
    _param.positionQuality = 1000;
    //QApplication q(argc, argv);

    //save a mesh
    QString c = QString::fromLocal8Bit(buff);
    QString a = c + "\\IDTFConverter.exe";

    int mask = 0 | vcg::tri::io::Mask::IOM_VERTNORMAL | vcg::tri::io::Mask::IOM_VERTCOLOR | vcg::tri::io::Mask::IOM_FACECOLOR;// | vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
    //int mask = vcg::tri::io::ExporterU3D::GetExportMaskCapability();
    const int &ra = mask;
    QString curr = QDir::currentPath();

    int k = vcg::tri::io::ExporterU3D::Save(m, out_file.c_str(), a.toUtf8().data(), _param, 0, curr);
    //q.~QApplication();

    //test
    if (k == 1)
        printf("Successful '%s'\n '%s'\n '%s'\n", a.toUtf8().data(), c.toLocal8Bit().data(), buff);
    else
        printf("Fail");
    return 0;
}

渲染功能截取同理,需要添加额外函数:目的是获取每个面的边和点以及上色

typedef vcg::Color4  Color4b;

void getSurroundingFacesVF(CFaceO* fac, int vert_pos, std::vector* surround) {
    CVertexO* vert = fac->V(vert_pos);
    int pos = vert->cVFi();
    CFaceO* first_fac = vert->cVFp();
    CFaceO* curr_f = first_fac;
    do {
        CFaceO* temp = curr_f->VFp(pos);
        if (curr_f != 0 && !curr_f->IsD()) {
            surround->push_back(curr_f);
            pos = curr_f->VFi(pos);
        }
        curr_f = temp;
    } while (curr_f != first_fac && curr_f != 0);
}

void applyColor(CVertexO* vertex, const vcg::Color4b& newcol, int opac)
{
    vcg::Color4b orig = vertex->C();

    opac *= ((float)newcol[3] / 255.0);

    for (int i = 0; i < 3; i++)
        orig[i] = std::min(255, ((newcol[i] - orig[i]) * opac + orig[i] * 100) / 100);

    vertex->C() = orig;
}

渲染功能需要在主函数load和save mesh之间添加:

//上色,设置透明度
    std::vector face;

    int red, blue, green, opac;  //RGB值以及透明度(最大均255)
    red = 255;
    green = 50;
    blue = 70;
    opac = 125;
    //QColor newcol = QColor::QColor(red, green, blue, opac);
    QColor newcol = vcg::Color4b::Green;

    for (size_t i = 0; i < m.face.size(); ++i) {
        if (!m.face[i].IsD())
            face.push_back(&m.face[i]);
    }

    printf("\nkkkkkk  %zu  kkkkkk\n", face.size());


    Color4b color(newcol.red(), newcol.green(), newcol.blue(), newcol.alpha());
    CFaceO* fac = face.at(0);
    for (size_t j = 0; j < m.face.size(); ++j) {
        bool who = face[j]->IsS();
        CFaceO* fac = face.at(j);
        if (who == fac->IsS()) {
            for (int lauf = 0; lauf < 3; lauf++)
                applyColor(fac->V(lauf), color, opac);
        }
    }

四、部分代码解释:

1.QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
//为了支持中文路径,需要设定编码模式为”GBK”
2.Mask的作用是,由于VCG兼容众多图形,各种格式的图形所包含的内容不一样,所以需要掩码来决定使用哪些部分(如边角点颜色),以防格式混乱。
3.QString a = c + "\IDTFConverter.exe";由于使用exportU3D函数需要使用IDTFConverter.exe文件进行文件转化,所以需要预设其所在位置。

五、生成解决方案

右击项目点击生成,就会在debug目录下看到项目的exe可执行文件,通过命令行调用,如 project3.exe test.stl test.u3d命令,则可以得到最终转化后的文件。

六、可能遇到的问题

1.LNK链接错误,将依赖库文件QtWidgerd.dll,Qtcored.dll(release版本则为QtWidger.dll,Qtcore.dll)添加到连接器的附加库目录中。
2.执行exe文件时报错:当前文件目录下没有所依赖的.dll动态库文件,复制即可。
3.没有.u3d文件且未报错:命令权限不足,需要在非C盘路径下使用。
4.只有.tex文件:同3或缺失IDTFConverter.exe文件。

你可能感兴趣的:(Meshlab .stl文件转化.u3d文件&渲染 功能截取)