Links

Content Skeleton

This Page

Previous topic

MeshLab Collada Materials

Next topic

MeshLab Distribution

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:

  1. calculate bbox during import
  2. transform the bbox min max with the appropriate transform,
  3. keep all the boxes in a structure keyed by node id
  4. 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<MeshDecorateInterface *>(plugin);
./meshlab/glarea.cpp:                   MeshDecorateInterface * decorInterface = qobject_cast<MeshDecorateInterface *>(p->parent());
./meshlab/glarea.cpp:           MeshDecorateInterface * decorInterface = qobject_cast<MeshDecorateInterface *>(p->parent());
./meshlab/layerDialog.cpp:              MeshDecorateInterface* decPlug =  qobject_cast<MeshDecorateInterface *>(decList[ii]->parent());
./meshlab/layerDialog.cpp:      MeshDecorateInterface* decPlug =  qobject_cast<MeshDecorateInterface *>(act->parent());
./meshlab/mainwindow_Init.cpp:  foreach(MeshDecorateInterface *iDecorate,PM.meshDecoratePlugins())
./meshlab/mainwindow_RunTime.cpp:       MeshDecorateInterface *iDecorateTemp = qobject_cast<MeshDecorateInterface *>(action->parent());
./meshlab/plugindialog.cpp:                MeshDecorateInterface *iDecorate = qobject_cast<MeshDecorateInterface *>(plugin);
./meshlab/plugindialog.cpp:             MeshDecorateInterface *iDecorate = qobject_cast<MeshDecorateInterface *>(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.

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:

<!DOCTYPE ViewState>
<project>
             <VCGCamera
                           TranslationVector="2047.35 397.133 2045.83 1"
                           LensDistortion="0 0"
                           ViewportPx="1678 906"
                           PixelSizeMm="0.0369161 0.0369161"
                           CenterPx="839 453"
                           FocalMm="28.9651"
                           RotationMatrix="-0.60671 -0.719133 -0.338749 0 -0.435627 0.657238 -0.615034 0 0.66493 -0.225579 -0.712027 0 0 0 0 1 "
                        />

            <ViewSettings
                          NearPlane="1.03109"
                          TrackScale="0.00729556"
                          FarPlane="13.0311"
                        />

            <Render
                          Lighting="1"
                          DoubleSideLighting="0"
                          SelectedVert="0"
                          ColorMode="0"
                          SelectedFace="0"
                          BackFaceCull="0"
                          FancyLighting="0"
                          DrawMode="5"
                          TextureMode="0"
                        />
</project>

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<Shotf>(shot,node);
1610         else if (QString::compare(node.nodeName(),"CamParam")==0)
1611             ReadShotFromOLDXML<Shotf>(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<Shotf, float> (shot,trackball.track.sca));
1641 }
....
1726 void GLArea::loadShot(const QPair<vcg::Shotf,float> &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 <class T>
485     void shot2Track(const vcg::Shot<T> &from, const float cameraDist, vcg::Trackball &tb){
486
487         vcg::Quaternion<T> 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 <class ShotType>
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<typename ShotType::ScalarType> 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<ScalarType> 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<ScalarType> &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<ScalarType> & shot, vcg::Box3<ScalarType> 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<ScalarType> & sShot,
218                           vcg::Shot<ScalarType> & shot )
219 {
220     vcg::Point3<ScalarType>     cen; cen.Import(tr.center);
221     vcg::Point3<ScalarType>     tra; tra.Import(tr.track.tra);
222     vcg::Matrix44<ScalarType>   trM; trM.FromMatrix(tr.track.Matrix());
223
224     vcg::Point3<ScalarType>     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<S> & point,const vcg::Point3<S> & 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<S> & z_dir,const vcg::Point3<S> & up);
...
330 /// look at (point+up)
331 template <class S, class RotationType>
332 void Shot<S,RotationType>::LookAt(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up)
333 {
334     LookTowards(z_dir-GetViewPoint(),up);
335 }
336
337 /// look at (opengl-like)
338 template <class S, class RotationType>
339 void Shot<S,RotationType>::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<S>(eye_x,eye_y,eye_z));
344   LookAt(Point3<S>(at_x,at_y,at_z),Point3<S>(up_x,up_y,up_z));
345 }
346
347 /// look towards
348 template <class S, class RotationType>
349 void Shot<S,RotationType>::LookTowards(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up)
350 {
351   vcg::Point3<S> x_dir = up ^-z_dir;
352   vcg::Point3<S> y_dir = -z_dir ^x_dir;
353
354   Matrix44<S> m;
355   m.SetIdentity();
356   *(vcg::Point3<S> *)&m[0][0] =  x_dir/x_dir.Norm();
357   *(vcg::Point3<S> *)&m[1][0] =  y_dir/y_dir.Norm();
358   *(vcg::Point3<S> *)&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" ]
<!DOCTYPE ViewState>
<project>
 <VCGCamera TranslationVector="2323.61 303.412 1334.53 1" LensDistortion="0 0" ViewportPx="1678 906" PixelSizeMm="0.0369161 0.0369161" CenterPx="839 453" FocalMm="28.9651" RotationMatrix="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 "/>
 <ViewSettings NearPlane="1.03109" TrackScale="0.00729556" FarPlane="13.0311"/>
 <Render Lighting="1" DoubleSideLighting="0" SelectedVert="0" ColorMode="0" SelectedFace="0" BackFaceCull="0" FancyLighting="0" DrawMode="5" TextureMode="0"/>
</project>
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 S,class RotationType = Quaternion<S> > class Similarity {
104 public:
105   Similarity() {}
106   Similarity(const RotationType &q) { SetRotate(q); }
107   Similarity(const Point3<S> &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<S>(0, 0, 0);
113         sca = 1;
114     }
115
116   Similarity operator*(const Similarity &affine) const;
117   Similarity &operator*=(const Similarity &affine);
118   //Point3<S> operator*(const Point3<S> &p) const;
119
120
121   Similarity &SetIdentity();
122   Similarity &SetScale(const S s);
123     Similarity &SetTranslate(const Point3<S> &t);
124   ///use radiants for angle.
125   Similarity &SetRotate(S angle, const Point3<S> & axis);
126   Similarity &SetRotate(const RotationType &q);
127
128   Matrix44<S> Matrix() const;
129   Matrix44<S> InverseMatrix() const;
130   void FromMatrix(const Matrix44<S> &m);
131
132   RotationType rot;
133   Point3<S> 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