在MeshLab中對其功能的擴展是通過插件來實現的,通過實現不同的接口,可以將插件放在不同的菜單下或工具欄上,在這裏只介紹Filter菜單下的插件實現,其他的類似。
1. 首先在meshlab/src目錄下創建myplugins文件夾,然後用QCreator打開meshlab_mini.pro。創建一個新的子工程叫helloplugin,選擇空Qt項目即可,並保存在myplugins文件下,如圖1所示。
圖1 創建的helloplugin空項目
2.MeshLab提供了一個公用的shared.pri,裏面包含了通用的插件編譯配置,只需在helloplugin.pro包含該文件即完成了插件編譯的基本配置。在helloplugin.pro中包含該文件:include (../../shared.pri)。然後在該工程下創建一個HelloPlugin類,繼承自QObject,如圖2所示。
圖2 創建HelloPlugin類
3.MeshLab提供了MeshFilterInterface接口,用於實現Filter菜單下的插件,因此需要實現該接口。
//helloplugin.h
#ifndef HELLOPLUGIN_H
#define HELLOPLUGIN_H
#include <QObject>
#include <common/interfaces.h>
class HelloPlugin : public QObject, public MeshFilterInterface
{
Q_OBJECT
Q_INTERFACES(MeshFilterInterface) //指名MeshFilterInterface爲接口
public:
explicit HelloPlugin(QObject *parent = 0);
//……
};
#endif // HELLOPLUGIN_H
//helloplugin.cpp
#include "helloplugin.h"
HelloPlugin::HelloPlugin(QObject *parent) :
QObject(parent)
{
}
Q_EXPORT_PLUGIN(HelloPlugin) //導出插件
4.接下來逐步完成Filter菜單下的插件,是通過實現MeshFilterInterface接口中的功能來完成。下面介紹下幾個核心函數和成員變量:
/**
\brief The MeshFilterInterface class provide the interface of the filter plugins.
*/
class MeshFilterInterface : public MeshCommonInterface
{
public:
/** The FilterClass enum represents the set of keywords that must be used to categorize a filter.
Each filter can belong to one or more filtering class, or-ed togheter.
*/
enum FilterClass
{
Generic =0x00000, /*!< Should be avoided if possible. */ //
Selection =0x00001, /*!< select or de-select something, basic operation on selections (like deleting)*/
Cleaning =0x00002, /*!< Filters that can be used to clean meshes (duplicated vertices etc)*/
Remeshing =0x00004, /*!< Simplification, Refinement, Reconstruction and mesh optimization*/
FaceColoring =0x00008,
VertexColoring =0x00010,
MeshCreation =0x00020,
Smoothing =0x00040, /*!< Stuff that does not change the topology, but just the vertex positions*/
Quality =0x00080,
Layer =0x00100, /*!< Layers, attributes */
RasterLayer =0x20000, /*!< Raster Layers, attributes */
Normal =0x00200, /*!< Normal, Curvature, orientation (rotations and transformations fall here)*/
Sampling =0x00400,
Texture =0x00800,
RangeMap =0x01000, /*!< filters specific for range map processing*/
PointSet =0x02000,
Measure =0x04000, /*!< Filters that compute measures and information on meshes.*/
Polygonal =0x08000, /*!< Filters that works on polygonal and quad meshes.*/
Camera =0x10000 /*!< Filters that works on shot of mesh and raster.*/
};
MeshFilterInterface() : MeshCommonInterface()
{
}
virtual ~MeshFilterInterface() {}
/** The very short string (a few words) describing each filtering action
// This string is used also to define the menu entry
*/
virtual QString filterName(FilterIDType ) const =0;
/** The long, formatted string describing each filtering action.
// This string is printed in the top of the parameter window
// so it should be at least one or two paragraphs long. The more the better.
// you can use simple html formatting tags (like "<br>" "<b>" and "<i>") to improve readability.
// This string is used in the 'About plugin' dialog and by meshlabserver to create the filter list wiki page and the doxygen documentation of the filters.
// Here is the place where you should put you bibliographic references in a form like this:
<br>
See: <br />
<i>Luiz Velho, Denis Zorin </i><br/>
<b>"4-8 Subdivision"</b><br/>
CAGD, volume 18, Issue 5, Pages 397-427.<br/>
<br>
e.g. italic for authors, bold for title (quoted) and plain for bib ref.
*/
virtual QString filterInfo(FilterIDType filter) const =0;
/** The FilterClass describes in which generic class of filters it fits.
// This choice affect the submenu in which each filter will be placed
// For example filters that perform an action only on the selection will be placed in the Selection Class
*/
virtual FilterClass getClass(QAction *) { return MeshFilterInterface::Generic; }
/**
The filters can have some additional requirements on the mesh capabiliteis.
// For example if a filters requires Face-Face Adjacency you shoud re-implement
// this function making it returns MeshModel::MM_FACEFACETOPO.
// The framework will ensure that the mesh has the requirements satisfied before invoking the applyFilter function
//
// Furthermore, requirements are checked just before the invocation of a filter. If your filter
// outputs a never used before mesh property (e.g. face colors), it will be allocated by a call
// to MeshModel::updateDataMask(...)
*/
virtual int getRequirements(QAction *){return MeshModel::MM_NONE;}
/** The FilterPrecondition mask is used to explicitate what kind of data a filter really needs to be applied.
// For example algorithms that compute per face quality have as precondition the existence of faces
// (but quality per face is not a precondition, because quality per face is created by these algorithms)
// on the other hand an algorithm that deletes faces according to the stored quality has both FaceQuality
// and Face as precondition.
// These conditions do NOT include computed properties like borderFlags, manifoldness or watertightness.
// They are also used to grayout menus un-appliable entries.
*/
virtual int getPreConditions(QAction *) const {return MeshModel::MM_NONE;}
/** Function used by the framework to get info about the mesh properties changed by the filter.
// It is widely used by the meshlab's preview system.
//TO BE REPLACED WITH = 0
*/
virtual int postCondition( QAction* ) const {return MeshModel::MM_UNKNOWN;}
/** \brief applies the selected filter with the already stabilished parameters
* This function is called by the framework after getting values for the parameters specified in the \ref InitParameterSet
* NO GUI interaction should be done here. No dialog asking, no messagebox errors.
* Think that his function will also be called by the commandline framework.
* If you want report errors, use the \ref errorMsg() string. It will displayed in case of filters returning false.
* When implementing your applyFilter, you should use the cb function to report to the framework the current state of the processing.
* During your (long) processing you should call from time to time cb(perc,descriptiveString), where perc is an int (0..100)
* saying what you are doing and at what point of the computation you currently are.
* \sa errorMsg
* \sa initParameterSet
*/
virtual bool applyFilter(QAction * filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) =0;
/** \brief tests if a filter is applicable to a mesh.
This function is a handy wrapper used by the framework for the \a getPreConditions callback;
For istance a colorize by quality filter cannot be applied to a mesh without per-vertex-quality.
On failure (returning false) the function fills the MissingItems list with strings describing the missing items.
*/
bool isFilterApplicable(QAction *act, const MeshModel& m, QStringList &MissingItems) const;
// This function is called to initialized the list of parameters.
// it is always called. If a filter does not need parameter it leave it empty and the framework
// will not create a dialog (unless for previewing)
virtual void initParameterSet(QAction *,MeshModel &/*m*/, RichParameterSet & /*par*/) {}
virtual void initParameterSet(QAction *filter,MeshDocument &md, RichParameterSet &par)
{initParameterSet(filter,*(md.mm()),par);}
/** \brief is invoked by the framework when the applyFilter fails to give some info to the user about the fiter failure
* Filters \b must never use QMessageBox for reporting errors.
* Failing filters should put some meaningful information inside the errorMessage string and return false with the \ref applyFilter
*/
const QString &errorMsg() {return this->errorMessage;}
virtual QString filterInfo(QAction *a) const {return this->filterInfo(ID(a));}
virtual QString filterName(QAction *a) const {return this->filterName(ID(a));}
virtual QString filterScriptFunctionName(FilterIDType /*filterID*/) {return "";}
//………………………..略
protected:
// Each plugins exposes a set of filtering possibilities.
// Each filtering procedure corresponds to a single QAction with a corresponding FilterIDType id.
//
// The list of actions exported by the plugin. Each actions strictly corresponds to
QList <QAction *> actionList;
QList <FilterIDType> typeList;
// this string is used to pass back to the framework error messages in case of failure of a filter apply.
QString errorMessage;
};
5.添加到Filter菜單下的插件有好幾個分類,已是預定義好的,在FilterClass所定義的枚舉類型,前面的代碼中有定義。插件中可以有許多的功能,每個功能可以列入FilterClass中的一個分類,這些功能都存在成員變量typeList中,其相應的動作響應由actionList來承接,參見上面代碼。
在HelloPlugin.h中定義該插件所具有的功能,加入代碼:
enum{//定義HelloPlugin插件所具有的功能
FP_FUNC_ONE,
FP_FUNC_TWO,
FP_FUNC_THREE
};
//重寫MeshFilterInterface中的虛函數,實現自己的功能
//給所定義的功能起個名字,該名字是要顯示在filter菜單中的子菜單中的,比如:給FP_FUNC_ONE起名爲“Function one”
virtual QString filterName(FilterIDType filter) const;
//給所定義的功能進行描述
virtual QString filterInfo(FilterIDType filter) const;
//給所定義的功能進行分類,即加入filter菜單中的哪個子菜單
virtual FilterClass getClass(QAction* a);
//定義參數輸入的GUI界面,進行交互
virtual void initParameterSet(QAction* a,MeshModel& mm,RichParameterSet& parlst);
//判定該所執行的功能在具體運行前,是不是已滿足運行條件
virtual int getPreConditions(QAction * a) const;
//應用該插件所執行的功能
virtual bool applyFilter(QAction *filter, MeshDocument &md, RichParameterSet &par, vcg::CallBackPos *cb);
然後在HelloPlugin.cpp中實現這些功能,代碼如下:
//HelloPlugin.cpp
#include "helloplugin.h"
HelloPlugin::HelloPlugin(QObject *parent) :
QObject(parent),MeshFilterInterface()
{
typeList << FP_FUNC_ONE
<<FP_FUNC_TWO
<<FP_FUNC_THREE;
foreach(FilterIDType tt,typeList)//爲各功能添加響應
actionList<< new QAction(filterName(tt),this);
}
QString HelloPlugin::filterName(FilterIDType filter) const{
//給各功能所起在名稱,即菜單名
switch(filter){
case FP_FUNC_ONE:
return tr("Function One");
case FP_FUNC_TWO:
return tr("Function Two");
case FP_FUNC_THREE:
return tr("Function Three");
default:assert(0);
}
return tr("");
}
QString HelloPlugin::filterInfo(FilterIDType filter) const{
//對各功能進行的解釋,描述
switch(filter){
case FP_FUNC_ONE:
return tr("the description for function one");
case FP_FUNC_TWO:
return tr("the description for function two");
case FP_FUNC_THREE:
return tr("the description for function three");
default: assert(0);
}
return tr("");
}
MeshFilterInterface::FilterClass HelloPlugin::getClass(QAction* a){
//定義每個功能所屬的子菜單
switch(ID(a)){
case FP_FUNC_ONE:
return MeshFilterInterface::Generic;//直接在filter菜單下
case FP_FUNC_TWO:
case FP_FUNC_THREE:
return MeshFilterInterface::Camera;//在filter->camera子菜單下
default:assert(0);
}
return MeshFilterInterface::Generic;
}
void HelloPlugin::initParameterSet(QAction* a,MeshModel& mm,RichParameterSet& parlst){
//定義每個功能接收參數的GUI,至於存在幾種控件類型,請查相關資料
switch(ID(a)){
case FP_FUNC_ONE:
{
parlst.addParam(new RichBool("Original",true,"remove original","if remove the original mesh."));
return;
}
case FP_FUNC_TWO:
{
parlst.addParam(new RichInt("Resolution",90,"resolution"," resolution when do voxelization"));
parlst.addParam(new RichFloat("Ratio",0.96,"Shreshold","the ratio over this threshold would be thought as the same."));
parlst.addParam(new RichBool("WriteFile",false,"write file","write the lightweighted model to file."));
parlst.addParam(new RichInt("OverNum",16,"over vertex num","if the vertex number of one mesh over this threshold, it will be write to an individual file"));
parlst.addParam(new RichBool("QuickSlow",false,"Quick","voxelize Quickly but high memory, else slowly with low memory."));
return;
}
case FP_FUNC_THREE:
{//不需要GUI接收參數
return;
}
default:assert(0);
}
return;
}
int HelloPlugin::getPreConditions(QAction * a) const{
//各個功能執行的先決條件
switch(ID(a)){
case FP_FUNC_ONE:
case FP_FUNC_TWO:
case FP_FUNC_THREE:
return MeshModel::MM_FACENUMBER;//必須存在三維模型
default:assert(0);
}
return MeshModel::MM_NONE;//不需任何條件
}
bool HelloPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet &par, vcg::CallBackPos *cb){
//your work is here
return true;
}
Q_EXPORT_PLUGIN(HelloPlugin)
如此,一個簡單的、不執行任何功能的插件編寫完成,上個截圖:
輝輝
(FightingBull Studio)