Meshlab Navigation ===================== From examination of the collada importer, it seems that all shapes are getting appended into a single mesh. With a single bbox. So no way to navigate to a subnode, unless: #. calculate bbox during import #. transform the bbox min max with the appropriate transform, #. keep all the boxes in a structure keyed by node id #. this will allow the camera to be steered to look at particular nodes * /usr/local/env/graphics/meshlab/vcglib/vcg/space/box.h Where to tack the structure ? ------------------------------- Needs to go on MeshModel ? GUI ----- :: simon:src blyth$ find . -name '*.cpp' -exec grep -H MeshDecorateInterface {} \; ./common/pluginmanager.cpp: MeshDecorateInterface *iDecorator = qobject_cast(plugin); ./meshlab/glarea.cpp: MeshDecorateInterface * decorInterface = qobject_cast(p->parent()); ./meshlab/glarea.cpp: MeshDecorateInterface * decorInterface = qobject_cast(p->parent()); ./meshlab/layerDialog.cpp: MeshDecorateInterface* decPlug = qobject_cast(decList[ii]->parent()); ./meshlab/layerDialog.cpp: MeshDecorateInterface* decPlug = qobject_cast(act->parent()); ./meshlab/mainwindow_Init.cpp: foreach(MeshDecorateInterface *iDecorate,PM.meshDecoratePlugins()) ./meshlab/mainwindow_RunTime.cpp: MeshDecorateInterface *iDecorateTemp = qobject_cast(action->parent()); ./meshlab/plugindialog.cpp: MeshDecorateInterface *iDecorate = qobject_cast(plugin); ./meshlab/plugindialog.cpp: MeshDecorateInterface *iDecorate = qobject_cast(plugin); simon:src blyth$ Controlling the camera ------------------------ /usr/local/env/graphics/meshlab/meshlab/src/meshlabplugins/decorate_background/decorate_background.cpp:: 85 bool SampleMeshDecoratePlugin::startDecorate( QAction * action, MeshDocument &/*m*/, RichParameterSet * parset, GLArea * gla) 86 { 87 switch(ID(action)){ 88 case DP_SHOW_CUBEMAPPED_ENV : 89 if(parset->findParameter(CubeMapPathParam())== NULL) qDebug("CubeMapPath was not setted!!!"); 90 cubemapFileName = parset->getString(CubeMapPathParam()); 91 break; 92 case DP_SHOW_GRID: 93 connect(gla,SIGNAL(transmitShot(QString,vcg::Shotf)),this,SLOT(setValue(QString,vcg::Shotf))); 94 connect(this,SIGNAL(askViewerShot(QString)),gla,SLOT(sendViewerShot(QString))); 95 break; 96 } 97 return true; 98 } :: 100 void SampleMeshDecoratePlugin::decorate(QAction *a, MeshDocument &m, RichParameterSet * parset,GLArea *gla, QPainter * ) 101 { 102 static QString lastname("unitialized"); 103 switch(ID(a)) ... 139 case DP_SHOW_GRID : 140 { 141 emit this->askViewerShot("me"); 142 Box3f bb=m.bbox(); header:: 96 97 signals: 98 void askViewerShot(QString); 99 100 public slots: 101 void setValue(QString name, vcg::Shotf val); 102 103 }; The implementation for the signal is generated by the moc meta-object-compiler. It just looks for connections and calls the slots presumably. * http://stackoverflow.com/questions/1406940/how-signal-and-slots-are-implemented-under-the-hood Lotsa interesting emits from glarea /usr/local/env/graphics/meshlab/meshlab/src/meshlab/glarea.cpp:: 1235 void GLArea::sendViewPos(QString name) 1236 { 1237 #ifndef VCG_USE_EIGEN 1238 Point3f pos= trackball.track.InverseMatrix() *Inverse(trackball.camera.model) *Point3f(0,0,0); 1239 #else 1240 Point3f pos= Eigen::Transform3f(trackball.track.InverseMatrix()) * Eigen::Transform3f(Inverse(trackball.camera.model)).translation(); 1241 #endif 1242 emit transmitViewPos(name, pos); 1243 } 1244 1245 void GLArea::sendSurfacePos(QString name) 1246 { 1247 qDebug("sendSurfacePos %s",qPrintable(name)); 1248 nameToGetPickPos = name; 1249 hasToGetPickPos=true; 1250 } 1251 1252 void GLArea::sendViewDir(QString name) 1253 { 1254 Point3f dir= getViewDir(); 1255 emit transmitViewDir(name,dir); 1256 } 1257 1258 void GLArea::sendMeshShot(QString name) 1259 { 1260 Shotf curShot=this->md()->mm()->cm.shot; 1261 emit transmitShot(name, curShot); 1262 } 1263 1264 void GLArea::sendMeshMatrix(QString name) 1265 { 1266 Matrix44f mat=this->md()->mm()->cm.Tr; 1267 emit transmitMatrix(name, mat); 1268 } 1269 1270 void GLArea::sendViewerShot(QString name) 1271 { 1272 Shotf curShot=shotFromTrackball().first; 1273 emit transmitShot(name, curShot); 1274 } 1275 void GLArea::sendRasterShot(QString name) 1276 { 1277 Shotf curShot = vcg::Shotf(); 1278 if (this->md()->rm() != NULL) 1279 curShot = this->md()->rm()->shot; 1280 emit transmitShot(name, curShot); 1281 } 1282 1283 void GLArea::sendCameraPos( QString name ) 1284 { 1285 Point3f dir= trackball.camera.ViewPoint(); 1286 emit transmitCameraPos(name,dir); 1287 } Meshlab ``Windows > Copy Shot (cmd-C)`` puts the below XML onto clipboard:: Then after navigating elsewhere ``Windows > Paste Shot (cmd-V)`` will return to the copy shot viewpoint and camera parameters. /usr/local/env/graphics/meshlab/meshlab/src/meshlab/mainwindow_Init.cpp:: 366 copyShotToClipboardAct = new QAction (tr("Copy shot"), this); 367 copyShotToClipboardAct->setShortcut(QKeySequence::Copy); 368 connect(copyShotToClipboardAct, SIGNAL(triggered()), this, SLOT(copyViewToClipBoard())); 369 370 pasteShotFromClipboardAct = new QAction (tr("Paste shot"), this); 371 pasteShotFromClipboardAct->setShortcut(QKeySequence::Paste); 372 connect(pasteShotFromClipboardAct, SIGNAL(triggered()), this, SLOT(pasteViewFromClipboard())); The paste is very close to what I want, but with control via a simple string (like a http bookmark) identifying the node, camera position and lookat direction in units of the bbox of the node. /usr/local/env/graphics/meshlab/meshlab/src/meshlab/mainwindow_RunTime.cpp:: 623 void MainWindow::viewFrom(QAction *qa) 624 { 625 if(GLA()) GLA()->createOrthoView(qa->text()); 626 } 627 628 void MainWindow::readViewFromFile() 629 { 630 if(GLA()) GLA()->viewFromFile(); 631 updateMenus(); 632 } 633 634 635 void MainWindow::viewFromCurrentMeshShot() 636 { 637 if(GLA()) GLA()->viewFromCurrentShot("Mesh"); 638 updateMenus(); 639 } 640 641 void MainWindow::viewFromCurrentRasterShot() 642 { 643 if(GLA()) GLA()->viewFromCurrentShot("Raster"); 644 updateMenus(); 645 } 646 647 void MainWindow::copyViewToClipBoard() 648 { 649 if(GLA()) GLA()->viewToClipboard(); 650 } // // TRIGGERING THE PASTE ACTION RESULTS IN THIS BEING SIGNALLED // 652 void MainWindow::pasteViewFromClipboard() 653 { 654 if(GLA()) GLA()->viewFromClipboard(); 655 updateMenus(); 656 } glarea.cpp:: 1681 void GLArea::viewFromClipboard() 1682 { 1683 QClipboard *clipboard = QApplication::clipboard(); 1684 QString shotString = clipboard->text(); 1685 QDomDocument doc("StringDoc"); 1686 doc.setContent(shotString); 1687 loadViewFromViewStateFile(doc); 1688 } :: 1597 /* 1598 ViewState file is an xml file format created by Meshlab with the action "copyToClipboard" 1599 */ 1600 void GLArea::loadViewFromViewStateFile(const QDomDocument &doc) 1601 { 1602 Shotf shot; 1603 QDomElement root = doc.documentElement(); 1604 QDomNode node = root.firstChild(); 1605 1606 while(!node.isNull()) 1607 { 1608 if (QString::compare(node.nodeName(),"VCGCamera")==0) 1609 ReadShotFromQDomNode(shot,node); 1610 else if (QString::compare(node.nodeName(),"CamParam")==0) 1611 ReadShotFromOLDXML(shot,node); 1612 1613 else if (QString::compare(node.nodeName(),"ViewSettings")==0) 1614 { 1615 QDomNamedNodeMap attr = node.attributes(); 1616 trackball.track.sca = attr.namedItem("TrackScale").nodeValue().section(' ',0,0).toFloat(); 1617 nearPlane = attr.namedItem("NearPlane").nodeValue().section(' ',0,0).toFloat(); 1618 farPlane = attr.namedItem("FarPlane").nodeValue().section(' ',0,0).toFloat(); 1619 fov = shot.GetFovFromFocal(); 1620 clipRatioNear = (getCameraDistance()-nearPlane)/2.0f ; 1621 clipRatioFar = (farPlane-getCameraDistance())/10.0f ; 1622 1623 } 1624 else if (QString::compare(node.nodeName(),"Render")==0) 1625 { 1626 QDomNamedNodeMap attr = node.attributes(); 1627 rm.drawMode = (vcg::GLW::DrawMode) (attr.namedItem("DrawMode").nodeValue().section(' ',0,0).toInt()); 1628 rm.colorMode = (vcg::GLW::ColorMode) (attr.namedItem("ColorMode").nodeValue().section(' ',0,0).toInt()); 1629 rm.textureMode = (vcg::GLW::TextureMode) (attr.namedItem("TextureMode").nodeValue().section(' ',0,0).toInt()); 1630 rm.lighting = (attr.namedItem("Lighting").nodeValue().section(' ',0,0).toInt() != 0); 1631 rm.backFaceCull = (attr.namedItem("BackFaceCull").nodeValue().section(' ',0,0).toInt() != 0); 1632 rm.doubleSideLighting = (attr.namedItem("DoubleSideLighting").nodeValue().section(' ',0,0).toInt() != 0); 1633 rm.fancyLighting = (attr.namedItem("FancyLighting").nodeValue().section(' ',0,0).toInt() != 0); 1634 rm.selectedFace = (attr.namedItem("SelectedFace").nodeValue().section(' ',0,0).toInt() != 0); 1635 rm.selectedVert = (attr.namedItem("SelectedVert").nodeValue().section(' ',0,0).toInt() != 0); 1636 } 1637 node = node.nextSibling(); 1638 } 1639 1640 loadShot(QPair (shot,trackball.track.sca)); 1641 } .... 1726 void GLArea::loadShot(const QPair &shotAndScale){ 1727 1728 Shotf shot = shotAndScale.first; 1729 1730 fov = shot.GetFovFromFocal(); 1731 1732 float cameraDist = getCameraDistance(); 1733 1734 //reset trackball. The point of view must be set only by the shot 1735 trackball.Reset(); 1736 trackball.track.sca = shotAndScale.second; 1737 1738 /*Point3f point = this->md()->bbox().Center(); 1739 Point3f p1 = ((trackball.track.Matrix()*(point-trackball.center))- Point3f(0,0,cameraDist));*/ 1740 shot2Track(shot, cameraDist,trackball); 1741 .... just comments here .... 1768 update(); 1769 } /usr/local/env/graphics/meshlab/meshlab/src/meshlab/glarea.h:: 479 /* 480 Given a shot "from" and a trackball "track", updates "track" with "from" extrinsics. 481 A traslation involving cameraDistance is included. This is necessary to compensate a trasformation that OpenGL performs 482 at the end of the graphic pipeline. 483 */ 484 template 485 void shot2Track(const vcg::Shot &from, const float cameraDist, vcg::Trackball &tb){ 486 487 vcg::Quaternion qfrom; qfrom.FromMatrix(from.Extrinsics.Rot()); 488 489 tb.track.rot = vcg::Quaternionf::Construct(qfrom); 490 tb.track.tra = (vcg::Point3f::Construct(-from.Extrinsics.Tra())); 491 tb.track.tra += vcg::Point3f::Construct(tb.track.rot.Inverse().Rotate(vcg::Point3f(0,0,cameraDist)))*(1/tb.track.sca); 492 } /usr/local/env/graphics/meshlab/vcglib/wrap/qt/shot_qt.h:: 09 template 10 bool ReadShotFromQDomNode( 11 ShotType &shot, /// the shot that will contain the read node 12 const QDomNode &node) /// The XML node to be read 13 { 14 typedef typename ShotType::ScalarType ScalarType; 15 typedef vcg::Point3 Point3x; 16 if(QString::compare(node.nodeName(),"VCGCamera")==0) 17 { 18 QDomNamedNodeMap attr = node.attributes(); 19 Point3x tra; 20 tra[0] = attr.namedItem("TranslationVector").nodeValue().section(' ',0,0).toDouble(); 21 tra[1] = attr.namedItem("TranslationVector").nodeValue().section(' ',1,1).toDouble(); 22 tra[2] = attr.namedItem("TranslationVector").nodeValue().section(' ',2,2).toDouble(); 23 shot.Extrinsics.SetTra(-tra); 24 25 vcg::Matrix44 rot; 26 QStringList values = attr.namedItem("RotationMatrix").nodeValue().split(" ", QString::SkipEmptyParts); 27 for(int y = 0; y < 4; y++) 28 for(int x = 0; x < 4; x++) 29 rot[y][x] = values[x + 4*y].toDouble(); 30 shot.Extrinsics.SetRot(rot); 31 32 vcg::Camera &cam = shot.Intrinsics; 33 cam.FocalMm = attr.namedItem("FocalMm").nodeValue().toDouble(); 34 cam.ViewportPx.X() = attr.namedItem("ViewportPx").nodeValue().section(' ',0,0).toInt(); 35 cam.ViewportPx.Y() = attr.namedItem("ViewportPx").nodeValue().section(' ',1,1).toInt(); 36 cam.CenterPx[0] = attr.namedItem("CenterPx").nodeValue().section(' ',0,0).toInt(); 37 cam.CenterPx[1] = attr.namedItem("CenterPx").nodeValue().section(' ',1,1).toInt(); 38 cam.PixelSizeMm[0] = attr.namedItem("PixelSizeMm").nodeValue().section(' ',0,0).toDouble(); 39 cam.PixelSizeMm[1] = attr.namedItem("PixelSizeMm").nodeValue().section(' ',1,1).toDouble(); 40 cam.k[0] = attr.namedItem("LensDistortion").nodeValue().section(' ',0,0).toDouble(); 41 cam.k[1] = attr.namedItem("LensDistortion").nodeValue().section(' ',1,1).toDouble(); 42 43 // scale correction should no more exist !!! 44 // float scorr = attr.namedItem("ScaleCorr").nodeValue().toDouble(); 45 // if(scorr != 0.0) { 46 // cam.PixelSizeMm[0] *= scorr; 47 // cam.PixelSizeMm[1] *= scorr; 48 // } 49 return true; 50 } 51 return false; 52 } Hmm need some bbox based shot calculations, /usr/local/env/graphics/meshlab/vcglib/wrap/gl/shot.h:: 172 /// given a shot and the mesh bounding box, return near and far plane (exact) 173 static void GetNearFarPlanes(vcg::Shot & shot, vcg::Box3 bbox, ScalarType &nr, ScalarType &fr) 174 { 210 /********************************** 211 DEFINE SHOT FROM TRACKBALL 212 Adds to a given shot the trackball transformations. 213 After this operation the trackball should be resetted, to avoid 214 multiple apply of the same transformation. 215 ***********************************/ 216 static void FromTrackball(const vcg::Trackball & tr, 217 vcg::Shot & sShot, 218 vcg::Shot & shot ) 219 { 220 vcg::Point3 cen; cen.Import(tr.center); 221 vcg::Point3 tra; tra.Import(tr.track.tra); 222 vcg::Matrix44 trM; trM.FromMatrix(tr.track.Matrix()); 223 224 vcg::Point3 vp = Inverse(trM)*(sShot.GetViewPoint()-cen) +cen;// +tra; 225 226 shot.SetViewPoint(vp); 227 shot.Extrinsics.SetRot(sShot.Extrinsics.Rot()*trM); 228 // shot.Extrinsics.sca = sShot.Extrinsics.sca*(ScalarType)tr.track.sca; 229 } 230 }; /usr/local/env/graphics/meshlab/vcglib/vcg/math/shot.h:: 185 /// look at (point+up) 186 void LookAt(const vcg::Point3 & point,const vcg::Point3 & up); 187 188 /// look at (opengl-like) 189 void LookAt(const S & eye_x,const S & eye_y,const S & eye_z, 190 const S & at_x,const S & at_y,const S & at_z, 191 const S & up_x,const S & up_y,const S & up_z); 192 193 /// look towards (dir+up) 194 void LookTowards(const vcg::Point3 & z_dir,const vcg::Point3 & up); ... 330 /// look at (point+up) 331 template 332 void Shot::LookAt(const vcg::Point3 & z_dir,const vcg::Point3 & up) 333 { 334 LookTowards(z_dir-GetViewPoint(),up); 335 } 336 337 /// look at (opengl-like) 338 template 339 void Shot::LookAt(const S & eye_x, const S & eye_y, const S & eye_z, 340 const S & at_x, const S & at_y, const S & at_z, 341 const S & up_x,const S & up_y,const S & up_z) 342 { 343 SetViewPoint(Point3(eye_x,eye_y,eye_z)); 344 LookAt(Point3(at_x,at_y,at_z),Point3(up_x,up_y,up_z)); 345 } 346 347 /// look towards 348 template 349 void Shot::LookTowards(const vcg::Point3 & z_dir,const vcg::Point3 & up) 350 { 351 vcg::Point3 x_dir = up ^-z_dir; 352 vcg::Point3 y_dir = -z_dir ^x_dir; 353 354 Matrix44 m; 355 m.SetIdentity(); 356 *(vcg::Point3 *)&m[0][0] = x_dir/x_dir.Norm(); 357 *(vcg::Point3 *)&m[1][0] = y_dir/y_dir.Norm(); 358 *(vcg::Point3 *)&m[2][0] = -z_dir/z_dir.Norm(); 359 360 Extrinsics.rot.FromMatrix(m); 361 } Succeeded to deliver string to GLArea via DBus and signal/slot -------------------------------------------------------------------- :: simon:src blyth$ t meshlab-v meshlab-v is a function meshlab-v () { qdbus com.meshlab.navigator / SayHelloThere ${1:-simon} } simon:src blyth$ meshlab-v yo :: Processing Visual Scene child 1 - of type 'extra' LOG: 0 Opened mesh /usr/local/env/geant4/geometry/gdml/3199.dae in 197 msec LOG: 0 All files opened in 3126 msec MyNav::SayHelloThere [ "simon" ] GLArea::handleNavigateViewer [ "simon" ] MyNav::SayHelloThere [ "yo" ] GLArea::handleNavigateViewer [ "yo" ] :: :: ColladaIOPlugin::open pass the bbox_cache to the MeshModel LOG: 0 Opened mesh /usr/local/env/geant4/geometry/gdml/3199.dae in 468 msec LOG: 0 All files opened in 3100 msec MyNav::SayHelloThere [ "simon" ] GLArea::handleNavigateViewer [ "simon" ] ** bbox 0 min -2473.613037 -402.842834 -1849.429443 max -2173.610107 -203.982086 -1650.569336 Trackball ------------ /usr/local/env/graphics/meshlab/vcglib/wrap/gui/trackball.h:: 099 class Transform { 100 public: 101 /*! 102 @brief The constructor. 103 104 Initialize: 105 - track to the identity transform. 106 - center to origin 0,0,0. 107 - radius to unit size. 108 */ 109 Transform(); 110 /// A trackball stores a transformation called 'track' that effectively rototranslate the object. 111 Similarityf track; 112 /// track position in model space. 113 Point3f center; 114 /// size of the widget in model space. 115 float radius; 116 }; ... 167 class Trackball: public Transform { 168 public: ... 279 // T(c) S R T(t) T(-c) => S R T(S^(-1) R^(-1)(c) + t - c) 280 Matrix44f Matrix() const; 281 Matrix44f InverseMatrix() const; ... /usr/local/env/graphics/meshlab/vcglib/vcg/math/similarity.h:: 103 template > class Similarity { 104 public: 105 Similarity() {} 106 Similarity(const RotationType &q) { SetRotate(q); } 107 Similarity(const Point3 &p) { SetTranslate(p); } 108 Similarity(S s) { SetScale(s); } 109 Similarity(S alpha, S beta, S gamma) 110 { 111 rot.FromEulerAngles(alpha, beta, gamma); 112 tra = Point3(0, 0, 0); 113 sca = 1; 114 } 115 116 Similarity operator*(const Similarity &affine) const; 117 Similarity &operator*=(const Similarity &affine); 118 //Point3 operator*(const Point3 &p) const; 119 120 121 Similarity &SetIdentity(); 122 Similarity &SetScale(const S s); 123 Similarity &SetTranslate(const Point3 &t); 124 ///use radiants for angle. 125 Similarity &SetRotate(S angle, const Point3 & axis); 126 Similarity &SetRotate(const RotationType &q); 127 128 Matrix44 Matrix() const; 129 Matrix44 InverseMatrix() const; 130 void FromMatrix(const Matrix44 &m); 131 132 RotationType rot; 133 Point3 tra; 134 S sca; 135 }; /usr/local/env/graphics/meshlab/vcglib/wrap/gui/trackball.cpp:: 33 Transform::Transform() { 34 track.SetIdentity(); 35 radius=1.0f; 36 center=Point3f(0,0,0); 37 } ... 109 void Trackball::Apply () { 110 glTranslate (center); 111 glMultMatrix (track.Matrix()); 112 glTranslate (-center); 113 } ... 129 // T(c) S R T(t) T(-c) => S R T(S^(-1) R^(-1)(c) + t - c) 130 Matrix44f Trackball::Matrix() const{ 131 #ifndef VCG_USE_EIGEN 132 Matrix44f r; track.rot.ToMatrix(r); 133 Matrix44f sr = Matrix44f().SetScale(track.sca, track.sca, track.sca) * r; 134 Matrix44f s_inv = Matrix44f().SetScale(1/track.sca, 1/track.sca, 1/track.sca); 135 Matrix44f t = Matrix44f().SetTranslate(s_inv*r.transpose()*center + track.tra - center); 136 137 return Matrix44f(sr*t); 138 #else 139 Eigen::Quaternionf rot(track.rot); 140 Eigen::Translation3f tr( (1/track.sca) * (rot.inverse() * center) + track.tra - center ); 141 return ( Eigen::Scaling3f(track.sca) * (rot * tr) ).matrix(); 142 #endif 143 } 144