From 395513e73da238bf9e44764e8a8dde76c67ddb7d Mon Sep 17 00:00:00 2001 From: Paolo Cignoni cignoni Date: Thu, 25 May 2006 04:57:46 +0000 Subject: [PATCH] Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. --- docs/ToDo.txt | 45 +++ src/meshlab/filterscript.h | 20 ++ src/meshlab/glarea.cpp | 131 ++++--- src/meshlab/glarea.h | 57 +-- src/meshlab/images/icons.xar | Bin 43232 -> 44865 bytes src/meshlab/interfaces.h | 135 +++++-- src/meshlab/mainwindow.h | 25 +- src/meshlab/mainwindow_Init.cpp | 73 +++- src/meshlab/mainwindow_RunTime.cpp | 167 ++++++--- src/meshlab/meshlab.pro | 3 +- src/meshlab/meshlab.qrc | 14 +- src/meshlab/meshmodel.cpp | 69 ++-- src/meshlab/meshmodel.h | 144 +++++++- src/meshlab/plugindialog.cpp | 17 +- src/meshlab/ui/GenericELDialog.ui | 7 +- src/meshlabplugins/meshcolorize/curvature.h | 70 +--- .../meshcolorize/meshcolorize.cpp | 332 +++++++++-------- .../meshcolorize/meshcolorize.h | 62 ++-- .../meshdecorate/meshdecorate.cpp | 51 +-- .../meshdecorate/meshdecorate.h | 14 +- .../meshedit/images/select_face.png | Bin 0 -> 2632 bytes src/meshlabplugins/meshedit/meshedit.cpp | 200 +++++++++++ src/meshlabplugins/meshedit/meshedit.h | 65 ++++ src/meshlabplugins/meshedit/meshedit.pro | 31 ++ src/meshlabplugins/meshedit/meshlab.qrc | 5 + src/meshlabplugins/meshfilter/invert_faces.h | 18 +- src/meshlabplugins/meshfilter/meshfilter.cpp | 334 ++++++++---------- src/meshlabplugins/meshfilter/meshfilter.h | 92 ++--- src/meshlabplugins/meshfilter/meshfilter.pro | 10 +- src/meshlabplugins/meshio/meshio.cpp | 31 +- src/meshlabplugins/meshio/meshio.pro | 2 +- src/meshlabplugins/meshlabplugins.pro | 4 +- src/meshlabplugins/meshrender/meshrender.cpp | 11 +- src/meshlabplugins/meshrender/meshrender.h | 8 +- .../meshrender/shaderDialog.cpp | 6 +- src/meshlabplugins/meshrender/shaderDialog.h | 9 +- src/meshlabplugins/meshselect/meshselect.cpp | 129 +++++++ src/meshlabplugins/meshselect/meshselect.h | 62 ++++ src/meshlabplugins/meshselect/meshselect.pro | 27 ++ 39 files changed, 1660 insertions(+), 820 deletions(-) create mode 100644 docs/ToDo.txt create mode 100644 src/meshlab/filterscript.h create mode 100644 src/meshlabplugins/meshedit/images/select_face.png create mode 100644 src/meshlabplugins/meshedit/meshedit.cpp create mode 100644 src/meshlabplugins/meshedit/meshedit.h create mode 100644 src/meshlabplugins/meshedit/meshedit.pro create mode 100644 src/meshlabplugins/meshedit/meshlab.qrc create mode 100644 src/meshlabplugins/meshselect/meshselect.cpp create mode 100644 src/meshlabplugins/meshselect/meshselect.h create mode 100644 src/meshlabplugins/meshselect/meshselect.pro diff --git a/docs/ToDo.txt b/docs/ToDo.txt new file mode 100644 index 000000000..c408f5ae8 --- /dev/null +++ b/docs/ToDo.txt @@ -0,0 +1,45 @@ +25/5/06 +o clustering nel filtro in input epoch. +o install doc e attempt. + +24/5/06 + +o Uniformare i nomi degli slot nel main window +o Measuring Tool +o risistemare Recent file stuff...(bug in apertura cambio dir?) +o snapshot con fbo invece che con il rendering nel backbuffer. +o Ambient Occlusion +o Clustering dialog diag percentage <1 non va +o esc che ritorna all'ultimo modo di selezione. +x selection ico che stanno accese // DONE 24/5 +x selection drawing abilitato subito // DONE 24/5 +o filtro geodesic quality + +23/5/06 + +o scrivere doc di sample di fiultri +x aggiornare i filtri epoch // DONE 25/5 +o aggiungere un filtro in ingresso per aln + +22/5/06 + +o Mapping Colore Qualita Implementare e Debuggare +o Default colore e curvatura +o Colore nel clustering +x Face Selection Tool // DONE 24/5 + - Feature // DONE 24/5 + - Icon // DONE 24/5 +x Face Selection Drawing // DONE 24/5 + - Func // DONE 23/5 + - Ico e slot // DONE 24/5 +o Global Options, for each plugins +o Animation Spinning e interopolation +o Ripulire glarea + - Snapshot + - FPS + - Log + - Paint +o ScriptDialog +o Generic dialog option +o Clustering con bound e selezione. +o Dialogo shaders che galleggia un po' troppo diff --git a/src/meshlab/filterscript.h b/src/meshlab/filterscript.h new file mode 100644 index 000000000..8ac94a1e6 --- /dev/null +++ b/src/meshlab/filterscript.h @@ -0,0 +1,20 @@ +#ifndef FILTERSCRIPT_H +#define FILTERSCRIPT_H + +#include +#include + +#include "meshmodel.h" +//#include "glarea.h" + +class FilterScript +{ +public: + bool Open(QString filename); + bool Save(QString filename); + + QList< QPair< QAction *, FilterParameter> > actionList; + typedef QList< QPair >::iterator iterator; +}; + +#endif diff --git a/src/meshlab/glarea.cpp b/src/meshlab/glarea.cpp index 15613f89c..54ba11f0f 100644 --- a/src/meshlab/glarea.cpp +++ b/src/meshlab/glarea.cpp @@ -24,6 +24,10 @@ History $Log$ +Revision 1.100 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.99 2006/03/01 10:08:01 ponchio WHEEL_DELTA -> WHEEL_STEP as WHEEL_DELTA is already defined somewhere. @@ -72,8 +76,10 @@ GLArea::GLArea(QWidget *parent) animMode=AnimNone; iRenderer=0; //Shader support iDecoratorsList=0; - currentTime=0; - lastTime=0; + iEdit=0; + currentEditor=0; + //currentTime=0; + //lastTime=0; deltaTime=0; cfps=0; hasToPick=false; @@ -86,11 +92,10 @@ GLArea::GLArea(QWidget *parent) currentSharder = NULL; lastFilterRef = NULL; mm = NULL; - time.start(); currLogLevel = -1; // Projection Matrix starting settings - objDist = 3.f; + //objDist = 3.f; fov = 60; clipRatioFar = 1; clipRatioNear = 1; @@ -132,6 +137,10 @@ void GLArea::initializeGL() trackball_light.center=Point3f(0, 0, 0); trackball_light.radius= 1; + GLenum err = glewInit(); + if (err != GLEW_OK ) { + assert(0); + } } @@ -218,7 +227,10 @@ void GLArea::myGluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GL void GLArea::paintGL() { - lastTime=time.elapsed(); + QTime time; + time.start(); + + //int lastTime=time.elapsed(); initTexture(); glClearColor(1.0,1.0,1.0,0.0); //vannini: alpha was 1.0 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -304,7 +316,7 @@ void GLArea::paintGL() glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - myGluPerspective(fov, (GLdouble) currentWidth / (GLdouble) currentHeight, nearPlane, farPlane); + myGluPerspective(fov, (GLdouble) curSiz.width() / (GLdouble) curSiz.height(), nearPlane, farPlane); glMatrixMode(GL_MODELVIEW); } @@ -316,21 +328,32 @@ void GLArea::paintGL() glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); } - if(iRenderer && currentSharder) { + if(rm.backFaceCull) glEnable(GL_CULL_FACE); + else glDisable(GL_CULL_FACE); + + if(iRenderer && currentSharder) { glPushAttrib(GL_ALL_ATTRIB_BITS); iRenderer->Render(currentSharder, *mm, rm, this); } mm->Render(rm.drawMode,rm.colorMode,rm.textureMode); + if(iEdit){ + iEdit->Decorate(currentEditor,*mm,this); + } + if(iRenderer) { glPopAttrib(); glUseProgramObjectARB(0); } + // Draw the selection + if(rm.selectedFaces) + mm->RenderSelectedFaces(); + if(iDecoratorsList){ pair p; - foreach(p,*iDecoratorsList){p.second->Decorate(p.first,*mm,rm,this);} + foreach(p,*iDecoratorsList){p.second->Decorate(p.first,*mm,rm,this,qFont);} } // ...and take a snapshot @@ -378,7 +401,7 @@ void GLArea::paintGL() glBlendFunc(GL_ONE,GL_SRC_ALPHA); cs.lColor.V(3) = 128; // set half alpha value glColor(cs.lColor); - float h = -0.80f;//vcg::math::Min(-0.8f,-.08f*qFont.pointSize());//((.03f * currentHeight) - (currentHeight>>1)) / (float)currentHeight; + float h = -0.80f;//vcg::math::Min(-0.8f,-.08f*qFont.pointSize());//((.03f * curSiz.height()) - (curSiz.height()>>1)) / (float)curSiz.height(); glBegin(GL_TRIANGLE_STRIP); glVertex2f(-1.f,h); glVertex2f(-1.f,-1.f); @@ -393,16 +416,17 @@ void GLArea::paintGL() glColor4f(1,1,1,1); if(logVisible) { - renderText(20,currentHeight - 5 * (qFont.pointSizeF()+(currentHeight/225.f)),tr("LOG MESSAGES"),qFont); - log.glDraw(this,currLogLevel,3,qFont.pointSizeF()+(currentHeight/225.f),qFont); + renderText(20,curSiz.height() - 5 * (qFont.pointSizeF()+(curSiz.height()/225.f)),tr("LOG MESSAGES"),qFont); + log.glDraw(this,currLogLevel,3,qFont.pointSizeF()+(curSiz.height()/225.f),qFont); } // Second the MESH INFO (numVert,NumFaces,....) displayMeshInfo(); // Third the ENV INFO (Fps,ClippingPlanes,....) - currentTime=time.elapsed(); - deltaTime=currentTime-lastTime; + //int currentTime=time.elapsed(); + //deltaTime=currentTime-lastTime; + deltaTime=time.elapsed(); updateFps(); displayEnvInfo(); @@ -421,35 +445,35 @@ void GLArea::paintGL() void GLArea::displayMeshInfo() { - float fontSpacingV = qFont.pointSizeF()+(currentHeight/225.f); - float startPos= currentHeight-(fontSpacingV/3); + float fontSpacingV = qFont.pointSizeF()+(curSiz.height()/225.f); + float startPos= curSiz.height()-(fontSpacingV/3); - renderText(currentWidth*.5f,startPos-5*fontSpacingV,tr("MESH INFO"),qFont); + renderText(curSiz.width()*.5f,startPos-5*fontSpacingV,tr("MESH INFO"),qFont); - renderText(currentWidth*.5f,startPos-3*fontSpacingV,tr("Vertices: %1").arg(mm->cm.vert.size()),qFont); - renderText(currentWidth*.5f,startPos-2*fontSpacingV,tr("Faces: %1").arg(mm->cm.face.size()),qFont); - renderText(currentWidth*.5f,startPos- fontSpacingV,GetMeshInfoString(mm->mask),qFont); + renderText(curSiz.width()*.5f,startPos-3*fontSpacingV,tr("Vertices: %1").arg(mm->cm.vert.size()),qFont); + renderText(curSiz.width()*.5f,startPos-2*fontSpacingV,tr("Faces: %1").arg(mm->cm.face.size()),qFont); + renderText(curSiz.width()*.5f,startPos- fontSpacingV,GetMeshInfoString(mm->mask),qFont); } void GLArea::displayEnvInfo() { - float fontSpacingV = qFont.pointSizeF()+(currentHeight/225.f); - float startPos = currentHeight-(fontSpacingV/3); + float fontSpacingV = qFont.pointSizeF()+(curSiz.height()/225.f); + float startPos = curSiz.height()-(fontSpacingV/3); QString strNear=QString("Nplane: %1 ").arg(nearPlane,2,'f',1); QString strFar=QString("Fplane: %1").arg(farPlane,2,'f',1); - QString strViewer=QString("Viewer: %1 ").arg(objDist,2,'f',1); + //QString strViewer=QString("Viewer: %1 ").arg(objDist,2,'f',1); - renderText(currentWidth-currentWidth*.25f,startPos-5*fontSpacingV,tr("ENV INFO"),qFont); + renderText(curSiz.width()-curSiz.width()*.25f,startPos-5*fontSpacingV,tr("ENV INFO"),qFont); - renderText(currentWidth-currentWidth*.25f,startPos-3*fontSpacingV,strViewer+strNear+strFar,qFont); - renderText(currentWidth-currentWidth*.25f,startPos-2*fontSpacingV,QString("FOV: ")+QString::number((int)fov,10),qFont); + //renderText(curSiz.width()-curSiz.width()*.25f,startPos-3*fontSpacingV,strViewer+strNear+strFar,qFont); + renderText(curSiz.width()-curSiz.width()*.25f,startPos-2*fontSpacingV,QString("FOV: ")+QString::number((int)fov,10),qFont); if ((cfps>0) && (cfps<200)) { QString strInfo=QString("FPS: %1").arg(cfps,7,'f',1); - renderText(currentWidth-currentWidth*.25f,startPos-fontSpacingV,strInfo,qFont); + renderText(curSiz.width()-curSiz.width()*.25f,startPos-fontSpacingV,strInfo,qFont); } } @@ -462,8 +486,8 @@ void GLArea::resizeGL(int _width, int _height) //gluPerspective(fov, float(_width)/float(_height), nearPlane, farPlane); //glMatrixMode(GL_MODELVIEW); glViewport(0,0, _width, _height); - currentWidth=_width; - currentHeight=_height; + curSiz.setWidth(_width); + curSiz.setHeight(_height); // Set font size depending on window size (min = 1, max = 9) qFont.setPointSizeF(vcg::math::Clamp(-3 + sqrtf(_width*_width + _height*_height) * .01f,1,9)); @@ -486,8 +510,8 @@ void GLArea::displayHelp() glEnd(); - float fontSpacingV = (currentHeight*.01f)+3; - float hPosition = currentWidth*.1f; + float fontSpacingV = (curSiz.height()*.01f)+3; + float hPosition = curSiz.width()*.1f; glColor(Color4b::White); qFont.setBold(true);renderText(2+hPosition-(qFont.pointSize()*9),1.5*fontSpacingV,QString("MeshLab Quick Help"),qFont);qFont.setBold(false); renderText(2,3*fontSpacingV,QString("Drag:"),qFont); renderText(hPosition,3*fontSpacingV,QString("Rotate"),qFont); @@ -573,14 +597,17 @@ void GLArea::keyReleaseEvent ( QKeyEvent * e ) void GLArea::mousePressEvent(QMouseEvent*e) { e->accept(); - if ((e->modifiers() & Qt::ShiftModifier) && (e->modifiers() & Qt::ControlModifier) && - (e->button()==Qt::LeftButton) ) - activeDefaultTrackball=false; - else activeDefaultTrackball=true; - - if (isDefaultTrackBall()) - trackball.MouseDown(e->x(),height()-e->y(), QT2VCG(e->button(), e->modifiers() ) ); - else trackball_light.MouseDown(e->x(),height()-e->y(), QT2VCG(e->button(), Qt::NoModifier ) ); + if(iEdit) iEdit->mousePressEvent(currentEditor,e,*mm,this); + else { + if ((e->modifiers() & Qt::ShiftModifier) && (e->modifiers() & Qt::ControlModifier) && + (e->button()==Qt::LeftButton) ) + activeDefaultTrackball=false; + else activeDefaultTrackball=true; + + if (isDefaultTrackBall()) + trackball.MouseDown(e->x(),height()-e->y(), QT2VCG(e->button(), e->modifiers() ) ); + else trackball_light.MouseDown(e->x(),height()-e->y(), QT2VCG(e->button(), Qt::NoModifier ) ); + } update(); } @@ -588,17 +615,23 @@ void GLArea::mouseMoveEvent(QMouseEvent*e) { if(e->buttons() | Qt::LeftButton) { - if (isDefaultTrackBall()) trackball.MouseMove(e->x(),height()-e->y()); - else trackball_light.MouseMove(e->x(),height()-e->y()); - update(); + if(iEdit) iEdit->mouseMoveEvent(currentEditor,e,*mm,this); + else { + if (isDefaultTrackBall()) trackball.MouseMove(e->x(),height()-e->y()); + else trackball_light.MouseMove(e->x(),height()-e->y()); + update(); + } } } void GLArea::mouseReleaseEvent(QMouseEvent*e) { activeDefaultTrackball=true; - if (isDefaultTrackBall()) trackball.MouseUp(e->x(),height()-e->y(), QT2VCG(e->button(), e->modifiers() ) ); - else trackball_light.MouseUp(e->x(),height()-e->y(), QT2VCG(e->button(),e->modifiers()) ); + if(iEdit) iEdit->mouseReleaseEvent(currentEditor,e,*mm,this); + else { + if (isDefaultTrackBall()) trackball.MouseUp(e->x(),height()-e->y(), QT2VCG(e->button(), e->modifiers() ) ); + else trackball_light.MouseUp(e->x(),height()-e->y(), QT2VCG(e->button(),e->modifiers()) ); + } update(); } @@ -693,12 +726,16 @@ void GLArea::setLightMode(bool state,LightingModel lmode) void GLArea::setBackFaceCulling(bool enabled) { - if(enabled) glEnable(GL_CULL_FACE); - else glDisable(GL_CULL_FACE); rm.backFaceCull = enabled; updateGL(); } +void GLArea::setSelectionRendering(bool enabled) +{ + rm.selectedFaces = enabled; + updateGL(); +} + void GLArea::setLightModel() { static GLfloat standard_light[]={1.f,1.f,1.f,1.f}; @@ -739,13 +776,13 @@ void GLArea::setSnapshotSetting(const SnapshotSetting & s) void GLArea::setView() { - GLfloat fAspect = (GLfloat)currentWidth/ currentHeight; + GLfloat fAspect = (GLfloat)curSiz.width()/ curSiz.height(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Si deve mettere la camera ad una distanza che inquadri la sfera unitaria bene. float ratio = 1.75f; - objDist = ratio / tanf(vcg::math::ToRad(fov*.5f)); + float objDist = ratio / tanf(vcg::math::ToRad(fov*.5f)); nearPlane = objDist - 2.f*clipRatioNear; farPlane = objDist + 2.f*clipRatioFar; diff --git a/src/meshlab/glarea.h b/src/meshlab/glarea.h index bf66d80e2..88f53fec3 100644 --- a/src/meshlab/glarea.h +++ b/src/meshlab/glarea.h @@ -24,6 +24,10 @@ History $Log$ +Revision 1.57 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.56 2006/02/16 10:09:34 cignoni Removed unnecessary stuff (modifiers) @@ -63,43 +67,14 @@ Revision 1.51 2006/01/25 03:57:15 glvertex #include #include "GLLogStream.h" - #include "meshmodel.h" #include "interfaces.h" +#include "filterscript.h" #define SSHOT_BYTES_PER_PIXEL 4 enum LightingModel{LDOUBLE,LFANCY}; -class RenderMode -{ -public: - vcg::GLW::DrawMode drawMode; - vcg::GLW::ColorMode colorMode; - vcg::GLW::TextureMode textureMode; - - bool lighting; - bool backFaceCull; - bool doubleSideLighting; - bool fancyLighting; - bool castShadow; - vcg::Point3f lightDir; - - - RenderMode() - { - drawMode = GLW::DMSmooth; - colorMode = GLW::CMNone; - textureMode = GLW::TMNone; - - lighting = true; - backFaceCull = false; - doubleSideLighting = false; - fancyLighting = false; - castShadow = false; - } -}; - class ColorSetting { @@ -147,9 +122,9 @@ public: vcg::Trackball trackball_light; GLLogStream log; short currLogLevel; + FilterScript filterHistory; - int currentWidth; - int currentHeight; + QSize curSiz; QSize minimumSizeHint() const; QSize sizeHint() const; QFont getFont() {return qFont;} @@ -182,6 +157,7 @@ public: bool isDefaultTrackBall() {return activeDefaultTrackball;} void setBackFaceCulling(bool enabled); + void setSelectionRendering(bool enabled); void setCustomSetting(const ColorSetting & s); void setSnapshotSetting(const SnapshotSetting & s); void setDrawMode(vcg::GLW::DrawMode mode); @@ -198,7 +174,9 @@ public: void setRenderer(MeshRenderInterface *rend, QAction *shader){ iRenderer = rend; currentSharder = shader;} MeshRenderInterface * getRenderer() { return iRenderer; } - void setEdit(MeshEditInterface *edit){ iEdit = edit; } + void setEdit(MeshEditInterface *edit, QAction *editor){ iEdit = edit; currentEditor=editor;} + QAction * getEditAction() { return currentEditor; } + void endEdit(){ iEdit = 0; currentEditor=0;}/// void closeEvent(QCloseEvent *event); @@ -220,6 +198,7 @@ protected: void mouseDoubleClickEvent ( QMouseEvent * event ) ; void wheelEvent(QWheelEvent*e); + bool drawSelection; private: void pasteTile(); void myGluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); @@ -240,21 +219,21 @@ private: // Editing support MeshEditInterface *iEdit; + QAction *currentEditor; RenderMode rm; ColorSetting cs; - float cfps; float fov; - float objDist; + //float objDist; float clipRatioFar; float clipRatioNear; float nearPlane; float farPlane; - QTime time; - int deltaTime; - int lastTime; - int currentTime; +public: + int deltaTime; +private: float fpsVector[10]; + float cfps; QString fileName; diff --git a/src/meshlab/images/icons.xar b/src/meshlab/images/icons.xar index 659770d44346dcaecae61cc2a95fd3bbbdede667..8e520b42e5f079675a38056b94db18ed1ef240cc 100644 GIT binary patch delta 21759 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 CG!6U! delta 20098 zcmV)PK()WY-U8s!0u@+6QbD7m4GIDP001cf002W+PX0cT6)1n42><{`Nk%v~VSfM> z0QUd@|NsB@_V(xJ=hM^E;M>;B%*^la@4mji+}F|G>h<>X@bTZww6wID#q!0)#r669 ztgWrh#l6FUJLT{A#=f-P-rm2ptEHu-;qCd9m6Z4I=d7#&=<@vi`}z6k)SjN5?eqD> z-S+SH{Qdd(e0+a>?(OOH>D~C=!t~3Y!?~}Tyx)R@f&KOFwyU7qwTsNTq}9&H=jGSI ztCP*r)_kDFyxI2T-`mj2!(?Oz=(Bpx&H?D=;L@OL%;EK;qXECw>yek7XM~_`c7I4* zZ%|QDo1&-klmY)}XoZfKinH9z%u&{?fV{}muC6b(≥jji7u~ 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 +#include +#include +#include class QWidget; +class QGLWidget; class QIcon; class QString; +class QVariant; class QMouseEvent; -//class QList; -class QAction; class MeshModel; class RenderMode; class GLArea; @@ -210,19 +218,106 @@ public: QWidget *parent= 0)=0 ; // prima istanza il dialogo di opzioni viene sempre. }; + + +class FilterParameter +{ +public: + + FilterParameter(){} + + inline bool getBool(QString name) { + QMap::iterator ii=paramMap.find(name); + assert(ii!=paramMap.end()); + assert(ii.value().type()==QVariant::Bool); + return ii.value().toBool(); + } + + inline int getInt(QString name) { + QMap::iterator ii=paramMap.find(name); + if(ii==paramMap.end()) assert(0); + assert(ii.value().type()==QVariant::Int); + return float(ii.value().toInt()); + } + + inline float getFloat(QString name) { + QMap::iterator ii=paramMap.find(name); + if(ii==paramMap.end()) assert(0); + assert(ii.value().type()==QVariant::Double); + return float(ii.value().toDouble()); + } + + inline Matrix44f getMatrix44(QString name) { + QMap::iterator ii=paramMap.find(name); + if(ii==paramMap.end()) assert(0); + assert(ii.value().type()==QVariant::List); + Matrix44f matrix; + QList matrixVals = ii.value().toList(); + assert(matrixVals.size()==16); + for(int i=0;i<16;++i) + matrix.V()[i]=matrixVals[i].toDouble(); + + return matrix; + } + + inline void addFloat(QString name,float val){ paramMap.insert(name, QVariant( double(val)) ); } + inline void addInt (QString name,float val){ paramMap.insert(name, QVariant( int(val)) ); } + inline void addBool (QString name,bool val) { paramMap.insert(name, QVariant( val ) ); } + + inline void addMatrix44(QString name,Matrix44f val) { + QList matrixVals; + for(int i=0;i<16;++i) + matrixVals.append(val.V()[i]); + paramMap.insert(name, QVariant(matrixVals) ); + } + + inline void clear() { paramMap.clear(); } +private: + // The data is just a list of Parameters + QMap paramMap; +}; + class MeshFilterInterface { public: - enum FilterClass { Generic, Selection, Cleaning, Subdivision} ; - + typedef int FilterType; + enum FilterClass { Generic, Selection, Cleaning, Remeshing, FaceColoring, VertexColoring} ; virtual ~MeshFilterInterface() {} - virtual QList actions() const = 0; - virtual const ActionInfo &Info(QAction *)=0; + virtual const ActionInfo &Info(QAction *)=0; + + // The filterclass describe in which submenu each filter should be placed virtual const FilterClass getClass(QAction *) {return MeshFilterInterface::Generic;}; + + // This function invokes a dialog and get back the parameters + virtual bool getParameters(QAction *, QWidget * /*parent*/, MeshModel &/*m*/, FilterParameter & /*par*/) {return true;}; + + // The filters can require some additional + virtual const int getRequirements(QAction *){return MeshModel::MM_NONE;} + + // The main function that apply the selected filter + virtual bool applyFilter(QAction * /*filter*/, MeshModel &/*m*/, FilterParameter & /*parent*/, vcg::CallBackPos * /*cb*/) = 0; + virtual const PluginInfo &Info()=0; - virtual void setLog(GLLogStream* )=0; - virtual bool applyFilter(QAction * /*filter*/, MeshModel &/*m*/, QWidget * /*parent*/, vcg::CallBackPos * /*cb*/) = 0; + void setLog(GLLogStream *log) { this->log = log ; } + + virtual const QString ST(FilterType filter)=0; + + virtual const FilterType ID(QAction *a) + { + foreach( FilterType tt, types()) + if( a->text() == this->ST(tt) ) return tt; + assert(0); + return 0; + } + virtual QList actions() const { return actionList;} + virtual QList &types() { return typeList;} + +protected: + QList actionList; + QList typeList; + GLLogStream *log; }; + /* Serve per customizzare totalmente il processo di rendering Viene invocata al posto del rendering standard della mesh. @@ -237,27 +332,14 @@ class MeshRenderInterface public: virtual ~MeshRenderInterface() {} - virtual void Init(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/){}; - virtual void Render(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/) = 0; + virtual void Init(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, QGLWidget * /*parent*/){}; + virtual void Render(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, QGLWidget * /*parent*/) = 0; virtual void Finalize(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; virtual bool isSupported() = 0; virtual const PluginInfo &Info()=0; virtual QList actions() const = 0; }; -class MeshColorizeInterface -{ -public: - virtual const ActionInfo &Info(QAction *)=0; - virtual const PluginInfo &Info()=0; - virtual void setLog(GLLogStream* )=0; - virtual void Compute(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/){}; - virtual void Show(QAction * /*mode*/, bool /*show*/, MeshModel &/*m*/, GLArea * /*parent*/) {}; - virtual void Finalize(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; - virtual QList actions() const = 0; -}; - - class MeshDecorateInterface { @@ -268,7 +350,7 @@ public: virtual const PluginInfo &Info()=0; virtual void Init(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; - virtual void Decorate(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/) = 0; + virtual void Decorate(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, QGLWidget * /*parent*/,QFont qf) = 0; virtual void Finalize(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; virtual QList actions() const = 0; }; @@ -283,9 +365,9 @@ public: virtual const PluginInfo &Info()=0; virtual void StartEdit(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; - virtual void Edit(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/) = 0; + //virtual void Edit(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/) = 0; virtual void EndEdit(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; - virtual void Decorate(QAction * /*mode*/, MeshModel &/*m*/, RenderMode &/*rm*/, GLArea * /*parent*/) = 0; + virtual void Decorate(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/) = 0; virtual void mousePressEvent (QAction *, QMouseEvent *event, MeshModel &/*m*/, GLArea * )=0; virtual void mouseMoveEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * )=0; virtual void mouseReleaseEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * )=0; @@ -296,7 +378,6 @@ public: Q_DECLARE_INTERFACE(MeshIOInterface, "vcg.meshlab.MeshIOInterface/1.0") Q_DECLARE_INTERFACE(MeshFilterInterface, "vcg.meshlab.MeshFilterInterface/1.0") Q_DECLARE_INTERFACE(MeshRenderInterface, "vcg.meshlab.MeshRenderInterface/1.0") -Q_DECLARE_INTERFACE(MeshColorizeInterface, "vcg.meshlab.MeshColorizeInterface/1.0") Q_DECLARE_INTERFACE(MeshDecorateInterface, "vcg.meshlab.MeshDecorateInterface/1.0") Q_DECLARE_INTERFACE(MeshEditInterface, "vcg.meshlab.MeshEditInterface/1.0") diff --git a/src/meshlab/mainwindow.h b/src/meshlab/mainwindow.h index 23635412e..43973cb41 100644 --- a/src/meshlab/mainwindow.h +++ b/src/meshlab/mainwindow.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.63 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.62 2006/04/12 15:12:18 cignoni Added Filter classes (cleaning, meshing etc) @@ -77,7 +81,7 @@ class MainWindow : public QMainWindow public: MainWindow(); static bool QCallBack(const int pos, const char * str); - const QString appName() const {return tr("MeshLab v0.6"); } + const QString appName() const {return tr("MeshLab v0.7"); } // MaskObj maskobj; public slots: @@ -87,13 +91,19 @@ public slots: private slots: //////////// Slot Menu File ////////////////////// + void openFilterScript(QString fileName=QString()); + void saveFilterScript(QString fileName=QString()); void reload(); void openRecentFile(); bool saveAs(); - bool saveSnapshot(); + bool saveSnapshot(); + ///////////Slot Menu Edit //////////////////////// + void applyEditMode(); + void endEditMode(); ///////////Slot Menu Filter //////////////////////// void applyFilter(); void applyLastFilter(); + void runFilterScript(); /////////// Slot Menu Render ///////////////////// void renderBbox(); void renderPoint(); @@ -109,8 +119,8 @@ private slots: void setColorMode(QAction *qa); void applyRenderMode(); void applyColorMode(); - void applyEditMode(); void toggleBackFaceCulling(); + void toggleSelectionRendering(); void applyDecorateMode(); ///////////Slot Menu View //////////////////////// void fullScreen(); @@ -153,16 +163,19 @@ private: QStringList pluginFileNames; std::vector meshIOPlugins; QByteArray toolbarState; //stato delle toolbar e dockwidgets + QMap filterMap; // a map to retrieve an action from a name. Used for playing filter scripts. //////// ToolBars /////////////// QToolBar *mainToolBar; QToolBar *renderToolBar; + QToolBar *editToolBar; ///////// Menus /////////////// QMenu *fileMenu; QMenu *filterMenu; QMenu *filterMenuSelect; QMenu *filterMenuClean; + QMenu *filterMenuRemeshing; QMenu *editMenu; //Render Menu and SubMenu //// QMenu *shadersMenu; @@ -183,14 +196,19 @@ private: //////////// Actions Menu File /////////////////////// QAction *openAct; + QAction *openFilterScriptAct; + QAction *saveFilterScriptAct; QAction *closeAct; QAction *reloadAct; QAction *saveAsAct; QAction *saveSnapshotAct; QAction *lastFilterAct; + QAction *runFilterScriptAct; QAction *recentFileActs[MAXRECENTFILES]; QAction *separatorAct; QAction *exitAct; + /////////// Actions Menu Edit ///////////////////// + QAction *endEditModeAct; /////////// Actions Menu Render ///////////////////// QActionGroup *renderModeGroupAct; QAction *renderBboxAct; @@ -205,6 +223,7 @@ private: QAction *setFancyLightingAct; QAction *setLightAct; QAction *backFaceCullAct; + QAction *setSelectionRenderingAct; QActionGroup *colorModeGroupAct; QAction *colorModeNoneAct; diff --git a/src/meshlab/mainwindow_Init.cpp b/src/meshlab/mainwindow_Init.cpp index a06285c3a..16ff4473b 100644 --- a/src/meshlab/mainwindow_Init.cpp +++ b/src/meshlab/mainwindow_Init.cpp @@ -24,6 +24,10 @@ History $Log$ +Revision 1.53 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.52 2006/04/18 06:57:34 zifnab1974 syntax errors for gcc 3.4.5 resolved @@ -104,7 +108,17 @@ void MainWindow::createActions() openAct->setShortcut(Qt::CTRL+Qt::Key_O); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - closeAct = new QAction(tr("&Close"), this); + openFilterScriptAct = new QAction(QIcon(":/images/open.png"),tr("&Open Filter Script..."), this); + openFilterScriptAct->setShortcutContext(Qt::ApplicationShortcut); + openFilterScriptAct->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_O); + connect(openFilterScriptAct, SIGNAL(triggered()), this, SLOT(openFilterScript())); + + saveFilterScriptAct = new QAction(QIcon(":/images/save.png"),tr("&Save Filter Script..."), this); + saveFilterScriptAct->setShortcutContext(Qt::ApplicationShortcut); + saveFilterScriptAct->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_S); + connect(saveFilterScriptAct, SIGNAL(triggered()), this, SLOT(saveFilterScript())); + + closeAct = new QAction(tr("&Close"), this); closeAct->setShortcutContext(Qt::ApplicationShortcut); closeAct->setShortcut(Qt::CTRL+Qt::Key_C); connect(closeAct, SIGNAL(triggered()),workspace, SLOT(closeActiveWindow())); @@ -191,7 +205,13 @@ void MainWindow::createActions() backFaceCullAct->setShortcutContext(Qt::ApplicationShortcut); backFaceCullAct->setShortcut(Qt::CTRL+Qt::Key_K); connect(backFaceCullAct, SIGNAL(triggered()), this, SLOT(toggleBackFaceCulling())); - + + setSelectionRenderingAct = new QAction(QIcon(":/images/selected.png"),tr("Selected Face Rendering"),this); + setSelectionRenderingAct->setCheckable(true); + setSelectionRenderingAct->setShortcutContext(Qt::ApplicationShortcut); + setSelectionRenderingAct->setShortcut(Qt::CTRL+Qt::Key_S); + connect(setSelectionRenderingAct, SIGNAL(triggered()), this, SLOT(toggleSelectionRendering())); + //////////////Action Menu View //////////////////////////////////////////////////////////////////////////// fullScreenAct = new QAction (tr("&FullScreen"), this); fullScreenAct->setCheckable(true); @@ -226,6 +246,12 @@ void MainWindow::createActions() resetTrackBallAct->setShortcut(Qt::CTRL+Qt::Key_H); connect(resetTrackBallAct, SIGNAL(triggered()), this, SLOT(resetTrackBall())); + endEditModeAct = new QAction (QIcon(":/images/no_edit.png"),tr("Not editing"), this); + endEditModeAct->setShortcut(Qt::Key_Escape); + endEditModeAct->setCheckable(true); + endEditModeAct->setChecked(true); + connect(endEditModeAct, SIGNAL(triggered()), this, SLOT(endEditMode())); + //////////////Action Menu Windows ///////////////////////////////////////////////////////////////////////// windowsTileAct = new QAction(tr("&Tile"), this); connect(windowsTileAct, SIGNAL(triggered()), workspace, SLOT(tile())); @@ -246,6 +272,10 @@ void MainWindow::createActions() lastFilterAct->setEnabled(false); connect(lastFilterAct, SIGNAL(triggered()), this, SLOT(applyLastFilter())); + runFilterScriptAct = new QAction(tr("Run current filter script"),this); + runFilterScriptAct->setEnabled(false); + connect(runFilterScriptAct, SIGNAL(triggered()), this, SLOT(runFilterScript())); + //////////////Action Menu Preferences ///////////////////////////////////////////////////////////////////// setCustomizeAct = new QAction(tr("&Options..."),this); connect(setCustomizeAct, SIGNAL(triggered()), this, SLOT(setCustomize())); @@ -268,10 +298,15 @@ void MainWindow::createToolBars() mainToolBar->addAction(saveSnapshotAct); renderToolBar = addToolBar(tr("Render")); - renderToolBar->setIconSize(QSize(32,32)); + //renderToolBar->setIconSize(QSize(32,32)); renderToolBar->addActions(renderModeGroupAct->actions()); renderToolBar->addAction(renderModeTextureAct); renderToolBar->addAction(setLightAct); + renderToolBar->addAction(setSelectionRenderingAct); + + editToolBar = addToolBar(tr("Edit")); + editToolBar->addAction(endEditModeAct); + } @@ -283,6 +318,8 @@ void MainWindow::createMenus() fileMenu->addAction(closeAct); fileMenu->addAction(reloadAct); fileMenu->addAction(saveAsAct); + fileMenu->addAction(openFilterScriptAct); + fileMenu->addAction(saveFilterScriptAct); fileMenu->addSeparator(); @@ -296,13 +333,16 @@ void MainWindow::createMenus() //////////////////// Menu Edit ////////////////////////////////////////////////////////////////////////// editMenu = menuBar()->addMenu(tr("&Edit")); + editMenu->addAction(endEditModeAct); //////////////////// Menu Filter ////////////////////////////////////////////////////////////////////////// filterMenu = menuBar()->addMenu(tr("Fi<ers")); filterMenu->addAction(lastFilterAct); filterMenu->addSeparator(); filterMenuSelect = filterMenu->addMenu(tr("Select")); - filterMenuClean = filterMenu->addMenu(tr("Clean")); + filterMenuClean = filterMenu->addMenu(tr("Clean")); + filterMenuRemeshing = filterMenu->addMenu(tr("Remeshing")); + //////////////////// Menu Render ////////////////////////////////////////////////////////////////////////// @@ -312,6 +352,7 @@ void MainWindow::createMenus() renderModeMenu->addAction(backFaceCullAct); renderModeMenu->addActions(renderModeGroupAct->actions()); renderModeMenu->addAction(renderModeTextureAct); + renderModeMenu->addAction(setSelectionRenderingAct); lightingModeMenu=renderMenu->addMenu(tr("&Lighting")); lightingModeMenu->addAction(setLightAct); @@ -397,23 +438,27 @@ void MainWindow::loadPlugins() QObject *plugin = loader.instance(); if (plugin) { - MeshColorizeInterface *iColor = qobject_cast(plugin); - if (iColor) - addToMenu(iColor->actions(), colorModeMenu, SLOT(applyColorMode())); - + //MeshColorizeInterface *iColor = qobject_cast(plugin); + MeshFilterInterface *iFilter = qobject_cast(plugin); if (iFilter) { QAction *filterAction; foreach(filterAction, iFilter->actions()) { + filterMap[filterAction->text()]=filterAction; connect(filterAction,SIGNAL(triggered()),this,SLOT(applyFilter())); - switch(iFilter->getClass(filterAction)) + switch(iFilter->getClass(filterAction)) { + case MeshFilterInterface::FaceColoring : + case MeshFilterInterface::VertexColoring : + colorModeMenu->addAction(filterAction); break; case MeshFilterInterface::Selection : filterMenuSelect->addAction(filterAction); break; case MeshFilterInterface::Cleaning : filterMenuClean->addAction(filterAction); break; + case MeshFilterInterface::Remeshing : + filterMenuRemeshing->addAction(filterAction); break; case MeshFilterInterface::Generic : default: filterMenu->addAction(filterAction); break; @@ -435,9 +480,15 @@ void MainWindow::loadPlugins() addToMenu(iRender->actions(), shadersMenu, SLOT(applyRenderMode())); MeshEditInterface *iEdit = qobject_cast(plugin); + QAction *editAction; if (iEdit) - addToMenu(iEdit->actions(), editMenu, SLOT(applyEditMode())); - + foreach(editAction, iEdit->actions()) + { + editMenu->addAction(editAction); + if(!editAction->icon().isNull()) + editToolBar->addAction(editAction); + connect(editAction,SIGNAL(triggered()),this,SLOT(applyEditMode())); + } pluginFileNames += fileName; } } diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 30fec2578..a858131c4 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -24,6 +24,10 @@ History $Log$ +Revision 1.94 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.93 2006/03/07 10:47:50 cignoni Better mask management during io @@ -144,34 +148,20 @@ void MainWindow::updateMenus() if(active){ const RenderMode &rm=GLA()->getCurrentRenderMode(); switch (rm.drawMode) { - case GLW::DMBox: - renderBboxAct->setChecked(true); - break; - case GLW::DMPoints: - renderModePointsAct->setChecked(true); - break; - case GLW::DMWire: - renderModeWireAct->setChecked(true); - break; - case GLW::DMFlat: - renderModeFlatAct->setChecked(true); - break; - case GLW::DMSmooth: - renderModeSmoothAct->setChecked(true); - break; - case GLW::DMFlatWire: - renderModeFlatLinesAct->setChecked(true); - break; - case GLW::DMHidden: - renderModeHiddenLinesAct->setChecked(true); - break; + case GLW::DMBox: renderBboxAct->setChecked(true); break; + case GLW::DMPoints: renderModePointsAct->setChecked(true); break; + case GLW::DMWire: renderModeWireAct->setChecked(true); break; + case GLW::DMFlat: renderModeFlatAct->setChecked(true); break; + case GLW::DMSmooth: renderModeSmoothAct->setChecked(true); break; + case GLW::DMFlatWire: renderModeFlatLinesAct->setChecked(true); break; + case GLW::DMHidden: renderModeHiddenLinesAct->setChecked(true); break; } switch (rm.colorMode) { - case GLW::CMNone: colorModeNoneAct->setChecked(true); break; - case GLW::CMPerVert: colorModePerVertexAct->setChecked(true); break; - case GLW::CMPerFace: colorModePerFaceAct->setChecked(true); break; + case GLW::CMNone: colorModeNoneAct->setChecked(true); break; + case GLW::CMPerVert: colorModePerVertexAct->setChecked(true); break; + case GLW::CMPerFace: colorModePerFaceAct->setChecked(true); break; } lastFilterAct->setEnabled(false); @@ -186,6 +176,13 @@ void MainWindow::updateMenus() } + if(GLA()->getEditAction()) + { + endEditModeAct->setChecked(false); + GLA()->getEditAction()->setChecked(true); + } + else endEditModeAct->setChecked(true); + showLogAct->setChecked(GLA()->isLogVisible()); showInfoPaneAct->setChecked(GLA()->isInfoAreaVisible()); showTrackBallAct->setChecked(GLA()->isTrackBallVisible()); @@ -198,6 +195,7 @@ void MainWindow::updateMenus() setFancyLightingAct->setChecked(rm.fancyLighting); setDoubleLightingAct->setChecked(rm.doubleSideLighting); + setSelectionRenderingAct->setChecked(rm.selectedFaces); foreach (QAction *a,TotalDecoratorsList){a->setChecked(false);} if(GLA()->iDecoratorsList){ @@ -209,34 +207,86 @@ void MainWindow::updateMenus() void MainWindow::applyLastFilter() { - GLA()->getLastAppliedFilter()->activate(QAction::Trigger); + GLA()->getLastAppliedFilter()->activate(QAction::Trigger); } +void MainWindow::runFilterScript() +{ + FilterScript::iterator ii; + for(ii= GLA()->filterHistory.actionList.begin();ii!= GLA()->filterHistory.actionList.end();++ii) + { + MeshFilterInterface *iFilter = qobject_cast( (*ii).first->parent()); + iFilter->applyFilter( (*ii).first, *(GLA()->mm), (*ii).second, QCallBack ); + GLA()->log.Log(GLLogStream::Info,"Re-Applied filter %s",qPrintable((*ii).first->text())); + } +} +// ///////////////////////////////////////////////// +// The Very Important Procedure of applying a filter +// ///////////////////////////////////////////////// + void MainWindow::applyFilter() { QAction *action = qobject_cast(sender()); MeshFilterInterface *iFilter = qobject_cast(action->parent()); - qb->show(); - iFilter->setLog(&(GLA()->log)); + + // (1) Ask for filter requirements (eg a filter can need topology, border flags etc) + // and statisfy them + int req=iFilter->getRequirements(action); + GLA()->mm->updateDataMask(req); + + + // (2) Ask for filter parameters (e.g. user defined threshold that could require a widget) + FilterParameter par; + iFilter->getParameters(action, GLA(),*(GLA()->mm), par); + - // Log if filter applied succesfully - if(iFilter->applyFilter(action,*(GLA()->mm ),GLA(),QCallBack)) + // (3) save the current filter and its parameters in the history + GLA()->filterHistory.actionList.append(qMakePair(action,par)); + + qDebug("Filter History size %i",GLA()->filterHistory.actionList.size()); + qDebug("Filter History Last entry %s",qPrintable (GLA()->filterHistory.actionList.front().first->text())); + + qb->show(); + iFilter->setLog(&(GLA()->log)); + // (4) Apply the Filter + bool ret=iFilter->applyFilter(action, *(GLA()->mm), par, QCallBack); + + // (5) Apply post filter actions (e.g. recompute non updated stuff if needed) + + if(ret) { - GLA()->log.Log(GLLogStream::Info,"Applied filter %s",action->text().toLocal8Bit().constData()); + GLA()->log.Log(GLLogStream::Info,"Applied filter %s",qPrintable(action->text())); GLA()->setWindowModified(true); GLA()->setLastAppliedFilter(action); lastFilterAct->setText(QString("Apply filter ") + action->text()); lastFilterAct->setEnabled(true); } - qb->reset(); -} + // at the end for filters that change the color set the appropriate color mode + if(iFilter->getClass(action)==MeshFilterInterface::FaceColoring ) + GLA()->setColorMode(vcg::GLW::CMPerFace); + if(iFilter->getClass(action)==MeshFilterInterface::VertexColoring ) + GLA()->setColorMode(vcg::GLW::CMPerVert); + if(iFilter->getClass(action)==MeshFilterInterface::Selection ) + GLA()->setSelectionRendering(true); + + qb->reset(); + updateMenus(); +} +void MainWindow::endEditMode() +{ + GLA()->getEditAction()->setChecked(false); + GLA()->endEdit(); +} void MainWindow::applyEditMode() { QAction *action = qobject_cast(sender()); MeshEditInterface *iEdit = qobject_cast(action->parent()); - GLA()->setEdit(iEdit); - GLA()->log.Log(GLLogStream::Info,"Started Mode %s",action->text().toLocal8Bit().constData()); + GLA()->setEdit(iEdit,action); + iEdit->StartEdit(action,*(GLA()->mm),GLA()); + GLA()->log.Log(GLLogStream::Info,"Started Mode %s",qPrintable (action->text())); + GLA()->setSelectionRendering(true); + updateMenus(); } void MainWindow::applyRenderMode() @@ -255,7 +305,7 @@ void MainWindow::applyRenderMode() if(iRenderTemp->isSupported()) { GLA()->setRenderer(iRenderTemp,action); - GLA()->log.Log(GLLogStream::Info,"%s",action->text().toLocal8Bit().constData()); // Prints out action name + GLA()->log.Log(GLLogStream::Info,"%s",qPrintable(action->text())); // Prints out action name } else { @@ -269,9 +319,9 @@ void MainWindow::applyRenderMode() void MainWindow::applyColorMode() { QAction *action = qobject_cast(sender()); - MeshColorizeInterface *iColorTemp = qobject_cast(action->parent()); + MeshFilterInterface *iColorTemp = qobject_cast(action->parent()); iColorTemp->setLog(&(GLA()->log)); - iColorTemp->Compute(action,*(GLA()->mm ),GLA()->getCurrentRenderMode(), GLA()); + //iColorTemp->Compute(action,*(GLA()->mm ),GLA()->getCurrentRenderMode(), GLA()); GLA()->log.Log(GLLogStream::Info,"Applied colorize %s",action->text().toLocal8Bit().constData()); updateMenus(); } @@ -338,10 +388,15 @@ void MainWindow::setFancyLighting() void MainWindow::toggleBackFaceCulling() { RenderMode &rm = GLA()->getCurrentRenderMode(); - GLA()->setBackFaceCulling(!rm.backFaceCull); } +void MainWindow::toggleSelectionRendering() +{ + RenderMode &rm = GLA()->getCurrentRenderMode(); + GLA()->setSelectionRendering(!rm.selectedFaces); +} + enum TypeIO{IMPORT,EXPORT}; void MainWindow::LoadKnownFilters(QStringList &filters, QHash &allKnownFormats, int type) @@ -392,6 +447,26 @@ void MainWindow::LoadKnownFilters(QStringList &filters, QHash &all filters.push_front(allKnownFormatsFilter); } +void MainWindow::openFilterScript(QString fileName) +{ + if (fileName.isEmpty()) + fileName = QFileDialog::getOpenFileName(this,tr("Open Filter Script File"),".", "*.mls"); + + if (fileName.isEmpty()) return; + + +} + +void MainWindow::saveFilterScript(QString fileName) +{ + if (fileName.isEmpty()) + fileName = QFileDialog::getOpenFileName(this,tr("Open Filter Script File"),".", "*.mls"); + + if (fileName.isEmpty()) return; +} + + + void MainWindow::open(QString fileName) { // Opening files in a transparent form (IO plugins contribution is hidden to user) @@ -434,24 +509,22 @@ void MainWindow::open(QString fileName) gla=new GLArea(workspace); gla->mm=mm; gla->mm->mask = mask; // store mask into model structure - + gla->setFileName(fileName); gla->setWindowTitle(QFileInfo(fileName).fileName()+tr("[*]")); gla->showInfoArea(true); workspace->addWindow(gla); if(workspace->isVisible()) gla->showMaximized(); setCurrentFile(fileName); + + if( mask & vcg::tri::io::Mask::IOM_FACECOLOR) + gla->setColorMode(GLW::CMPerFace); if( mask & vcg::tri::io::Mask::IOM_VERTCOLOR) { gla->mm->storeVertexColor(); gla->setColorMode(GLW::CMPerVert); } - else if( mask & vcg::tri::io::Mask::IOM_FACECOLOR) - gla->setColorMode(GLW::CMPerFace); - else - gla->setColorMode(GLW::CMNone); - updateMenus(); - renderModeTextureAct->setChecked(false); + renderModeTextureAct->setChecked(false); renderModeTextureAct->setEnabled(false); if(!GLA()->mm->cm.textures.empty()) { @@ -460,10 +533,10 @@ void MainWindow::open(QString fileName) GLA()->setTextureMode(GLW::TMPerWedgeMulti); } vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(mm->cm); + updateMenus(); } - //qb->hide(); - qb->reset(); + qb->reset(); } void MainWindow::openRecentFile() diff --git a/src/meshlab/meshlab.pro b/src/meshlab/meshlab.pro index 4740853c9..dcb73db86 100644 --- a/src/meshlab/meshlab.pro +++ b/src/meshlab/meshlab.pro @@ -15,14 +15,13 @@ SOURCES = main.cpp \ mainwindow_Init.cpp \ mainwindow_RunTime.cpp\ meshmodel.cpp \ - GLLogStream.cpp \ + GLLogStream.cpp \ glarea.cpp \ plugindialog.cpp \ customDialog.cpp \ saveSnapshotDialog.cpp \ savemaskexporter.cpp \ changetexturename.cpp \ - ../../../sf/wrap/ply/plylib.cpp\ ../../../sf/wrap/gui/trackball.cpp\ ../../../sf/wrap/gui/trackmode.cpp \ ../../../code/lib/glew/src/glew.c diff --git a/src/meshlab/meshlab.qrc b/src/meshlab/meshlab.qrc index 5fef16eab..bdefd2c6c 100644 --- a/src/meshlab/meshlab.qrc +++ b/src/meshlab/meshlab.qrc @@ -1,21 +1,23 @@ - images/bbox.png images/backlines.png + images/bbox.png + images/eye_256_splash.png images/flat.png images/flatlines.png images/info.png images/lightoff.png images/lighton.png + images/logo.png images/open.png images/points.png + images/reload.png images/save.png + images/selected.png images/smooth.png - images/wire.png + images/snapshot.png images/textures.png - images/logo.png - images/eye_256_splash.png - images/snapshot.png - images/reload.png + images/wire.png + images/no_edit.png diff --git a/src/meshlab/meshmodel.cpp b/src/meshlab/meshmodel.cpp index 51389a8a6..ce7c9a170 100644 --- a/src/meshlab/meshmodel.cpp +++ b/src/meshlab/meshmodel.cpp @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.23 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.22 2006/01/17 23:45:12 cignoni Removed useless open function @@ -32,45 +36,6 @@ update ply::PlyMask -> io::Mask Revision 1.20 2005/12/22 21:05:43 cignoni Removed Optional Face Normal and added some initalization after opening -Revision 1.19 2005/12/09 18:16:12 fmazzant -added generic obj save with plugin arch. - -Revision 1.18 2005/12/09 00:26:25 buzzelli -io importing mechanism adapted in order to be fully transparent towards the user - -Revision 1.17 2005/12/07 00:56:40 fmazzant -added support for exporter generic obj file (level base) - -Revision 1.16 2005/12/06 16:27:43 fmazzant -added obj file in generic open dialog - -Revision 1.15 2005/12/04 00:22:46 cignoni -Switched from progresBar widget to progressbar dialog - -Revision 1.14 2005/12/02 00:54:13 cignoni -Added TextureMode in render - -Revision 1.13 2005/11/25 11:55:59 alemochi -Added function to Enable/Disable lighting (work in progress) - -Revision 1.12 2005/11/24 01:45:28 cignoni -commented line 62. dangerous unuseful debug line. - -Revision 1.11 2005/11/24 01:38:36 cignoni -Added new plugins intefaces, tested with shownormal render mode - -Revision 1.10 2005/11/23 00:04:03 cignoni -added hint for better hiddenline - -Revision 1.9 2005/11/22 11:40:14 glvertex -Now using a single method to compute normals (PerVertePerFace instead PerVertex then PerFace) - -Revision 1.8 2005/11/21 22:09:35 cignoni -added missing enablenormal - -Revision 1.7 2005/11/21 12:12:54 cignoni -Added copyright info - ****************************************************************************/ #include "meshmodel.h" @@ -79,8 +44,32 @@ Added copyright info bool MeshModel::Render(GLW::DrawMode dm, GLW::ColorMode cm, GLW::TextureMode tm) { - glw.SetHintParamf(GLW::HNPZTwist,0.0005f); // glColor3f(.8f,.8f,.8f); glw.Draw(dm,cm,tm); return true; } + +bool MeshModel::RenderSelectedFaces() +{ + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT ); + glEnable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glDepthMask(GL_FALSE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ; + glColor4f(1.0f,0.0,0.0,.3f); + glPolygonOffset(-1.0, -1); + CMeshO::FaceIterator fi; + glBegin(GL_TRIANGLES); + for(fi=cm.face.begin();fi!=cm.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS()) + { + glVertex((*fi).cP(0)); + glVertex((*fi).cP(1)); + glVertex((*fi).cP(2)); + } + glEnd(); + glPopAttrib(); + return true; +} diff --git a/src/meshlab/meshmodel.h b/src/meshlab/meshmodel.h index 64f1b536a..f7536d34e 100644 --- a/src/meshlab/meshmodel.h +++ b/src/meshlab/meshmodel.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.21 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.20 2006/02/13 14:20:13 cignoni Removed glew.h @@ -53,6 +57,8 @@ Made FFAdj optional and added store and restore color functions #include #include #include +#include +#include #include #include @@ -65,13 +71,29 @@ class CEdge; // dummy prototype never used class CFaceO; class CVertexO; -// Opt stuff +//Vert Mem Occupancy --- 36 --- -class CVertexO : public VertexSimp2< CVertexO, CEdge, CFaceO, vert::Coord3f, vert::Color4b, vert::Normal3f, vert::Qualityf, vert::BitFlags >{ -public: - Color4b origC; +class CVertexO : public VertexSimp2< CVertexO, CEdge, CFaceO, + vert::Coord3f, /* 12b */ + vert::BitFlags, /* 4b */ + vert::Normal3f, /* 12b */ + vert::Qualityf, /* 4b */ + vert::Color4b /* 4b */ + >{ }; -class CFaceO : public FaceSimp2< CVertexO, CEdge, CFaceO, face::InfoOcf, face::Color4b, face::FFAdjOcf, face::WedgeTexturefOcf, face::VertexRef, face::BitFlags, face::Normal3f, face::Mark > {}; + +//Face Mem Occupancy --- 32 --- + +class CFaceO : public FaceSimp2< CVertexO, CEdge, CFaceO, + face::InfoOcf, /* 4b */ + face::VertexRef, /*12b */ + face::BitFlags, /* 4b */ + face::Normal3f, /*12b */ + face::MarkOcf, /* 0b */ + face::Color4bOcf, /* 0b */ + face::FFAdjOcf, /* 0b */ + face::WedgeTexturefOcf /* 0b */ + > {}; class CMeshO : public vcg::tri::TriMesh< vector, face::vector_ocf > {}; /* @@ -85,27 +107,129 @@ class MeshModel : public tri::io::Mask { public: + enum FilterReq { MM_NONE = 0x0000, + MM_BORDERFLAG = 0x0001, + MM_FACETOPO = 0x0002, + MM_WEDGTEXCOORD = 0x0004, + MM_FACECOLOR = 0x0008, + MM_FACEMARK = 0x0010, + MM_ALL = 0xffff} ; + + CMeshO cm; GlTrimesh glw; + vector originalVertexColor; + + // Bitmask denoting what fields are currently kept updated in mesh + int currentDataMask; + // Bitmask denoting what fields are loaded/saved + int mask; + MeshModel() { +// size_t faceSize=sizeof(CFaceO); +// size_t vertSize=sizeof(CVertexO); + glw.m=&cm; - cm.face.EnableWedgeTex(); - cm.face.EnableFFAdjacency(); + currentDataMask=MM_NONE; mask= IOM_VERTCOORD | IOM_FACEINDEX | IOM_FLAGS; } bool Render(GLW::DrawMode dm, GLW::ColorMode cm, GLW::TextureMode tm); + bool RenderSelectedFaces(); + inline void storeVertexColor() { + originalVertexColor.resize(cm.vert.size()); + vector::iterator ci; CMeshO::VertexIterator vi; - for(vi=cm.vert.begin();vi!=cm.vert.end();++vi) (*vi).origC=(*vi).C(); + for(vi=cm.vert.begin(),ci=originalVertexColor.begin();vi!=cm.vert.end();++vi,++ci) + (*ci)=(*vi).C(); } inline void restoreVertexColor() { + vector::iterator ci; CMeshO::VertexIterator vi; - for(vi=cm.vert.begin();vi!=cm.vert.end();++vi) (*vi).C()=(*vi).origC; + for(vi=cm.vert.begin(),ci=originalVertexColor.begin();vi!=cm.vert.end();++vi,++ci) + (*vi).C()=(*ci); } - int mask; +// FUNZIONE equivalente alla updatedatamask ma solo che prende in ingresso mask da filetype. + void Enable(int openingFileMask) + { + if( openingFileMask & IOM_WEDGTEXCOORD ) updateDataMask(MM_WEDGTEXCOORD); + if( openingFileMask & IOM_FACECOLOR ) updateDataMask(MM_FACECOLOR); + } + + // Ogni filtro dichiara + // 1) di che cosa ha bisogno + // 2) che cosa sa aggiornare (di solito quello di cui ha bisogno) + // 3) quello che ha cambiato (vertici topologia colore) + + // il framework si preoccupa + // 1) prima di invocare il filtro di preparare quel che serve + // 2) dopo il filtro di aggiornare quello che non ha aggiornato il filtro + + // Enable optional fields that could be needed + void updateDataMask(int neededDataMask) + { + if( ( (neededDataMask & MM_FACETOPO)!=0) && (currentDataMask& MM_FACETOPO)==0) + { + cm.face.EnableFFAdjacency(); + currentDataMask |= MM_FACETOPO; + tri::UpdateTopology::FaceFace(cm); + } + if( ( (neededDataMask & MM_BORDERFLAG)!=0) && (currentDataMask& MM_BORDERFLAG)==0) + { + if(currentDataMask& MM_FACETOPO) tri::UpdateFlags::FaceBorderFromFF(cm); + else tri::UpdateFlags::FaceBorderFromNone(cm); + currentDataMask |= MM_BORDERFLAG; + } + if( ( (neededDataMask & MM_WEDGTEXCOORD)!=0) && (currentDataMask& MM_WEDGTEXCOORD)==0) + { + cm.face.EnableWedgeTex(); + currentDataMask |= MM_WEDGTEXCOORD; + } + if( ( (neededDataMask & MM_FACECOLOR)!=0) && (currentDataMask& MM_FACECOLOR)==0) + { + cm.face.EnableColor(); + currentDataMask |= MM_FACECOLOR; + } + if( ( (neededDataMask & MM_FACEMARK)!=0) && (currentDataMask& MM_FACEMARK)==0) + { + cm.face.EnableMark(); + currentDataMask |= MM_FACEMARK; + } + } +}; + +class RenderMode +{ +public: + vcg::GLW::DrawMode drawMode; + vcg::GLW::ColorMode colorMode; + vcg::GLW::TextureMode textureMode; + + bool lighting; + bool backFaceCull; + bool doubleSideLighting; + bool fancyLighting; + bool castShadow; + bool selectedFaces; + vcg::Point3f lightDir; + + + RenderMode() + { + drawMode = GLW::DMFlat; + colorMode = GLW::CMNone; + textureMode = GLW::TMNone; + + lighting = true; + backFaceCull = false; + doubleSideLighting = false; + fancyLighting = false; + castShadow = false; + selectedFaces=false; + } }; #endif \ No newline at end of file diff --git a/src/meshlab/plugindialog.cpp b/src/meshlab/plugindialog.cpp index 798343218..b003332aa 100644 --- a/src/meshlab/plugindialog.cpp +++ b/src/meshlab/plugindialog.cpp @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.12 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.11 2006/02/15 23:09:06 fmazzant added the part of MeshIO credits @@ -155,12 +159,6 @@ void PluginDialog::populateTreeWidget(const QString &path,const QStringList &fil foreach(QAction *a,iDecorate->actions()){Templist.push_back(a->text());} addItems(pluginItem,Templist); } - MeshColorizeInterface *iColorize = qobject_cast(plugin); - if (iColorize){ - QStringList Templist; - foreach(QAction *a,iColorize->actions()){Templist.push_back(a->text());} - addItems(pluginItem,Templist); - } MeshFilterInterface *iFilter = qobject_cast(plugin); if (iFilter){ QStringList Templist; @@ -233,13 +231,6 @@ void PluginDialog::displayInfo(QTreeWidgetItem* item,int ncolumn) else foreach(QAction *a,iDecorate->actions()) if (actionName==a->text()) labelInfo->setText(iDecorate->Info(a).Help); } - MeshColorizeInterface *iColorize = qobject_cast(plugin); - if (iColorize) - { - if (item->parent()==NULL) labelInfo->setText(QString("Author: ")+iColorize->Info().Author+QString(" Date: ")+iColorize->Info().Date+QString(" Version: ")+iColorize->Info().Version); - else foreach(QAction *a,iColorize->actions()) - if (actionName==a->text()) labelInfo->setText(iColorize->Info(a).Help); - } MeshFilterInterface *iFilter = qobject_cast(plugin); if (iFilter) { diff --git a/src/meshlab/ui/GenericELDialog.ui b/src/meshlab/ui/GenericELDialog.ui index feed93f7e..cb39e1b4e 100644 --- a/src/meshlab/ui/GenericELDialog.ui +++ b/src/meshlab/ui/GenericELDialog.ui @@ -57,7 +57,7 @@ - <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:MS Shell Dlg; font-weight:400; font-style:normal; text-decoration:none;"><pre style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:Courier New,courier;">Percentile</span></pre></body></html> + Percentile @@ -97,7 +97,7 @@ - <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:MS Shell Dlg; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">Diagonal percentage</span></p></body></html> + Diagonal Percentage @@ -119,6 +119,9 @@ 100 + + 0.2 + diff --git a/src/meshlabplugins/meshcolorize/curvature.h b/src/meshlabplugins/meshcolorize/curvature.h index bcb838077..abbce7364 100644 --- a/src/meshlabplugins/meshcolorize/curvature.h +++ b/src/meshlabplugins/meshcolorize/curvature.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.9 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.8 2006/02/04 09:41:44 vannini Better handling of curvature computation for border vertex Plugin info updated @@ -89,8 +93,12 @@ namespace vcg class Frange { public: - float min; - float max; + Frange(){} + Frange(pair minmax):minV(minmax.first),maxV(minmax.second){} + Frange(float _min,float _max):minV(_min),maxV(_max){} + + float minV; + float maxV; }; template class Curvature @@ -208,18 +216,21 @@ namespace vcg } public: + // REQUIREMENTS: + // FF Topology + // Face Border flags + // Vertex Border flags + // in case of doubts before calling it: + // vcg::tri::UpdateTopology::FaceFace((*ms)); + // vcg::tri::UpdateFlags::FaceBorderFromFF((*ms)); + // vcg::tri::UpdateFlags::VertexBorderFromFace((*ms)); + Curvature(MESH_TYPE &mt):ms(&mt) { TDCurvPtr = new SimpleTempData((*ms).vert); (*TDCurvPtr).Start(CurvData()); TDAreaPtr = new SimpleTempData((*ms).vert); (*TDAreaPtr).Start(AreaData()); - - vcg::tri::UpdateTopology::FaceFace((*ms)); - vcg::tri::UpdateFlags::FaceBorderFromFF((*ms)); - vcg::tri::UpdateFlags::VertexBorderFromFace((*ms)); - //vcg::tri::UpdateColor::VertexBorderFlag((*ms)); - ComputeHK(); } @@ -268,49 +279,6 @@ namespace vcg } } - Frange minMaxQ() - { - VertexIterator vi; - Frange r; - r.min=std::numeric_limits::max(); - r.max=-std::numeric_limits::max(); - - for(vi=(*ms).vert.begin(); vi!=(*ms).vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/) - { - if ((*vi).Q() < r.min) r.min = (*vi).Q(); - if ((*vi).Q() > r.max) r.max = (*vi).Q(); - } - - return r; - - } - - Frange histoPercentile(Frange Q, float histo_frac=DEFAULT_HISTO_FRAC, int histo_range=DEFAULT_HISTO_RANGE) - { - VertexIterator vi; - vcg::Histogram histo; - - histo.SetRange(Q.min, Q.max, histo_range); - - for(vi=(*ms).vert.begin(); vi!=(*ms).vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/) - histo.Add((*vi).Q()); - - Q.min = histo.Percentile(histo_frac); - Q.max = histo.Percentile(1.0f - histo_frac); - - return Q; - } - - - void ColorizeByEqualizedQuality(Frange P) - { - VertexIterator vi; - - for(vi=(*ms).vert.begin(); vi!=(*ms).vert.end(); ++vi) if(!(*vi).IsD() /*&& !(*vi).IsB()*/) - (*vi).C().ColorRamp(P.min, P.max, (*vi).Q()); - - } - }; } #endif // CURVATURE_H \ No newline at end of file diff --git a/src/meshlabplugins/meshcolorize/meshcolorize.cpp b/src/meshlabplugins/meshcolorize/meshcolorize.cpp index 39ebf69a2..f67cefaf6 100644 --- a/src/meshlabplugins/meshcolorize/meshcolorize.cpp +++ b/src/meshlabplugins/meshcolorize/meshcolorize.cpp @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.25 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.24 2006/02/04 09:41:44 vannini Better handling of curvature computation for border vertex Plugin info updated @@ -102,6 +106,7 @@ Added copyright info #include #include #include +#include #include #include "meshcolorize.h" #include "color_manifold.h" @@ -111,40 +116,36 @@ Added copyright info using namespace vcg; ExtraMeshColorizePlugin::ExtraMeshColorizePlugin() { - actionList << new QAction(ST(CP_EQUALIZE), this); - actionList << new QAction(ST(CP_GAUSSIAN), this); - actionList << new QAction(ST(CP_MEAN), this); - actionList << new QAction(ST(CP_RMS), this); - actionList << new QAction(ST(CP_ABSOLUTE), this); - actionList << new QAction(ST(CP_SELFINTERSECT), this); - actionList << new QAction(ST(CP_BORDER), this); - actionList << new QAction(ST(CP_COLORNM), this); - actionList << new QAction(ST(CP_SMOOTH), this); - actionList << new QAction(ST(CP_RESTORE_ORIGINAL), this); + typeList << + CP_MAP_QUALITY_INTO_COLOR << + CP_GAUSSIAN << + CP_MEAN << + CP_RMS << + CP_ABSOLUTE << + CP_SELFINTERSECT << + CP_BORDER << + CP_COLOR_NON_MANIFOLD << + CP_SMOOTH << + CP_RESTORE_ORIGINAL; + + FilterType tt; + foreach(tt , types()) + actionList << new QAction(ST(tt), this); } -const QString ExtraMeshColorizePlugin::ST(ColorizeType c) { + +const QString ExtraMeshColorizePlugin::ST(FilterType c) { switch(c) { - case CP_EQUALIZE: - return QString("Colorize by Quality"); - case CP_GAUSSIAN: - return QString("Gaussian Curvature (equalized)"); - case CP_MEAN: - return QString("Mean Curvature (equalized)"); - case CP_RMS: - return QString("Root mean square Curvature (equalized)"); - case CP_ABSOLUTE: - return QString("Absolute Curvature (equalized)"); - case CP_SELFINTERSECT: - return QString("Self Intersections"); - case CP_BORDER: - return QString("Border"); - case CP_COLORNM: - return QString("Color non Manifold"); - case CP_SMOOTH: - return QString("Smooth Color"); - case CP_RESTORE_ORIGINAL: - return QString("Restore Color"); + case CP_MAP_QUALITY_INTO_COLOR: return QString("Colorize by Quality"); + case CP_GAUSSIAN: return QString("Gaussian Curvature (equalized)"); + case CP_MEAN: return QString("Mean Curvature (equalized)"); + case CP_RMS: return QString("Root mean square Curvature (equalized)"); + case CP_ABSOLUTE: return QString("Absolute Curvature (equalized)"); + case CP_SELFINTERSECT: return QString("Self Intersections"); + case CP_BORDER: return QString("Border"); + case CP_COLOR_NON_MANIFOLD: return QString("Color non Manifold"); + case CP_SMOOTH: return QString("Smooth Color"); + case CP_RESTORE_ORIGINAL: return QString("Restore Color"); default: assert(0); } return QString("error!"); @@ -152,58 +153,28 @@ const QString ExtraMeshColorizePlugin::ST(ColorizeType c) { const ActionInfo &ExtraMeshColorizePlugin::Info(QAction *action) { static ActionInfo ai; - - if( action->text() == ST(CP_EQUALIZE) ) + switch(ID(action)) { - ai.Help = tr("Colorize vertex and faces depending on quality field (manually equalized)."); - ai.ShortHelp = tr("Colorize by quality"); - } - if( action->text() == ST(CP_GAUSSIAN) ) - { - ai.Help = tr("Colorize vertex and faces depending on equalized gaussian curvature."); - ai.ShortHelp = tr("Colorize by gaussian curvature"); - } - if( action->text() == ST(CP_MEAN) ) - { - ai.Help = tr("Colorize vertex and faces depending on equalized mean curvature."); - ai.ShortHelp = tr("Colorize by mean curvature"); - } - if( action->text() == ST(CP_RMS) ) - { - ai.Help = tr("Colorize vertex and faces depending on equalized root mean square curvature."); - ai.ShortHelp = tr("Colorize by root mean square curvature"); - } - if( action->text() == ST(CP_ABSOLUTE) ) - { - ai.Help = tr("Colorize vertex and faces depending on equalize absolute curvature."); - ai.ShortHelp = tr("Colorize by absolute curvature"); - } - if( action->text() == ST(CP_SELFINTERSECT) ) - { - ai.Help = tr("Colorize only self intersecting faces."); - ai.ShortHelp = tr("Colorize only self intersecting faces"); - } - - if( action->text() == ST(CP_BORDER) ) - { - ai.Help = tr("Colorize only border edges."); - ai.ShortHelp = tr("Colorize only border edges"); - } - - if( action->text() == ST(CP_COLORNM) ) - { - ai.Help = tr("Colorize only non manifold edges."); - ai.ShortHelp = tr("Colorize only non manifold edges"); - } - if( action->text() == ST(CP_SMOOTH) ) - { - ai.Help = tr("Apply laplacian smooth for colors."); - ai.ShortHelp = tr("Laplacian smooth for colors"); - } - if( action->text() == ST(CP_RESTORE_ORIGINAL) ) - { - ai.Help = tr("Restore original per vertex color."); - ai.ShortHelp = tr("Restore original per vertex color"); + case CP_MAP_QUALITY_INTO_COLOR : ai.Help = tr("Colorize vertex and faces depending on quality field (manually equalized)."); + ai.ShortHelp = tr("Colorize by quality");break; + case CP_GAUSSIAN : ai.Help = tr("Colorize vertex and faces depending on equalized gaussian curvature."); + ai.ShortHelp = tr("Colorize by gaussian curvature");break; + case CP_MEAN : ai.Help = tr("Colorize vertex and faces depending on equalized mean curvature."); + ai.ShortHelp = tr("Colorize by mean curvature");break; + case CP_RMS : ai.Help = tr("Colorize vertex and faces depending on equalized root mean square curvature."); + ai.ShortHelp = tr("Colorize by root mean square curvature");break; + case CP_ABSOLUTE : ai.Help = tr("Colorize vertex and faces depending on equalize absolute curvature."); + ai.ShortHelp = tr("Colorize by absolute curvature");break; + case CP_SELFINTERSECT: ai.Help = tr("Colorize only self intersecting faces."); + ai.ShortHelp = tr("Colorize only self intersecting faces");break; + case CP_BORDER : ai.Help = tr("Colorize only border edges."); + ai.ShortHelp = tr("Colorize only border edges");break; + case CP_COLOR_NON_MANIFOLD: ai.Help = tr("Colorize only non manifold edges."); + ai.ShortHelp = tr("Colorize only non manifold edges");break; + case CP_SMOOTH : ai.Help = tr("Apply laplacian smooth for colors."); + ai.ShortHelp = tr("Laplacian smooth for colors");break; + case CP_RESTORE_ORIGINAL : ai.Help = tr("Restore original per vertex color."); + ai.ShortHelp = tr("Restore original per vertex color"); break; } return ai; } @@ -216,80 +187,88 @@ const PluginInfo &ExtraMeshColorizePlugin::Info() return ai; } -QList ExtraMeshColorizePlugin::actions() const { - return actionList; -} -void ExtraMeshColorizePlugin::Compute(QAction * mode, MeshModel &m, RenderMode &rm, GLArea *parent){ - if(mode->text() == ST(CP_EQUALIZE)) - { - Curvature c(m.cm); - - Frange mmmq = c.minMaxQ(); - eqSettings.meshMinQ = mmmq.min; - eqSettings.meshMaxQ = mmmq.max; - Frange hmmq=c.histoPercentile(mmmq, 1.0f / (float) eqSettings.percentile, eqSettings.range); - eqSettings.histoMinQ = hmmq.min; - eqSettings.histoMaxQ = hmmq.max; +const int ExtraMeshColorizePlugin::getRequirements(QAction *action) +{ + switch(ID(action)) + { + case CP_GAUSSIAN: + case CP_MEAN: + case CP_RMS: + case CP_ABSOLUTE: return MeshModel::MM_FACETOPO | MeshModel::MM_BORDERFLAG; + case CP_SELFINTERSECT: return MeshModel::MM_FACEMARK | MeshModel::MM_FACETOPO | MeshModel::MM_FACECOLOR; + case CP_BORDER: return MeshModel::MM_BORDERFLAG; + case CP_COLOR_NON_MANIFOLD: return MeshModel::MM_FACETOPO; + case CP_SMOOTH: + case CP_RESTORE_ORIGINAL: + case CP_MAP_QUALITY_INTO_COLOR: return 0; + default: assert(0); + } + return 0; +} + + +bool ExtraMeshColorizePlugin::getParameters(QAction *action, QWidget *parent, MeshModel &m,FilterParameter &par) +{ + par.clear(); + switch(ID(action)) + { + case CP_MAP_QUALITY_INTO_COLOR : + Histogramf H; + tri::Stat::ComputePerVertexQualityHistogram(m.cm,H); + + Frange mmmq(tri::Stat::ComputePerVertexQualityMinMax(m.cm)); + eqSettings.meshMinQ = mmmq.minV; + eqSettings.meshMaxQ = mmmq.maxV; + + eqSettings.histoMinQ = H.Percentile(eqSettings.percentile/100); + eqSettings.histoMaxQ = H.Percentile(1.0f-eqSettings.percentile/100); EqualizerDialog eqdialog(parent); eqdialog.setValues(eqSettings); + if (eqdialog.exec()!=QDialog::Accepted) - return; + return false; + Frange FinalRange; eqSettings=eqdialog.getValues(); - if (eqSettings.useManual) - { - Frange manual; - manual.min=eqSettings.manualMinQ; - manual.max=eqSettings.manualMaxQ; - c.ColorizeByEqualizedQuality(manual); - }else{ - c.ColorizeByEqualizedQuality(c.histoPercentile(mmmq, 1.0f / (float) eqSettings.percentile, eqSettings.range)); - } + if (eqSettings.useManual) + FinalRange = Frange(eqSettings.manualMinQ,eqSettings.manualMaxQ); + else + { + FinalRange.minV=H.Percentile(eqSettings.percentile); + FinalRange.maxV=H.Percentile(1.0f-eqSettings.percentile); + } + + par.addFloat("RangeMin",FinalRange.minV); + par.addFloat("RangeMax",FinalRange.maxV); + } +} +bool ExtraMeshColorizePlugin::applyFilter(QAction *filter, MeshModel &m, FilterParameter & par, vcg::CallBackPos *cb) +{ + switch(ID(filter)) { + case CP_MAP_QUALITY_INTO_COLOR : + { +// Curvature c(m.cm); + break; + } + case CP_GAUSSIAN: + case CP_MEAN: + case CP_RMS: + case CP_ABSOLUTE: + { + Curvature c(m.cm); + switch (ID(filter)){ + case CP_GAUSSIAN: c.MapGaussianCurvatureIntoQuality(); break; + case CP_MEAN: c.MapMeanCurvatureIntoQuality(); break; + case CP_RMS: c.MapRMSCurvatureIntoQuality(); break; + case CP_ABSOLUTE: c.MapAbsoluteCurvatureIntoQuality(); break; + } - rm.colorMode = GLW::CMPerVert; - - return; + tri::UpdateColor::VertexQuality(m.cm,-.1,.1); + break; } - - if(mode->text() == ST(CP_GAUSSIAN)) - { - Curvature c(m.cm); - c.MapGaussianCurvatureIntoQuality(); - c.ColorizeByEqualizedQuality(c.histoPercentile(c.minMaxQ(), 1.0f / (float) eqSettings.percentile, eqSettings.range)); - rm.colorMode = GLW::CMPerVert; - return; - } - - if(mode->text() == ST(CP_MEAN)) - { - Curvature c(m.cm); - c.MapMeanCurvatureIntoQuality(); - c.ColorizeByEqualizedQuality(c.histoPercentile(c.minMaxQ(), 1.0f / (float) eqSettings.percentile, eqSettings.range)); - rm.colorMode = GLW::CMPerVert; - return; - } - - if(mode->text() == ST(CP_RMS)) - { - Curvature c(m.cm); - c.MapRMSCurvatureIntoQuality(); - c.ColorizeByEqualizedQuality(c.histoPercentile(c.minMaxQ(), 1.0f / (float) eqSettings.percentile, eqSettings.range)); - rm.colorMode = GLW::CMPerVert; - return; - } - - if(mode->text() == ST(CP_ABSOLUTE)) - { - Curvature c(m.cm); - c.MapAbsoluteCurvatureIntoQuality(); - c.ColorizeByEqualizedQuality(c.histoPercentile(c.minMaxQ(), 1.0f / (float) eqSettings.percentile, eqSettings.range)); - rm.colorMode = GLW::CMPerVert; - return; - } - - if(mode->text() == ST(CP_SELFINTERSECT)) + case CP_SELFINTERSECT: { vector IntersFace; tri::Clean::SelfIntersections(m.cm,IntersFace); @@ -298,38 +277,45 @@ void ExtraMeshColorizePlugin::Compute(QAction * mode, MeshModel &m, RenderMode & for(fpi=IntersFace.begin();fpi!=IntersFace.end();++fpi) (*fpi)->C()=Color4b::Red; - rm.colorMode = GLW::CMPerFace; - return; + break; } - if(mode->text() == ST(CP_BORDER)) - { - vcg::tri::UpdateTopology::FaceFace(m.cm); - vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm); - vcg::tri::UpdateFlags::VertexBorderFromFace (m.cm); - vcg::tri::UpdateColor::VertexBorderFlag(m.cm); - rm.colorMode = GLW::CMPerVert; - return; - } - - if(mode->text() == ST(CP_COLORNM)) - { - vcg::tri::UpdateTopology::FaceFace(m.cm); + case CP_BORDER: + vcg::tri::UpdateColor::VertexBorderFlag(m.cm); + break; + case CP_COLOR_NON_MANIFOLD: ColorManifold(m.cm); - rm.colorMode = GLW::CMPerVert; - } - - if(mode->text() == ST(CP_RESTORE_ORIGINAL)) - { + break; + case CP_RESTORE_ORIGINAL: m.restoreVertexColor(); - rm.colorMode = GLW::CMPerVert; - } - - if(mode->text() == ST(CP_SMOOTH)) - { + break; + case CP_SMOOTH: LaplacianSmoothColor(m.cm,1); - rm.colorMode = GLW::CMPerVert; + break; + } + return true; +} + +const MeshFilterInterface::FilterClass ExtraMeshColorizePlugin::getClass(QAction *a) +{ + switch(ID(a)) + { + case CP_BORDER: + case CP_COLOR_NON_MANIFOLD: + case CP_SMOOTH: + case CP_RESTORE_ORIGINAL: + case CP_MAP_QUALITY_INTO_COLOR: + case CP_GAUSSIAN: + case CP_MEAN: + case CP_RMS: + case CP_ABSOLUTE: + return MeshFilterInterface::VertexColoring; + case CP_SELFINTERSECT: + return MeshFilterInterface::FaceColoring; + default: assert(0); + return MeshFilterInterface::Generic; } } + Q_EXPORT_PLUGIN(ExtraMeshColorizePlugin) diff --git a/src/meshlabplugins/meshcolorize/meshcolorize.h b/src/meshlabplugins/meshcolorize/meshcolorize.h index 590a2165b..6df71c982 100644 --- a/src/meshlabplugins/meshcolorize/meshcolorize.h +++ b/src/meshlabplugins/meshcolorize/meshcolorize.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.19 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.18 2006/02/01 16:23:09 vannini Added "smooth color" filter @@ -35,21 +39,6 @@ added colorize equalizer dialog and "Colorize by Quality" filter some small bugfixes removed color_curvature.h in favour of curvature.h - -Revision 1.15 2006/01/20 18:17:07 vannini -added Restore Color - -Revision 1.14 2006/01/20 16:25:39 vannini -Added Absolute Curvature colorize - -Revision 1.13 2006/01/20 14:46:44 vannini -Code refactoring -Added RMS Curvature colorize - -Revision 1.12 2006/01/13 16:24:16 vannini -Moved gaussian and mean curvature functions into color_curvature.h - - ****************************************************************************/ #ifndef EXTRACOLORIZEPLUGIN_H @@ -70,30 +59,41 @@ Moved gaussian and mean curvature functions into color_curvature.h #include #include "equalizerDialog.h" -class ExtraMeshColorizePlugin : public QObject, public MeshColorizeInterface +class ExtraMeshColorizePlugin : public QObject, public MeshFilterInterface { Q_OBJECT - Q_INTERFACES(MeshColorizeInterface) + Q_INTERFACES(MeshFilterInterface) public: - enum ColorizeType {CP_EQUALIZE,CP_GAUSSIAN,CP_MEAN,CP_RMS,CP_ABSOLUTE,CP_SELFINTERSECT,CP_BORDER,CP_COLORNM,CP_SMOOTH,CP_RESTORE_ORIGINAL}; - const QString ST(ColorizeType c); + enum { + CP_MAP_QUALITY_INTO_COLOR, + CP_GAUSSIAN, + CP_MEAN, + CP_RMS, + CP_ABSOLUTE, + CP_SELFINTERSECT, + CP_BORDER, + CP_COLOR_NON_MANIFOLD, + CP_SMOOTH, + CP_RESTORE_ORIGINAL + }; + + ExtraMeshColorizePlugin(); - - virtual const ActionInfo &Info(QAction *); - virtual const PluginInfo &Info(); - virtual QList actions() const; - - void Compute(QAction * mode, MeshModel &m, RenderMode &rm, GLArea *parent); - void setLog(GLLogStream *log) { this->log = log ; } - -protected: - GLLogStream *log; - QList actionList; - EqualizerSettings eqSettings; + ~ExtraMeshColorizePlugin(){}; + + virtual const QString ST(FilterType filter); + virtual const ActionInfo &Info(QAction *); + virtual const PluginInfo &Info(); + virtual const FilterClass getClass(QAction *); + virtual bool getParameters(QAction *, QWidget *, MeshModel &m, FilterParameter &par); + virtual const int getRequirements(QAction *); + virtual bool applyFilter(QAction *filter, MeshModel &m, FilterParameter & /*parent*/, vcg::CallBackPos * cb) ; +protected: + EqualizerSettings eqSettings; }; #endif diff --git a/src/meshlabplugins/meshdecorate/meshdecorate.cpp b/src/meshlabplugins/meshdecorate/meshdecorate.cpp index d51979dc6..0e81a5bbe 100644 --- a/src/meshlabplugins/meshdecorate/meshdecorate.cpp +++ b/src/meshlabplugins/meshdecorate/meshdecorate.cpp @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.36 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.35 2006/04/20 16:58:51 cignoni Disambiguated (hopefully for the last time) max(float/double) issue. @@ -78,9 +82,8 @@ Some changes in DrawAxis in order to compile under gcc #include #include #include - #include "meshdecorate.h" -#include +#include #include using namespace vcg; @@ -137,7 +140,7 @@ const QString ExtraMeshDecoratePlugin::ST(int id) const return QString("error!"); } -void ExtraMeshDecoratePlugin::Decorate(QAction *a, MeshModel &m, RenderMode &/*rm*/, GLArea *gla) +void ExtraMeshDecoratePlugin::Decorate(QAction *a, MeshModel &m, RenderMode &/*rm*/, QGLWidget *gla, QFont qf) { if(a->text() == ST(DP_SHOW_NORMALS)) { @@ -150,20 +153,21 @@ void ExtraMeshDecoratePlugin::Decorate(QAction *a, MeshModel &m, RenderMode &/*r glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_LINES); glColor4f(.4f,.4f,1.f,.6f); - for(vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) - { - glVertex((*vi).P()); - glVertex((*vi).P()+(*vi).N()*LineLen); - } + for(vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) + if(!(*vi).IsD()) + { + glVertex((*vi).P()); + glVertex((*vi).P()+(*vi).N()*LineLen); + } glEnd(); glPopAttrib(); } if(a->text() == ST(DP_SHOW_BOX_CORNERS)) DrawBBoxCorner(m); - if(a->text() == ST(DP_SHOW_AXIS)) DrawAxis(m,gla); - if(a->text() == ST(DP_SHOW_QUOTED_BOX)) DrawQuotedBox(m,gla); + if(a->text() == ST(DP_SHOW_AXIS)) DrawAxis(m,gla,qf); + if(a->text() == ST(DP_SHOW_QUOTED_BOX)) DrawQuotedBox(m,gla,qf); } -void ExtraMeshDecoratePlugin::DrawQuotedBox(MeshModel &m,GLArea *gla) +void ExtraMeshDecoratePlugin::DrawQuotedBox(MeshModel &m,QGLWidget *gla,QFont qf) { glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT ); glDisable(GL_LIGHTING); @@ -198,21 +202,21 @@ void ExtraMeshDecoratePlugin::DrawQuotedBox(MeshModel &m,GLArea *gla) glPushMatrix(); glScalef(1,s,s); glTranslatef(0,c[1]/s-c[1],c[2]/s-c[2]); - drawQuotedLine(p1,p2,b.min[0],b.max[0],calcSlope(p1,p2,b.DimX(),LabelSpacing,mm,mp,vp),gla); // Draws x axis + drawQuotedLine(p1,p2,b.min[0],b.max[0],calcSlope(p1,p2,b.DimX(),LabelSpacing,mm,mp,vp),gla,qf); // Draws x axis glPopMatrix(); chooseY(b,mm,mp,vp,p1,p2); // Selects y axis candidate glPushMatrix(); glScalef(s,1,s); glTranslatef(c[0]/s-c[0],0,c[2]/s-c[2]); - drawQuotedLine(p1,p2,b.min[1],b.max[1],calcSlope(p1,p2,b.DimY(),LabelSpacing,mm,mp,vp),gla); // Draws y axis + drawQuotedLine(p1,p2,b.min[1],b.max[1],calcSlope(p1,p2,b.DimY(),LabelSpacing,mm,mp,vp),gla,qf); // Draws y axis glPopMatrix(); chooseZ(b,mm,mp,vp,p1,p2); // Selects z axis candidate glPushMatrix(); glScalef(s,s,1); glTranslatef(c[0]/s-c[0],c[1]/s-c[1],0); - drawQuotedLine(p2,p1,b.min[2],b.max[2],calcSlope(p1,p2,b.DimZ(),LabelSpacing,mm,mp,vp),gla); // Draws z axis + drawQuotedLine(p2,p1,b.min[2],b.max[2],calcSlope(p1,p2,b.DimZ(),LabelSpacing,mm,mp,vp),gla,qf); // Draws z axis glPopMatrix(); glPopAttrib(); @@ -365,7 +369,7 @@ void ExtraMeshDecoratePlugin::drawTickedLine(const Point3d &a,const Point3d &b, } -void ExtraMeshDecoratePlugin::drawQuotedLine(const Point3d &a,const Point3d &b, float aVal, float bVal, float tickDist,GLArea *gla) +void ExtraMeshDecoratePlugin::drawQuotedLine(const Point3d &a,const Point3d &b, float aVal, float bVal, float tickDist,QGLWidget *gla, QFont qf) { qDebug("drawQuotedLine %f",tickDist); @@ -391,7 +395,7 @@ void ExtraMeshDecoratePlugin::drawQuotedLine(const Point3d &a,const Point3d &b, int neededZeros=ceil(max(0.0,-log10(double(tickDist)))); for(i=firstTick;irenderText(Zero[0]+i*v[0],Zero[1]+i*v[1],Zero[2]+i*v[2],tr("%1").arg(i,3+neededZeros,'f',neededZeros),gla->getFont()); + gla->renderText(Zero[0]+i*v[0],Zero[1]+i*v[1],Zero[2]+i*v[2],tr("%1").arg(i,3+neededZeros,'f',neededZeros),qf); glPointSize(1); glBegin(GL_POINTS); @@ -413,7 +417,6 @@ void ExtraMeshDecoratePlugin::drawQuotedLine(const Point3d &a,const Point3d &b, glPopAttrib(); // bold font at beginning and at the end - QFont qf = gla->getFont(); qf.setBold(true); gla->renderText(a[0],a[1],a[2],tr("%1").arg(aVal,3+neededZeros,'f',neededZeros+2 ),qf); gla->renderText(b[0],b[1],b[2],tr("%1").arg(bVal,3+neededZeros,'f',neededZeros+2 ),qf); @@ -475,8 +478,8 @@ void ExtraMeshDecoratePlugin::DrawBBoxCorner(MeshModel &m) glPopAttrib(); } - -void ExtraMeshDecoratePlugin::DrawAxis(MeshModel &m,GLArea* gla) + +void ExtraMeshDecoratePlugin::DrawAxis(MeshModel &m,QGLWidget* gla,QFont qf) { float hw=m.cm.bbox.Diag()/2.0; glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT ); @@ -523,11 +526,11 @@ void ExtraMeshDecoratePlugin::DrawAxis(MeshModel &m,GLArea* gla) glTranslate(c); glRotatef(-90,0,1,0); glScalef(sf,sf,sf); Add_Ons::Cone(10,3,1,true); glPopMatrix(); - QFont f(gla->getFont()); - f.setBold(true); - glColor(Color4b::Red); gla->renderText(hw+(sf*3),0,0,QString("X"),f); - glColor(Color4b::Green); gla->renderText(0,hw+(sf*3),0,QString("Y"),f); - glColor(Color4b::Blue); gla->renderText(0,0,hw+(sf*3),QString("Z"),f); + //QFont f(gla->getFont()); + qf.setBold(true); + glColor(Color4b::Red); gla->renderText(hw+(sf*3),0,0,QString("X"),qf); + glColor(Color4b::Green); gla->renderText(0,hw+(sf*3),0,QString("Y"),qf); + glColor(Color4b::Blue); gla->renderText(0,0,hw+(sf*3),QString("Z"),qf); glPopAttrib(); } diff --git a/src/meshlabplugins/meshdecorate/meshdecorate.h b/src/meshlabplugins/meshdecorate/meshdecorate.h index 470bb19fb..9106763f2 100644 --- a/src/meshlabplugins/meshdecorate/meshdecorate.h +++ b/src/meshlabplugins/meshdecorate/meshdecorate.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.16 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.15 2006/02/22 12:24:41 cignoni Restructured Quoted Box. @@ -66,7 +70,7 @@ Starting quoted box (simply draws xyz axes) #include #include -#include "../../meshlab/mainwindow.h" +//#include "../../meshlab/mainwindow.h" class ExtraMeshDecoratePlugin : public QObject, public MeshDecorateInterface { @@ -92,7 +96,7 @@ private: float calcSlope(const Point3d &a,const Point3d &b,float dim,int spacing,double *mm,double *mp,int *vp); void drawTickedLine(const Point3d &p1,const Point3d &p2,float dim,float tickDist); - void drawQuotedLine(const Point3d &a,const Point3d &b,float aVal, float bVal,float tickDist,GLArea *gla); + void drawQuotedLine(const Point3d &a,const Point3d &b,float aVal, float bVal,float tickDist,QGLWidget *gla, QFont qf); void chooseX(Box3f &box,double *modelview,double *projection,int *viewport,Point3d &x1,Point3d &x2); @@ -121,10 +125,10 @@ public: QList actions () const {return actionList;} void DrawBBoxCorner(MeshModel &m); - void DrawAxis(MeshModel &m,GLArea* gla); - void DrawQuotedBox(MeshModel &m,GLArea *gla); + void DrawAxis(MeshModel &m,QGLWidget* gla, QFont qf); + void DrawQuotedBox(MeshModel &m,QGLWidget *gla, QFont qf); - virtual void Decorate(QAction *a, MeshModel &m, RenderMode &rm, GLArea *gla); + virtual void Decorate(QAction *a, MeshModel &m, RenderMode &rm, QGLWidget *gla,QFont qf); }; diff --git a/src/meshlabplugins/meshedit/images/select_face.png b/src/meshlabplugins/meshedit/images/select_face.png new file mode 100644 index 0000000000000000000000000000000000000000..a4681476e0e334978c56fb89791ebffbd0c9db96 GIT binary patch literal 2632 zcmV-O3b*x%P)m!3vV*sN` z)1=g5oFcT=AALJx>yNfG?U*{rWIEas+h(-2Z88#Tu`^m9u`gP+0c+Z^l|nQcgGL2G z5cC4~^!x6)SFe`~_jN#+;hR~*J@?$R_g>#xd+oLMJ^(`)!eGZ@%H4N*5dDQIlrK?U zw*fW$+etC_c~7hjI;|R6A7QaiW>U#)0~n<*6M?pSfrcdD^nZXfeqR}D2R5rlY5`>3 zCn%3mW>C^74i3;Wn#xWBF6IDdih$a=z!g1kA5eM@aFGD=;q`{7aVI^XSLP@X&{Mh zT3 z9zcEYIm)9PQD%U3TcfdVB%t+eB*1ZlB#+l<+4CuDDQ`qy$U2W()-zotAWl&uSn3n)vH%yquu_@ zE9SKGTA!|eFUR;IEV91f2)h0)8Gh>Tz&yJ9yV*dCRUHL`QO-fd6PhV4K9LvHF(G<~ zvFFH@>r8_q)K8t=`aqLW(C$^AW=yblpjY^Vs_3&jZU@Z6sUOQBTmE zr&dl8KrcwV?wghlhXZf!+-aLLZ=S8h>0J2%@KLL+F9Z`HTx3U!I1oFN@|9P7F4>o$ zGcO2^OBg$LEDjtv zU>`MV)O{r35Bw_0sy=%oK=>i$4fgoxSIiut&(YOGa+a2shJyzWCL|^%KFs*=dwBDv z_z{i(;jSce1?}liMPFo2q0QW!V}Eix)=;z=#Q15H!NMc6x+6K(^S#^;QDyl zij)AMh9o@SL=Tt=6vfl26pwx?ojBP33l}08bViAl0AVqw!B0^7!hXCO$NqCy!Dw%$_BA$kh0;yNI!mzz# zv9_VQVgLU9&T#}&<^zZzXNi{p;m=wb&QgI-0H2Drk-q@hw0;9Wo``2K6DCYh@syB~ zk}{RkVL!su4OS2!lud*(8>b}TVOsw(3DDe~9Zjdo7iwy1aQygj?{#ZytM^*S%*=FFS63JC z<1b-u_I88SCqvIrs=p09`!V3Pd=e4vfnC6qCUfmCi>1T%_IAvkJsVwJUEXWX{RUdT zkwoZhp%%CHG$&I|2S0Ds6u3DaT5%z3`j$eb%i*@v)HxUK9Gf@)!GUm%%euiBKYl!x zELq~A;{WAkdPhgcbpGy>{5%h))s?e+5h0ZKkHKox8|DUP;zAC<-Af-#q%vuqyA9Jt zdqr7U8S?V-Lf$JbF1AzgEPk9wX@_@%$$WVtT)TZifKbhzi$4Iy&Ea(D=|^ueYoqn+ zaqA5a4Y)1vH*U8Zt5&Tt)~s2B%F0UA)zx)tr=Xw!W5$e;!q57iXFla6pbKG0=qmz* z->~PGs(=TXD9sPP`68FSMqsqzJp=JVgVn%q5^(nHS911hc#(NPe_%JQ-1RMBCr9%gzc6>c#P8qdeJ&c_rBiQ*$jA9p7!?&2hOA1Z zF7cWfx3>g8G;!iYw-ExhwY5@K5>r*!mvB&q$NhYnj7(uW(1eTOad`Nv4g z8QxO~tDvRu-s}_a(YVLs`6-vS5|~S=Tw2rMBy0c1ty{Nth3crZwA8_8a$zoc0}fj5 zuBDjAZL~?eHB58)nJ2^ z)?ui&xctFT>%6AHi5W9yG==hjrlux?1(D*)*TZ$tAIo4okPDaNXGlB642o;QbubtR(CQ$i{UrDcooX7pgE>J+7iVg0V}Yb)H0lkSfd<6@y{)F? z<`K#QnEMV2hDTFVQ?G=Kgv697Q{2obxAoTeK=%MaUf>QAd4ZjS-hd4!PvBElaZB`C4vmb z0BwTkjnb4mHE^q-`SxLmGIy1bO%>AM&9UFd=I z4->sX<5Z?7OKO`zK0000 + +#include +#include +#include +#include "meshedit.h" +#include +using namespace vcg; + +ExtraMeshEditPlugin::ExtraMeshEditPlugin() { + isDragging=false; + + actionList << new QAction(QIcon(":/images/select_face.png"),"Select Faces in a region", this); + QAction *editAction; + foreach(editAction, actionList) + editAction->setCheckable(true); + +} + +QList ExtraMeshEditPlugin::actions() const { + return actionList; +} + + + const ActionInfo &ExtraMeshEditPlugin::Info(QAction *action) + { + static ActionInfo ai; + + if( action->text() == tr("Invert Selection") ) + { + ai.Help = tr("Apply Loop's Subdivision Surface algorithm, it is an approximate method"); + ai.ShortHelp = tr("Apply Loop's Subdivision Surface algorithm"); + } + return ai; + } + + const PluginInfo &ExtraMeshEditPlugin::Info() +{ + static PluginInfo ai; + ai.Date=tr("__DATE__"); + ai.Version = tr("0.5"); + ai.Author = ("Paolo Cignoni"); + return ai; + } + void ExtraMeshEditPlugin::mousePressEvent (QAction *, QMouseEvent * event, MeshModel &/*m*/, GLArea * gla) + { + start=event->pos(); + cur=start; + return; + } + + void ExtraMeshEditPlugin::mouseMoveEvent (QAction *,QMouseEvent * event, MeshModel &/*m*/, GLArea * gla) + { + prev=cur; + cur=event->pos(); + isDragging = true; + + // now the management of the update + static int lastMouse=0; + static int lastRendering=0; + int curT = clock(); + if(gla->deltaTime < 50 ) gla->update(); + else{ + gla->makeCurrent (); + glDrawBuffer(GL_FRONT); + DrawXORRect(gla,true); + glDrawBuffer(GL_BACK); + glFlush(); + } + } + + void ExtraMeshEditPlugin::mouseReleaseEvent (QAction *,QMouseEvent * event, MeshModel &/*m*/, GLArea * gla) + { + gla->update(); + prev=cur; + cur=event->pos(); + } + void ExtraMeshEditPlugin::DrawXORRect(GLArea * gla, bool doubleDraw) + { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0,gla->curSiz.width(),gla->curSiz.height(),0,-1,1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + glColor3f(1,1,1); + if(doubleDraw) + { + glBegin(GL_LINE_LOOP); + glVertex2f(start.x(),start.y()); + glVertex2f(prev.x(),start.y()); + glVertex2f(prev.x(),prev.y()); + glVertex2f(start.x(),prev.y()); + glEnd(); + } + glBegin(GL_LINE_LOOP); + glVertex2f(start.x(),start.y()); + glVertex2f(cur.x(),start.y()); + glVertex2f(cur.x(),cur.y()); + glVertex2f(start.x(),cur.y()); + glEnd(); + glDisable(GL_LOGIC_OP); + + // Closing 2D + glPopAttrib(); + glPopMatrix(); // restore modelview + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + } + void ExtraMeshEditPlugin::Decorate(QAction * ac, MeshModel &m, GLArea * gla) + { + if(isDragging) + { + DrawXORRect(gla,false); + vector::iterator fpi; + vector NewSel; + QPoint mid=(start+cur)/2; + mid.setY(gla->curSiz.height()- mid.y()); + QPoint wid=(start-cur); + if(wid.x()<0) wid.setX(-wid.x()); + if(wid.y()<0) wid.setY(-wid.y()); + + CMeshO::FaceIterator fi; + for(fi=m.cm.face.begin(),fpi=NewSel.begin();fpi!=NewSel.end();++fi) + if(!(*fi).IsD()) { + if(&(*fi)!=*fpi) (*fpi)->ClearS(); + else { + (*fpi)->SetS(); + ++fpi; + } + } + for(;fi!=m.cm.face.end();++fi) + if(!(*fi).IsD()) (*fi).ClearS(); + + + GLPickTri::PickFace(mid.x(), mid.y(), m.cm, NewSel, wid.x(), wid.y()); + qDebug("Pickface: rect %i %i - %i %i",mid.x(),mid.y(),wid.x(),wid.y()); + qDebug("Pickface: Got %i on %i",NewSel.size(),m.cm.face.size()); + + for(fpi=NewSel.begin();fpi!=NewSel.end();++fpi) + (*fpi)->SetS(); + + isDragging=false; + } + + } + +void ExtraMeshEditPlugin::StartEdit(QAction * /*mode*/, MeshModel &m, GLArea * /*parent*/) +{ + LastSel.clear(); + CMeshO::FaceIterator fi; + for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS() ) + LastSel.push_back(&*fi); + +} + + + +Q_EXPORT_PLUGIN(ExtraMeshEditPlugin) diff --git a/src/meshlabplugins/meshedit/meshedit.h b/src/meshlabplugins/meshedit/meshedit.h new file mode 100644 index 000000000..de966b931 --- /dev/null +++ b/src/meshlabplugins/meshedit/meshedit.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2005-2005 Trolltech AS. All rights reserved. +** +** This file is part of the example classes of the Qt Toolkit. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef EDITPLUGIN_H +#define EDITPLUGIN_H + +#include +#include +#include + +#include +#include + +class ExtraMeshEditPlugin : public QObject, public MeshEditInterface +{ + Q_OBJECT + Q_INTERFACES(MeshEditInterface) + + QList actionList; + +public: + ExtraMeshEditPlugin(); + + virtual ~ExtraMeshEditPlugin() {} + + virtual const ActionInfo &Info(QAction *); + virtual const PluginInfo &Info(); + + virtual void StartEdit(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/); + virtual void EndEdit(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/){}; + virtual void Decorate(QAction * /*mode*/, MeshModel &/*m*/, GLArea * /*parent*/); + virtual void mousePressEvent (QAction *, QMouseEvent *event, MeshModel &/*m*/, GLArea * ); + virtual void mouseMoveEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * ); + virtual void mouseReleaseEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * ); +// virtual void wheelEvent (QAction *QWheelEvent*e, MeshModel &/*m*/, GLArea * ); + virtual QList actions() const ; + QPoint start; + QPoint cur; + QPoint prev; + bool isDragging; + vector LastSel; + +private: + void DrawXORRect(GLArea * gla, bool doubleDraw); +}; + +#endif diff --git a/src/meshlabplugins/meshedit/meshedit.pro b/src/meshlabplugins/meshedit/meshedit.pro new file mode 100644 index 000000000..4d2b202be --- /dev/null +++ b/src/meshlabplugins/meshedit/meshedit.pro @@ -0,0 +1,31 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. ../../../../sf ../../../../code/lib/glew/include +HEADERS = meshedit.h +SOURCES = meshedit.cpp ../../../../code/lib/glew/src/glew.c +TARGET = meshedit +DESTDIR = ../../meshlab/plugins +DEFINES += GLEW_STATIC +QT += opengl +RESOURCES = meshlab.qrc + +# the following line is needed to avoid mismatch between +# the awful min/max macros of windows and the limits max +win32:DEFINES += NOMINMAX + +unix{ + QMAKE_CC = gcc-3.3 + QMAKE_CXX = g++-3.3 + QMAKE_LINK = gcc-3.3 + CONFIG += warn_off debug_and_release +} + +contains(TEMPLATE,lib) { + CONFIG(debug, debug|release) { + unix:TARGET = $$member(TARGET, 0)_debug + else:TARGET = $$member(TARGET, 0)d + } +} + + + diff --git a/src/meshlabplugins/meshedit/meshlab.qrc b/src/meshlabplugins/meshedit/meshlab.qrc new file mode 100644 index 000000000..b58759f17 --- /dev/null +++ b/src/meshlabplugins/meshedit/meshlab.qrc @@ -0,0 +1,5 @@ + + + images/select_face.png + + diff --git a/src/meshlabplugins/meshfilter/invert_faces.h b/src/meshlabplugins/meshfilter/invert_faces.h index 5487c9329..1a3c11d7e 100644 --- a/src/meshlabplugins/meshfilter/invert_faces.h +++ b/src/meshlabplugins/meshfilter/invert_faces.h @@ -24,6 +24,10 @@ /* * $Log$ + * Revision 1.4 2006/05/25 04:57:45 cignoni + * Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. + * Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + * * Revision 1.3 2006/05/06 17:05:18 mariolatronico * - Added license and history * - On invert face also swap the texture coordinates if the @@ -51,21 +55,15 @@ namespace vcg{ typename MESH_TYPE::FaceIterator fi; for (fi = m.face.begin(); fi != m.face.end(); ++fi) - { - + { swap((*fi).V1(0), (*fi).V2(0)); // swap also texture coordinates - if (MESH_TYPE::HasPerWedgeTexture()) { + if (HasPerWedgeTexture(m)) { swap((*fi).WT(0),(*fi).WT(1)); - swap((*fi).WT(1),(*fi).WT(2)); - swap((*fi).WT(2),(*fi).WT(0)); - + // swap((*fi).WT(1),(*fi).WT(2)); + // swap((*fi).WT(2),(*fi).WT(0)); } } - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m); - vcg::tri::UpdateTopology::FaceFace(m); - vcg::tri::UpdateTopology::VertexFace(m); - } } // end of namespace diff --git a/src/meshlabplugins/meshfilter/meshfilter.cpp b/src/meshlabplugins/meshfilter/meshfilter.cpp index 6aaa4544a..5e5610d9e 100644 --- a/src/meshlabplugins/meshfilter/meshfilter.cpp +++ b/src/meshlabplugins/meshfilter/meshfilter.cpp @@ -22,6 +22,10 @@ /**************************************************************************** History $Log$ +Revision 1.61 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.60 2006/04/18 06:57:35 zifnab1974 syntax errors for gcc 3.4.5 resolved @@ -77,15 +81,16 @@ added scale to unit box, move obj center. Rotate around object and origin are no #include #include "refine_loop.h" #include "meshfilter.h" -#include "detacher.h" #include +#include #include +#include #include #include #include -#include #include "invert_faces.h" -#include "decimator.h" +//#include "decimator.h" + #include "../../meshlab/GLLogStream.h" #include "../../meshlab/LogStream.h" @@ -97,14 +102,14 @@ ExtraMeshFilterPlugin::ExtraMeshFilterPlugin() FP_BUTTERFLY_SS<< FP_REMOVE_UNREFERENCED_VERTEX<< FP_REMOVE_DUPLICATED_VERTEX<< - FP_REMOVE_NULL_FACES<< + FP_REMOVE_FACES_BY_AREA<< + FP_REMOVE_FACES_BY_EDGE<< FP_LAPLACIAN_SMOOTH<< FP_DECIMATOR<< FP_MIDPOINT<< FP_REORIENT << FP_INVERT_FACES<< - FP_TRANSFORM<< - FP_REMOVE_SMALL_FACES ; + FP_TRANSFORM; FilterType tt; @@ -129,24 +134,19 @@ const ExtraMeshFilterPlugin::FilterClass ExtraMeshFilterPlugin::getClass(QAction { case FP_REMOVE_UNREFERENCED_VERTEX : case FP_REMOVE_DUPLICATED_VERTEX : - case FP_REMOVE_NULL_FACES : - case FP_REMOVE_SMALL_FACES : + case FP_REMOVE_FACES_BY_AREA: + case FP_REMOVE_FACES_BY_EDGE : return MeshFilterInterface::Cleaning; + case FP_BUTTERFLY_SS : + case FP_LOOP_SS : + case FP_MIDPOINT : + return MeshFilterInterface::Remeshing; default : return MeshFilterInterface::Generic; } } -const ExtraMeshFilterPlugin::FilterType ExtraMeshFilterPlugin::ID(QAction *a) -{ - foreach( FilterType tt, types()) - if( a->text() == ST(tt) ) return tt; - assert(0); - return FP_LOOP_SS; -} - - const QString ExtraMeshFilterPlugin::ST(FilterType filter) { switch(filter) @@ -155,12 +155,12 @@ const QString ExtraMeshFilterPlugin::ST(FilterType filter) case FP_BUTTERFLY_SS : return QString("Butterfly Subdivision Surfaces"); case FP_REMOVE_UNREFERENCED_VERTEX : return QString("Remove Unreferenced Vertex"); case FP_REMOVE_DUPLICATED_VERTEX : return QString("Remove Duplicated Vertex"); - case FP_REMOVE_NULL_FACES : return QString("Remove Null Faces"); - case FP_REMOVE_SMALL_FACES: return QString("Remove faces wrt size"); + case FP_REMOVE_FACES_BY_AREA : return QString("Remove Zero Area Faces"); + case FP_REMOVE_FACES_BY_EDGE : return QString("Remove Faces with edges longer than..."); case FP_LAPLACIAN_SMOOTH : return QString("Laplacian Smooth"); case FP_DECIMATOR : return QString("Clustering decimation"); case FP_MIDPOINT : return QString("Midpoint Subdivision Surfaces"); - case FP_REORIENT : return QString("Re-oriented"); + case FP_REORIENT : return QString("Re-orient"); case FP_INVERT_FACES: return QString("Invert Faces"); case FP_TRANSFORM: return QString("Apply Transform"); @@ -181,10 +181,6 @@ ExtraMeshFilterPlugin::~ExtraMeshFilterPlugin() { } } -QList ExtraMeshFilterPlugin::actions() const { - return actionList; -} - const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) { static ActionInfo ai; @@ -198,6 +194,10 @@ const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) ai.Help = tr("Apply Butterfly Subdivision Surface algorithm. It is an interpolated method, defined on arbitrary triangular meshes. The scheme is known to be C1 but not C2 on regular meshes"); ai.ShortHelp = tr("Apply Butterfly Subdivision Surface algorithm"); break; + case FP_MIDPOINT : + ai.Help = tr("Splits every edge in two"); + ai.ShortHelp = tr("Apply Midpoint's Subdivision Surface algorithm"); + break; case FP_REMOVE_UNREFERENCED_VERTEX : ai.Help = tr("Check for every vertex on the mesh if it is referenced by a face and removes it"); ai.ShortHelp = tr("Remove Unreferenced Vertexes"); @@ -206,10 +206,14 @@ const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) ai.Help = tr("Check for every vertex on the mesh if there are two vertices with same coordinates and removes it"); ai.ShortHelp = tr("Remove Duplicated Vertexes"); break; - case FP_REMOVE_NULL_FACES : + case FP_REMOVE_FACES_BY_AREA : ai.Help = tr("Removes faces with area equal to zero"); ai.ShortHelp = tr("Remove Null Faces"); break; + case FP_REMOVE_FACES_BY_EDGE : + ai.Help = tr("Remove from the mesh all triangles whose have an edge with lenght greater or equal than a threshold"); + ai.ShortHelp = tr("Remove triangle with edge greater than a threshold"); + break; case FP_LAPLACIAN_SMOOTH : ai.Help = tr("For each vertex it calculates the average position with nearest vertex"); ai.ShortHelp = tr("Smooth the mesh surface"); @@ -218,10 +222,6 @@ const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) ai.Help = tr("Collapse vertices by creating a three dimensional grid enveloping the mesh and discretizes them based on the cells of this grid"); ai.ShortHelp = tr("Simplify the surface eliminating triangle"); break; - case FP_MIDPOINT : - ai.Help = tr("Splits every edge in two"); - ai.ShortHelp = tr("Apply Midpoint's Subdivision Surface algorithm"); - break; case FP_REORIENT : ai.Help = tr("Re-oriented the adjacencies of the face of the mesh"); ai.ShortHelp = tr("Re-oriented the face"); @@ -234,15 +234,12 @@ const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) ai.Help = tr("Apply transformation, you can rotate, translate or scale the mesh"); ai.ShortHelp = tr("Apply Transform"); break; - case FP_REMOVE_SMALL_FACES : - ai.Help = tr("Remove from the mesh all triangles whose have an edge with lenght greater or equal than a threshold"); - ai.ShortHelp = tr("Remove triangle with edge greater than a threshold"); - break; } return ai; } - const PluginInfo &ExtraMeshFilterPlugin::Info() + +const PluginInfo &ExtraMeshFilterPlugin::Info() { static PluginInfo ai; ai.Date=tr("__DATE__"); @@ -251,101 +248,117 @@ const ActionInfo &ExtraMeshFilterPlugin::Info(QAction *action) return ai; } - -bool ExtraMeshFilterPlugin::applyFilter(QAction *filter, MeshModel &m, QWidget *parent, vcg::CallBackPos *cb) +const int ExtraMeshFilterPlugin::getRequirements(QAction *action) { - double threshold = 0.0; - bool selected = false; + switch(ID(action)) + { + case FP_LOOP_SS : + case FP_BUTTERFLY_SS : + case FP_MIDPOINT : return MeshModel::MM_FACETOPO | MeshModel::MM_BORDERFLAG; + case FP_LAPLACIAN_SMOOTH: return MeshModel::MM_BORDERFLAG; + case FP_REORIENT: return MeshModel::MM_FACETOPO; + case FP_REMOVE_UNREFERENCED_VERTEX: + case FP_REMOVE_DUPLICATED_VERTEX: + case FP_REMOVE_FACES_BY_AREA: + case FP_REMOVE_FACES_BY_EDGE: + case FP_DECIMATOR: + case FP_TRANSFORM: + case FP_INVERT_FACES: return 0; + default: assert(0); + } + return 0; +} - if( filter->text().contains(tr("Subdivision Surface")) ) { +bool ExtraMeshFilterPlugin::getParameters(QAction *action, QWidget *, MeshModel &m,FilterParameter &par) +{ + par.clear(); + switch(ID(action)) + { + case FP_LOOP_SS : + case FP_BUTTERFLY_SS : + case FP_MIDPOINT : + case FP_REMOVE_FACES_BY_EDGE: + case FP_DECIMATOR: + { + Histogram histo; + genericELD->setHistogram(&histo); + genericELD->setDiagonale(m.cm.bbox.Diag()); + int continueValue = genericELD->exec(); - vcg::tri::UpdateTopology::FaceFace(m.cm); + //int continueValue = refineDialog->exec(); + if (continueValue == QDialog::Rejected) return false; // don't continue, user pressed Cancel + float threshold = genericELD->getThreshold(); // threshold for refinying + // qDebug( "%f", threshold ); + bool selected = genericELD->getSelected(); // refine only selected faces + par.addBool("Selected",selected); + par.addFloat("Threshold",threshold); + break; + } + case FP_TRANSFORM: + { + transformDialog->setMesh(&m.cm); + int continueValue = transformDialog->exec(); + if (continueValue == QDialog::Rejected) + return false; + Matrix44f matrix = transformDialog->getTransformation(); + par.addMatrix44("Transform",matrix); + break; + } + case FP_REMOVE_FACES_BY_AREA: + case FP_REMOVE_UNREFERENCED_VERTEX: + case FP_REMOVE_DUPLICATED_VERTEX: + case FP_REORIENT: + case FP_INVERT_FACES: + case FP_LAPLACIAN_SMOOTH: + return true; // no parameters + default :assert(0); + } + return true; +} + +bool ExtraMeshFilterPlugin::applyFilter(QAction *filter, MeshModel &m, FilterParameter & par, vcg::CallBackPos *cb) +{ + if( getClass(filter)==Remeshing) + { + if ( ! vcg::tri::Clean::IsTwoManifoldFace(m.cm) ) { + QMessageBox::warning(0, QString("Can't continue"), QString("Mesh faces not 2 manifold")); // text + return false; // can't continue, mesh can't be processed + } - if ( ! vcg::tri::Clean::IsTwoManifoldFace(m.cm) ) { - QMessageBox::warning(parent, // parent - QString("Can't continue"), // caption - QString("Mesh faces not 2 manifold")); // text - return false; // can't continue, mesh can't be processed - } - if(!m.cm.face.IsWedgeTexEnabled()) m.cm.face.EnableWedgeTex(); - vcg::tri::UpdateTopology::FaceFace(m.cm); - vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm); - vcg::tri::UpdateNormals::PerVertexNormalized(m.cm); - vcg::tri::UpdateBounding::Box(m.cm); + bool selected=par.getBool("Selected"); + float threshold = par.getFloat("Threshold"); - float diagonale = m.cm.bbox.Diag(); - Histogram *histo= new Histogram(); - histo->SetRange( 0, diagonale, 10000); - CMeshO::FaceIterator fi; - for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) - { - if(!(*fi).IsD()) - { - if( !(*fi).V(0)->IsS() && !(*fi).V(1)->IsS() ) - { - histo->Add(Distance((*fi).V(0)->P(),(*fi).V(1)->P())); - (*fi).V(0)->SetS(); - (*fi).V(1)->SetS(); - } - if( !(*fi).V(1)->IsS() && !(*fi).V(2)->IsS()) - { - histo->Add(Distance((*fi).V(1)->P(),(*fi).V(2)->P())); - (*fi).V(2)->SetS(); - (*fi).V(1)->SetS(); - } - if( !(*fi).V(2)->IsS() && !(*fi).V(0)->IsS()) - { - histo->Add(Distance((*fi).V(2)->P(),(*fi).V(0)->P())); - (*fi).V(0)->SetS(); - (*fi).V(2)->SetS(); - } - } - } - CMeshO::VertexIterator vi; - for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) - (*vi).ClearS(); - genericELD->setHistogram(histo); - genericELD->setDiagonale(diagonale); - int continueValue = genericELD->exec(); - //int continueValue = refineDialog->exec(); - if (continueValue == QDialog::Rejected) - return false; // don't continue, user pressed Cancel - threshold = genericELD->getThreshold(); // threshold for refinying - // qDebug( "%f", threshold ); - bool selected = genericELD->getSelected(); // refine only selected faces + switch(ID(filter)) { + case FP_LOOP_SS : + RefineOddEvenE, EvenPointLoop > + (m.cm, OddPointLoop(), EvenPointLoop(), threshold, selected, cb); + break; + case FP_BUTTERFLY_SS : + Refine > + (m.cm, MidPointButterfly(), threshold, selected, cb); + break; + case FP_MIDPOINT : + Refine > + (m.cm, MidPoint(), threshold, selected, cb); + } + } + if (ID(filter) == FP_REMOVE_FACES_BY_EDGE ) { + bool selected = par.getBool("Selected"); + float threshold = par.getFloat("Threshold"); + if(selected) tri::Clean::RemoveFaceOutOfRangeEdgeSel(m.cm,0,threshold ); + else tri::Clean::RemoveFaceOutOfRangeEdgeSel(m.cm,0,threshold ); } - if(filter->text() == ST(FP_LOOP_SS) ) + if(filter->text() == ST(FP_REMOVE_FACES_BY_AREA) ) { - vcg::RefineOddEvenE, vcg::EvenPointLoop > - (m.cm, OddPointLoop(), EvenPointLoop(),threshold, selected, cb); - - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); + int nullFaces=tri::Clean::RemoveFaceOutOfRangeArea(m.cm,0); + if (log) log->Log(GLLogStream::Info, "Removed %d null faces", nullFaces); } - if(filter->text() == ST(FP_BUTTERFLY_SS) ) - { - vcg::Refine > - (m.cm,vcg::MidPointButterfly(),threshold, selected, cb); - - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); - } - - if(filter->text() == ST(FP_MIDPOINT) ) - { - vcg::Refine > - (m.cm,vcg::MidPoint(),threshold, selected, cb); - - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); - } - - if(filter->text() == ST(FP_REMOVE_UNREFERENCED_VERTEX) ) + if(filter->text() == ST(FP_REMOVE_UNREFERENCED_VERTEX) ) { int delvert=tri::Clean::RemoveUnreferencedVertex(m.cm); - if (log) - log->Log(GLLogStream::Info, "Removed %d unreferenced vertices",delvert); - if (delvert != 0) - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); + if (log) log->Log(GLLogStream::Info, "Removed %d unreferenced vertices",delvert); } if(filter->text() == ST(FP_REMOVE_DUPLICATED_VERTEX) ) @@ -357,23 +370,12 @@ bool ExtraMeshFilterPlugin::applyFilter(QAction *filter, MeshModel &m, QWidget * vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); } - if(filter->text() == ST(FP_REMOVE_NULL_FACES) ) - { - int nullFaces=tri::Clean::RemoveZeroAreaFace(m.cm); - if (log) - log->Log(GLLogStream::Info, "Removed %d null faces", nullFaces); - if (nullFaces != 0) - vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); - } - if(filter->text() == ST(FP_REORIENT) ) { bool oriented; bool orientable; - vcg::tri::UpdateTopology::FaceFace(m.cm); tri::Clean::IsOrientedMesh(m.cm, oriented,orientable); vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); - } if(filter->text() == ST(FP_LAPLACIAN_SMOOTH)) @@ -382,40 +384,25 @@ bool ExtraMeshFilterPlugin::applyFilter(QAction *filter, MeshModel &m, QWidget * vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); } - if(filter->text() == ST(FP_DECIMATOR)) { - float diagonale = m.cm.bbox.Diag(); - - decimatorDialog->setBboxEdge(m.cm.bbox.min,m.cm.bbox.max); - //decimatorDialog->setDiagonale(diagonale); - - int continueValue = decimatorDialog->exec(); - - if (continueValue == QDialog::Rejected) - return false; // don't continue, user pressed Cancel - int Xstep = decimatorDialog->getXStep(); - int Ystep = decimatorDialog->getYStep(); - int Zstep = decimatorDialog->getZStep(); - vcg::tri::UpdateTopology::FaceFace(m.cm); - int delvert = Decimator(m.cm,Xstep,Ystep,Zstep); - if (log) - log->Log(GLLogStream::Info, "Removed %d vertices", delvert); + bool selected = par.getBool("Selected"); + float threshold = par.getFloat("Threshold"); + vcg::tri::Clustering > Grid; + Grid.Init(m.cm.bbox,100000,threshold); + Grid.Add(m.cm); + Grid.Extract(m.cm); vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m.cm); } if (filter->text() == ST(FP_INVERT_FACES) ) { - InvertFaces(m.cm); } if (filter->text() == ST(FP_TRANSFORM) ) { - transformDialog->setMesh(&m.cm); - int continueValue = transformDialog->exec(); - if (continueValue == QDialog::Rejected) - return false; - Matrix44f matrix = transformDialog->getTransformation(); - if (log) { + Matrix44f matrix= par.getMatrix44("Transform"); + + if (log) { log->Log(GLLogStream::Info, transformDialog->getLog().toAscii().data()); } @@ -424,49 +411,6 @@ bool ExtraMeshFilterPlugin::applyFilter(QAction *filter, MeshModel &m, QWidget * vcg::tri::UpdateBounding::Box(m.cm); } - if (filter->text() == ST(FP_REMOVE_SMALL_FACES) ) { - float diagonale = m.cm.bbox.Diag(); - Histogram *histo= new Histogram(); - histo->SetRange( 0, diagonale, 10000); - CMeshO::FaceIterator fi; - for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) - { - if(!(*fi).IsD()) - { - if( !(*fi).V(0)->IsS() && !(*fi).V(1)->IsS() ) - { - histo->Add(Distance((*fi).V(0)->P(),(*fi).V(1)->P())); - (*fi).V(0)->SetS(); - (*fi).V(1)->SetS(); - } - if( !(*fi).V(1)->IsS() && !(*fi).V(2)->IsS()) - { - histo->Add(Distance((*fi).V(1)->P(),(*fi).V(2)->P())); - (*fi).V(2)->SetS(); - (*fi).V(1)->SetS(); - } - if( !(*fi).V(2)->IsS() && !(*fi).V(0)->IsS()) - { - histo->Add(Distance((*fi).V(2)->P(),(*fi).V(0)->P())); - (*fi).V(0)->SetS(); - (*fi).V(2)->SetS(); - } - } - } - CMeshO::VertexIterator vi; - for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) - (*vi).ClearS(); - genericELD->setHistogram(histo); - genericELD->setDiagonale(diagonale); - int continueValue = genericELD->exec(); - - if (continueValue == QDialog::Rejected) - return false; // don't continue, user pressed Cancel - double threshold = genericELD->getThreshold(); // threshold for refinying - selected = genericELD->getSelected(); - Detacher(m.cm, threshold,selected); - vcg::tri::UpdateTopology::FaceFace(m.cm); - } return true; } diff --git a/src/meshlabplugins/meshfilter/meshfilter.h b/src/meshlabplugins/meshfilter/meshfilter.h index 761e2174d..d391cd4ff 100644 --- a/src/meshlabplugins/meshfilter/meshfilter.h +++ b/src/meshlabplugins/meshfilter/meshfilter.h @@ -1,25 +1,32 @@ /**************************************************************************** -** -** Copyright (C) 2005-2005 Trolltech AS. All rights reserved. -** -** This file is part of the example classes of the Qt Toolkit. -** -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software. -** -** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for -** information about Qt Commercial License Agreements. -** -** Contact info@trolltech.com if any conditions of this licensing are -** not clear to you. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** +* 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. * +* * ****************************************************************************/ +/**************************************************************************** /* History $Log$ +Revision 1.29 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.28 2006/04/12 15:12:18 cignoni Added Filter classes (cleaning, meshing etc) @@ -41,8 +48,7 @@ changed spinbox to QEdgeLength widget Revision 1.22 2006/01/31 14:40:40 mariolatronico removed unused variable ActionInfo *ai, added Log history -*/ - +****************************************************************************/ #ifndef EXTRAFILTERSPLUGIN_H #define EXTRAFILTERSPLUGIN_H @@ -68,37 +74,35 @@ class ExtraMeshFilterPlugin : public QObject, public MeshFilterInterface - FP -> Filter Plugin - name of the plugin separated by _ */ - enum FilterType { FP_LOOP_SS, - FP_BUTTERFLY_SS, - FP_REMOVE_UNREFERENCED_VERTEX, - FP_REMOVE_DUPLICATED_VERTEX, - FP_REMOVE_NULL_FACES, - FP_LAPLACIAN_SMOOTH, - FP_DECIMATOR, - FP_MIDPOINT, - FP_REORIENT , - FP_INVERT_FACES, - FP_TRANSFORM, - FP_REMOVE_SMALL_FACES } ; - - const QString ST(FilterType filter); - const FilterType ID(QAction *a); - - virtual QList &types() { return typeList;} + enum { FP_LOOP_SS, + FP_BUTTERFLY_SS, + FP_REMOVE_UNREFERENCED_VERTEX, + FP_REMOVE_DUPLICATED_VERTEX, + FP_REMOVE_FACES_BY_AREA, + FP_REMOVE_FACES_BY_EDGE, + FP_LAPLACIAN_SMOOTH, + FP_DECIMATOR, + FP_MIDPOINT, + FP_REORIENT , + FP_INVERT_FACES, + FP_TRANSFORM + } ; + + ExtraMeshFilterPlugin(); ~ExtraMeshFilterPlugin(); - virtual const ActionInfo &Info(QAction *); + virtual const QString ST(FilterType filter); + virtual const ActionInfo &Info(QAction *); virtual const PluginInfo &Info(); virtual const FilterClass getClass(QAction *); + virtual bool getParameters(QAction *, QWidget *, MeshModel &m, FilterParameter &par); + virtual const int getRequirements(QAction *); - virtual QList actions() const; - bool applyFilter(QAction *filter, MeshModel &m, QWidget *parent, vcg::CallBackPos * cb) ; - void setLog(GLLogStream *log) { this->log = log ; } + + virtual bool applyFilter(QAction *filter, MeshModel &m, FilterParameter & /*parent*/, vcg::CallBackPos * cb) ; protected: - GLLogStream *log; - QList actionList; - QList typeList; + // RefineDialog *refineDialog; DecimatorDialog *decimatorDialog; diff --git a/src/meshlabplugins/meshfilter/meshfilter.pro b/src/meshlabplugins/meshfilter/meshfilter.pro index df65bfcd4..a2a0a85b2 100644 --- a/src/meshlabplugins/meshfilter/meshfilter.pro +++ b/src/meshlabplugins/meshfilter/meshfilter.pro @@ -2,11 +2,11 @@ TEMPLATE = lib CONFIG += plugin INCLUDEPATH += ../.. ../../../../sf ../../../../code/lib/glew/include HEADERS = transformDialog.h \ -decimatorDialog.h \ -detacher.h \ -../../meshlab/GenericELDialog.h \ -../../meshlab/interfaces.h \ -meshfilter.h + decimatorDialog.h \ + clean.h \ + ../../meshlab/GenericELDialog.h \ + ../../meshlab/interfaces.h \ + meshfilter.h SOURCES = transformDialog.cpp \ meshfilter.cpp \ diff --git a/src/meshlabplugins/meshio/meshio.cpp b/src/meshlabplugins/meshio/meshio.cpp index 81aabbfe0..1e522540d 100644 --- a/src/meshlabplugins/meshio/meshio.cpp +++ b/src/meshlabplugins/meshio/meshio.cpp @@ -24,6 +24,10 @@ History $Log$ + Revision 1.85 2006/05/25 04:57:45 cignoni + Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. + Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.84 2006/03/29 10:05:33 cignoni Added missing include export.obj @@ -114,13 +118,8 @@ bool ExtraMeshIOPlugin::open(const QString &formatName, QString &fileName, MeshM oi.cb = cb; if (!vcg::tri::io::ImporterOBJ::LoadMask(filename.c_str(), oi)) return false; - - if(oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD) - { - qDebug("Has Wedge Text Coords\n"); - m.cm.face.EnableWedgeTex(); - } - + m.Enable(oi.mask); + int result = vcg::tri::io::ImporterOBJ::Open(m.cm, filename.c_str(), oi); if (result != vcg::tri::io::ImporterOBJ::E_NOERROR) { @@ -141,16 +140,7 @@ bool ExtraMeshIOPlugin::open(const QString &formatName, QString &fileName, MeshM else if (formatName.toUpper() == tr("PLY")) { vcg::tri::io::ImporterPLY::LoadMask(filename.c_str(), mask); - - if(mask&MeshModel::IOM_VERTQUALITY) qDebug("Has Vertex Quality\n"); - if(mask&MeshModel::IOM_FACEQUALITY) qDebug("Has Face Quality\n"); - if(mask&MeshModel::IOM_FACECOLOR) qDebug("Has Face Color\n"); - if(mask&MeshModel::IOM_VERTCOLOR) qDebug("Has Vertex Color\n"); - if(mask&MeshModel::IOM_WEDGTEXCOORD) - { - qDebug("Has Wedge Text Coords\n"); - m.cm.face.EnableWedgeTex(); - } + m.Enable(mask); int result = vcg::tri::io::ImporterPLY::Open(m.cm, filename.c_str(), mask, cb); if (result != ::vcg::ply::E_NOERROR) @@ -191,12 +181,7 @@ bool ExtraMeshIOPlugin::open(const QString &formatName, QString &fileName, MeshM info.cb = cb; Lib3dsFile *file = NULL; vcg::tri::io::Importer3DS::LoadMask(filename.c_str(), file, info); - - if(info.mask & MeshModel::IOM_WEDGTEXCOORD) - { - qDebug("Has Wedge Text Coords\n"); - m.cm.face.EnableWedgeTex(); - } + m.Enable(info.mask); int result = vcg::tri::io::Importer3DS::Open(m.cm, filename.c_str(), file, info); if (result != vcg::tri::io::Importer3DS::E_NOERROR) diff --git a/src/meshlabplugins/meshio/meshio.pro b/src/meshlabplugins/meshio/meshio.pro index 93f02e67b..31e203f0f 100644 --- a/src/meshlabplugins/meshio/meshio.pro +++ b/src/meshlabplugins/meshio/meshio.pro @@ -15,7 +15,7 @@ SOURCES = meshio.cpp \ TARGET = meshio DESTDIR = ../../meshlab/plugins -#win32:LIBS += ../../../../code/lib/lib3ds-1.2.0/lib3ds-120s.lib +win32-msvc.net:LIBS += ../../../../code/lib/lib3ds-1.2.0/lib3ds-120s.lib win32-g++:LIBS += ../../../../code/lib/lib3ds-1.2.0/lib3ds/lib3ds.a diff --git a/src/meshlabplugins/meshlabplugins.pro b/src/meshlabplugins/meshlabplugins.pro index 430612333..664d526ab 100644 --- a/src/meshlabplugins/meshlabplugins.pro +++ b/src/meshlabplugins/meshlabplugins.pro @@ -3,4 +3,6 @@ SUBDIRS = meshfilter \ meshrender \ meshio \ meshcolorize \ - meshdecorate \ No newline at end of file + meshdecorate \ + meshedit \ + meshselect \ No newline at end of file diff --git a/src/meshlabplugins/meshrender/meshrender.cpp b/src/meshlabplugins/meshrender/meshrender.cpp index fd3cf7784..883cc838b 100644 --- a/src/meshlabplugins/meshrender/meshrender.cpp +++ b/src/meshlabplugins/meshrender/meshrender.cpp @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.17 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.16 2006/03/08 17:26:13 ggangemi added texture tab @@ -69,6 +73,7 @@ Added copyright info #include #include "meshrender.h" +#include using namespace vcg; @@ -259,10 +264,10 @@ void MeshShaderRenderPlugin::initActionList() { } } -void MeshShaderRenderPlugin::Init(QAction *a, MeshModel &m, RenderMode &rm, GLArea *gla) +void MeshShaderRenderPlugin::Init(QAction *a, MeshModel &m, RenderMode &rm, QGLWidget *gla) { if (sDialog) sDialog->close(); - + gla->makeCurrent(); GLenum err = glewInit(); if (GLEW_OK == err) { if (GLEW_ARB_vertex_program && GLEW_ARB_fragment_program) { @@ -359,7 +364,7 @@ void MeshShaderRenderPlugin::Init(QAction *a, MeshModel &m, RenderMode &rm, GLAr } -void MeshShaderRenderPlugin::Render(QAction *a, MeshModel &m, RenderMode &rm, GLArea *gla) +void MeshShaderRenderPlugin::Render(QAction *a, MeshModel &m, RenderMode &rm, QGLWidget * /* gla */) { if (shaders.find(a->text()) != shaders.end()) { ShaderInfo si = shaders[a->text()]; diff --git a/src/meshlabplugins/meshrender/meshrender.h b/src/meshlabplugins/meshrender/meshrender.h index 7e367a941..376e7fde1 100644 --- a/src/meshlabplugins/meshrender/meshrender.h +++ b/src/meshlabplugins/meshrender/meshrender.h @@ -23,6 +23,10 @@ /**************************************************************************** History $Log$ +Revision 1.18 2006/05/25 04:57:45 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + Revision 1.17 2006/02/27 05:02:01 ggangemi Added texture support @@ -126,8 +130,8 @@ public: virtual const PluginInfo &Info(); virtual bool isSupported() {return supported;} - virtual void Init(QAction *a, MeshModel &m, RenderMode &rm, GLArea *gla); - virtual void Render(QAction *a, MeshModel &m, RenderMode &rm, GLArea *gla); + virtual void Init(QAction *a, MeshModel &m, RenderMode &rm, QGLWidget *gla); + virtual void Render(QAction *a, MeshModel &m, RenderMode &rm, QGLWidget *gla); }; diff --git a/src/meshlabplugins/meshrender/shaderDialog.cpp b/src/meshlabplugins/meshrender/shaderDialog.cpp index 221a99a0c..ba0f0afa1 100644 --- a/src/meshlabplugins/meshrender/shaderDialog.cpp +++ b/src/meshlabplugins/meshrender/shaderDialog.cpp @@ -1,11 +1,13 @@ #include "shaderDialog.h" #include - +#include +#include +#include #define DECFACTOR 100000.0f -ShaderDialog::ShaderDialog(ShaderInfo *sInfo, GLArea* gla, RenderMode &rm, QWidget *parent) +ShaderDialog::ShaderDialog(ShaderInfo *sInfo, QGLWidget* gla, RenderMode &rm, QWidget *parent) : QDialog(parent) { ui.setupUi(this); diff --git a/src/meshlabplugins/meshrender/shaderDialog.h b/src/meshlabplugins/meshrender/shaderDialog.h index 5b9a55016..668138435 100644 --- a/src/meshlabplugins/meshrender/shaderDialog.h +++ b/src/meshlabplugins/meshrender/shaderDialog.h @@ -11,20 +11,23 @@ #include #include #include +#include +#include + #include "shaderStructs.h" #include "ui_shaderDialog.h" -#include +//#include class ShaderDialog : public QDialog { Q_OBJECT public: - ShaderDialog(ShaderInfo *sInfo, GLArea* gla, RenderMode &rm, QWidget *parent = 0); + ShaderDialog(ShaderInfo *sInfo, QGLWidget* gla, RenderMode &rm, QWidget *parent = 0); ~ShaderDialog(); private: - GLArea* glarea; + QGLWidget* glarea; RenderMode * rendMode; ShaderInfo * shaderInfo; QSignalMapper *colorSignalMapper; diff --git a/src/meshlabplugins/meshselect/meshselect.cpp b/src/meshlabplugins/meshselect/meshselect.cpp new file mode 100644 index 000000000..53c437521 --- /dev/null +++ b/src/meshlabplugins/meshselect/meshselect.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** + * 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. * + * * + ****************************************************************************/ +/**************************************************************************** + History +$Log$ +Revision 1.1 2006/05/25 04:57:46 cignoni +Major 0.7 release. A lot of things changed. Colorize interface gone away, Editing and selection start to work. +Optional data really working. Clustering decimation totally rewrote. History start to work. Filters organized in classes. + + +****************************************************************************/ +#include + +#include +#include +#include "meshselect.h" + +using namespace vcg; + +const QString SelectionFilterPlugin::ST(FilterType filter) +{ + switch(filter) + { + case FP_SELECT_ALL : return QString("Select All"); + case FP_SELECT_NONE : return QString("Select None"); + case FP_SELECT_INVERT : return QString("Invert Selection"); + case FP_SELECT_DELETE : return QString("Delete Selected Faces"); + } + return QString("Unknown filter"); +} + +SelectionFilterPlugin::SelectionFilterPlugin() +{ + typeList << + FP_SELECT_ALL << + FP_SELECT_NONE << + FP_SELECT_DELETE << + FP_SELECT_INVERT; + + FilterType tt; + + foreach(tt , types()) + actionList << new QAction(ST(tt), this); +} +SelectionFilterPlugin::~SelectionFilterPlugin() +{ + for (int i = 0; i < actionList.count() ; i++ ) { + delete actionList.at(i); + } +} + + +bool SelectionFilterPlugin::applyFilter(QAction *action, MeshModel &m, FilterParameter & par, vcg::CallBackPos * cb) +{ + par.clear(); + CMeshO::FaceIterator fi; + switch(ID(action)) + { + case FP_SELECT_DELETE : + for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS() ) + { + (*fi).SetD(); + --m.cm.fn; + } + break; + case FP_SELECT_ALL : + for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) + if(!(*fi).IsD()) (*fi).SetS(); + break; + case FP_SELECT_NONE : + for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) + if(!(*fi).IsD()) (*fi).ClearS(); + break; + case FP_SELECT_INVERT : + for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) + if(!(*fi).IsD()) + { + if((*fi).IsS()) (*fi).ClearS(); + else (*fi).SetS(); + } + break; + default: assert(0); + } + return true; +} + + const ActionInfo &SelectionFilterPlugin::Info(QAction *action) + { + static ActionInfo ai; + + if( action->text() == tr("Loop Subdivision Surface") ) + { + ai.Help = tr("Apply Loop's Subdivision Surface algorithm, it is an approximate method"); + ai.ShortHelp = tr("Apply Loop's Subdivision Surface algorithm"); + } + return ai; + } + + const PluginInfo &SelectionFilterPlugin::Info() +{ + static PluginInfo ai; + ai.Date=tr("__DATE__"); + ai.Version = tr("0.5"); + ai.Author = ("Paolo Cignoni"); + return ai; + } + +Q_EXPORT_PLUGIN(SelectionFilterPlugin) diff --git a/src/meshlabplugins/meshselect/meshselect.h b/src/meshlabplugins/meshselect/meshselect.h new file mode 100644 index 000000000..cf18bc086 --- /dev/null +++ b/src/meshlabplugins/meshselect/meshselect.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2005-2005 Trolltech AS. All rights reserved. +** +** This file is part of the example classes of the Qt Toolkit. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef EDITPLUGIN_H +#define EDITPLUGIN_H + +#include +#include +#include + +#include +#include + + +class SelectionFilterPlugin : public QObject, public MeshFilterInterface +{ + Q_OBJECT + Q_INTERFACES(MeshFilterInterface) + + public: + /* naming convention : + - FP -> Filter Plugin + - name of the plugin separated by _ + */ + enum { FP_SELECT_ALL, FP_SELECT_NONE, FP_SELECT_INVERT, FP_SELECT_DELETE} ; + + SelectionFilterPlugin(); + ~SelectionFilterPlugin(); + virtual const ActionInfo &Info(QAction *); + virtual const PluginInfo &Info(); + + virtual const QString ST(FilterType filter); + virtual const FilterClass getClass(QAction *) {return FilterClass::Selection;}; + virtual bool getParameters(QAction *, QWidget *, MeshModel &m, FilterParameter &par){return true;}; + virtual const int getRequirements(QAction *){return 0;}; + virtual bool applyFilter(QAction *filter, MeshModel &m, FilterParameter & /*parent*/, vcg::CallBackPos * cb) ; + +protected: + + ActionInfo *ai; + +}; + +#endif diff --git a/src/meshlabplugins/meshselect/meshselect.pro b/src/meshlabplugins/meshselect/meshselect.pro new file mode 100644 index 000000000..91f056f97 --- /dev/null +++ b/src/meshlabplugins/meshselect/meshselect.pro @@ -0,0 +1,27 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. ../../../../sf ../../../../code/lib/glew/include +HEADERS = meshselect.h +SOURCES = meshselect.cpp +TARGET = meshselect +DESTDIR = ../../meshlab/plugins +# the following line is needed to avoid mismatch between +# the awful min/max macros of windows and the limits max +win32:DEFINES += NOMINMAX + +unix{ + QMAKE_CC = gcc-3.3 + QMAKE_CXX = g++-3.3 + QMAKE_LINK = gcc-3.3 + CONFIG += warn_off debug_and_release +} + +contains(TEMPLATE,lib) { + CONFIG(debug, debug|release) { + unix:TARGET = $$member(TARGET, 0)_debug + else:TARGET = $$member(TARGET, 0)d + } +} + + +