Forráskód Böngészése

separate camera class

Former-commit-id: 17a9f1508dcee8c3cbabb554ac339305bef9a8f0
Alec Jacobson (jalec 11 éve
szülő
commit
4874f0e624
3 módosított fájl, 343 hozzáadás és 232 törlés
  1. 280 0
      examples/camera/Camera.h
  2. 61 230
      examples/camera/example.cpp
  3. 2 2
      include/igl/ReAntTweakBar.cpp

+ 280 - 0
examples/camera/Camera.h

@@ -0,0 +1,280 @@
+#include <Eigen/Geometry>
+#include <Eigen/Core>
+
+class Camera
+{
+  public:
+    //  m_zoom   Zoom of camera lens {1}
+    //  m_angle  Field of view angle in degrees {45}
+    //  m_aspect  Aspect ratio {1}
+    //  m_near  near clipping plane {1e-2}
+    //  m_far  far clipping plane {100}
+    //  m_at_dist  distance of looking at point {1}
+    //  m_rotation  Rotation part of rigid transformation of camera {identity}.
+    //    Note that this seems to the inverse of what's stored in the old
+    //    igl::Camera class.
+    //  m_translation  Translation part of rigid transformation of camera
+    //    {(0,0,1)}
+    double m_zoom, m_angle, m_aspect, m_near, m_far, m_at_dist;
+    Eigen::Quaterniond m_rotation;
+    Eigen::Vector3d m_translation;
+    Camera();
+    // Return projection matrix that takes relative camera coordinates and
+    // transforms it to viewport coordinates
+    //
+    // Note:
+    //
+    //     gluPerspective(m_angle,m_aspect,m_near,m_far);
+    //
+    // Is equivalent to
+    //
+    //     glMultMatrixd(v_camera.projection().data());
+    //
+    Eigen::Matrix4d projection() const;
+    // Return an Affine transformation (rigid actually) that takes a world 3d coordinate and
+    // transforms it into the relative camera coordinates.
+    Eigen::Affine3d affine() const;
+    // Return an Affine transformation (rigid actually) that takes relative
+    // coordinates and tramsforms them into world 3d coordinates.
+    //
+    // Note:
+    //
+    //     gluLookAt(
+    //       eye()(0), eye()(1), eye()(2),
+    //       at()(0), at()(1), at()(2),
+    //       up()(0), up()(1), up()(2));
+    //
+    // Is equivalent to
+    //
+    //     glMultMatrixd(camera.affine().matrix().data());
+    //
+    // See also: affine, eye, at, up
+    Eigen::Affine3d inverse() const;
+    // Returns world coordinates position of center or "eye" of camera.
+    Eigen::Vector3d eye() const;
+    // Returns world coordinate position of a point "eye" is looking at.
+    Eigen::Vector3d at() const;
+    // Returns world coordinate unit vector of "up" vector
+    Eigen::Vector3d up() const;
+    // Return top right corner of unit plane in relative coordinates, that is
+    // (w/2,h/2,1)
+    Eigen::Vector3d unit_plane() const;
+    // Move dv in the relative coordinate frame of the camera (move the FPS)
+    //
+    // Inputs:
+    //   dv  (x,y,z) displacement vector
+    //
+    void dolly(const Eigen::Vector3d & dv);
+    // "Scale zoom": Move `eye`, but leave `at`
+    //
+    // Input:
+    //   s  amount to scale distance to at
+    void push_away(const double s);
+    // Aka "Hitchcock", "Vertigo", "Spielberg" or "Trombone" zoom:
+    // simultaneously dolly while changing angle so that `at` not only stays
+    // put in relative coordinates but also projected coordinates. That is
+    //
+    // Inputs:
+    //   da  change in angle in degrees
+    void dolly_zoom(const double da);
+    // Turn around eye so that rotation is now q
+    //
+    // Inputs:
+    //   q  new rotation as quaternion
+    void turn_eye(const Eigen::Quaterniond & q);
+    // Orbit around at so that rotation is now q
+    //
+    // Inputs:
+    //   q  new rotation as quaternion
+    void orbit(const Eigen::Quaterniond & q);
+    // Rotate and translate so that camera is situated at "eye" looking at "at"
+    // with "up" pointing up.
+    //
+    // Inputs:
+    //   eye  (x,y,z) coordinates of eye position
+    //   at   (x,y,z) coordinates of at position
+    //   up   (x,y,z) coordinates of up vector
+    void look_at(
+      const Eigen::Vector3d & eye,
+      const Eigen::Vector3d & at,
+      const Eigen::Vector3d & up);
+};
+
+// Implementation
+#include <igl/PI.h>
+#include <igl/EPS.h>
+#include <cmath>
+#include <cassert>
+
+Camera::Camera():
+  m_zoom(1),m_angle(45.0),m_aspect(1),m_near(1e-2),m_far(100),m_at_dist(1),
+  m_rotation(1,0,0,0),
+  m_translation(0,0,1)
+{
+}
+
+Eigen::Matrix4d Camera::projection() const
+{
+  Eigen::Matrix4d P;
+  using namespace std;
+  using namespace igl;
+  // http://stackoverflow.com/a/3738696/148668
+  const double yScale = tan(PI*0.5 - 0.5*m_angle*PI/180.);
+  // http://stackoverflow.com/a/14975139/148668
+  const double xScale = yScale/m_aspect;
+  P<< 
+    xScale, 0, 0, 0,
+    0, yScale, 0, 0,
+    0, 0, -(m_far+m_near)/(m_far-m_near), -1,
+    0, 0, -2.*m_near*m_far/(m_far-m_near), 0;
+  return P.transpose();
+}
+
+Eigen::Affine3d Camera::affine() const
+{
+  using namespace Eigen;
+  Affine3d t = Affine3d::Identity();
+  t.rotate(m_rotation);
+  t.translate(m_translation);
+  return t;
+}
+
+Eigen::Affine3d Camera::inverse() const
+{
+  using namespace Eigen;
+  Affine3d t = Affine3d::Identity();
+  t.translate(-m_translation);
+  t.rotate(m_rotation.conjugate());
+  return t;
+}
+
+Eigen::Vector3d Camera::eye() const
+{
+  using namespace Eigen;
+  return affine() * Vector3d(0,0,0);
+}
+
+Eigen::Vector3d Camera::at() const
+{
+  using namespace Eigen;
+  return affine() * (Vector3d(0,0,-1)*m_at_dist);
+}
+
+Eigen::Vector3d Camera::up() const
+{
+  using namespace Eigen;
+  Affine3d t = Affine3d::Identity();
+  t.rotate(m_rotation);
+  return t * Vector3d(0,1,0);
+}
+
+Eigen::Vector3d Camera::unit_plane() const
+{
+  using namespace igl;
+  // Distance of center pixel to eye
+  const double d = 1.0;
+  const double a = m_aspect;
+  const double theta = m_angle*PI/180.;
+  const double w =
+    2.*sqrt(-d*d/(a*a*pow(tan(0.5*theta),2.)-1.))*a*tan(0.5*theta);
+  const double h = w/a;
+  return Eigen::Vector3d(w*0.5,h*0.5,-d);
+}
+
+void Camera::dolly(const Eigen::Vector3d & dv)
+{
+  m_translation += dv;
+}
+
+void Camera::push_away(const double s)
+{
+  using namespace Eigen;
+  using namespace igl;
+#ifndef NDEBUG
+  Vector3d old_at = at();
+#endif
+  const double old_at_dist = m_at_dist;
+  m_at_dist = old_at_dist * s;
+  dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
+  assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
+}
+
+void Camera::dolly_zoom(const double da)
+{
+  using namespace std;
+  using namespace igl;
+  using namespace Eigen;
+#ifndef NDEBUG
+  Vector3d old_at = at();
+#endif
+  const double old_angle = m_angle;
+  m_angle += da;
+  m_angle = min(89.,max(5.,m_angle));
+  const double s = 
+    (2.*tan(old_angle/2./180.*M_PI)) /
+    (2.*tan(m_angle/2./180.*M_PI)) ;
+  const double old_at_dist = m_at_dist;
+  m_at_dist = old_at_dist * s;
+  dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
+  assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
+}
+
+void Camera::turn_eye(const Eigen::Quaterniond & q)
+{
+  using namespace Eigen;
+  using namespace igl;
+  Vector3d old_eye = eye();
+  // eye should be fixed
+  //
+  // eye_1 = R_1 * t_1 = eye_0
+  // t_1 = R_1' * eye_0
+  m_rotation = q;
+  m_translation = m_rotation.conjugate() * old_eye;
+  assert((old_eye - eye()).squaredNorm() < DOUBLE_EPS);
+}
+
+void Camera::orbit(const Eigen::Quaterniond & q)
+{
+  using namespace Eigen;
+  using namespace igl;
+#ifndef NDEBUG
+  Vector3d old_at = at();
+#endif
+  // at should be fixed
+  //
+  // at_1 = R_1 * t_1 - R_1 * z = at_0
+  // t_1 = R_1' * (at_0 + R_1 * z)
+  m_rotation = q;
+  m_translation = 
+    m_rotation.conjugate() * 
+      (old_at + 
+         m_rotation * Vector3d(0,0,1) * m_at_dist);
+  assert((old_at - at()).squaredNorm() < DOUBLE_EPS);
+}
+
+void Camera::look_at(
+  const Eigen::Vector3d & eye,
+  const Eigen::Vector3d & at,
+  const Eigen::Vector3d & up)
+{
+  using namespace Eigen;
+  using namespace std;
+  using namespace igl;
+  // http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
+  // Normalize vector from at to eye
+  Vector3d F = eye-at;
+  m_at_dist = F.norm();
+  F.normalize();
+  // Project up onto plane orthogonal to F and normalize
+  const Vector3d proj_up = (up-(up.dot(F))*F).normalized();
+  Quaterniond a,b;
+  a.setFromTwoVectors(Vector3d(0,0,-1),-F);
+  b.setFromTwoVectors(a*Vector3d(0,1,0),proj_up);
+  m_rotation = a*b;
+  m_translation = m_rotation.conjugate() * eye;
+  assert(           (eye-this->eye()).squaredNorm() < DOUBLE_EPS);
+  assert((F-(this->eye()-this->at()).normalized()).squaredNorm() < 
+    DOUBLE_EPS);
+  assert(           (at-this->at()).squaredNorm() < DOUBLE_EPS);
+  assert(        (proj_up-this->up()).squaredNorm() < DOUBLE_EPS);
+}

+ 61 - 230
examples/camera/example.cpp

@@ -1,4 +1,7 @@
+#include "Camera.h"
+
 #include <igl/Viewport.h>
+#include <igl/matlab_format.h>
 #include <igl/report_gl_error.h>
 #include <igl/ReAntTweakBar.h>
 #include <igl/trackball.h>
@@ -6,6 +9,10 @@
 #include <igl/PI.h>
 #include <igl/EPS.h>
 #include <igl/get_seconds.h>
+#include <igl/material_colors.h>
+#include <igl/draw_mesh.h>
+#include <igl/readOBJ.h>
+#include <igl/per_face_normals.h>
 #include <igl/draw_floor.h>
 
 #include <Eigen/Core>
@@ -22,227 +29,6 @@
 #include <iostream>
 #include <algorithm>
 
-class Camera
-{
-  public:
-    //  m_zoom   Zoom of camera lens {1}
-    //  m_angle  Field of view angle in degrees {45}
-    //  m_aspect  Aspect ratio {1}
-    //  m_near  near clipping plane {1e-2}
-    //  m_far  far clipping plane {100}
-    //  m_at_dist  distance of looking at point {1}
-    //  m_rotation  Rotation part of rigid transformation of camera {identity}.
-    //    Note that this seems to the inverse of what's stored in the old
-    //    igl::Camera class.
-    //  m_translation  Translation part of rigid transformation of camera
-    //    {(0,0,1)}
-    double m_zoom, m_angle, m_aspect, m_near, m_far, m_at_dist;
-    Eigen::Quaterniond m_rotation;
-    Eigen::Vector3d m_translation;
-    Camera():
-      m_zoom(1),m_angle(45.0),m_aspect(1),m_near(1e-2),m_far(100),m_at_dist(1),
-      m_rotation(1,0,0,0),
-      m_translation(0,0,1)
-    {
-    }
-
-    // Return projection matrix that takes relative camera coordinates and
-    // transforms it to viewport coordinates
-    Eigen::Matrix4d projection() const
-    {
-      Eigen::Matrix4d P;
-      using namespace std;
-      using namespace igl;
-      // http://stackoverflow.com/a/3738696/148668
-      const double yScale = tan(PI*0.5 - 0.5*m_angle*PI/180.);
-      // http://stackoverflow.com/a/14975139/148668
-      const double xScale = yScale/m_aspect;
-      P<< 
-        xScale, 0, 0, 0,
-        0, yScale, 0, 0,
-        0, 0, -(m_far+m_near)/(m_far-m_near), -1,
-        0, 0, -2.*m_near*m_far/(m_far-m_near), 0;
-      return P.transpose();
-    }
-
-    // Return an Affine transformation that takes a world 3d coordinate and
-    // transforms it into the relative camera coordinates.
-    Eigen::Affine3d affine() const
-    {
-      using namespace Eigen;
-      Affine3d t = Affine3d::Identity();
-      t.rotate(m_rotation);
-      t.translate(m_translation);
-      return t;
-    }
-
-    // Returns world coordinates position of center or "eye" of camera.
-    Eigen::Vector3d eye() const
-    {
-      using namespace Eigen;
-      return affine() * Vector3d(0,0,0);
-    }
-
-    // Returns world coordinate position of a point "eye" is looking at.
-    Eigen::Vector3d at() const
-    {
-      using namespace Eigen;
-      return affine() * (Vector3d(0,0,-1)*m_at_dist);
-    }
-
-    // Returns world coordinate unit vector of "up" vector
-    Eigen::Vector3d up() const
-    {
-      using namespace Eigen;
-      Affine3d t = Affine3d::Identity();
-      t.rotate(m_rotation);
-      return t * Vector3d(0,1,0);
-    }
-
-    // Move dv in the relative coordinate frame of the camera (move the FPS)
-    //
-    // Inputs:
-    //   dv  (x,y,z) displacement vector
-    //
-    void dolly(const Eigen::Vector3d & dv)
-    {
-      m_translation += dv;
-    }
-
-    // "Scale zoom": Move `eye`, but leave `at`
-    //
-    // Input:
-    //   s  amount to scale distance to at
-    void push_away(const double s)
-    {
-      using namespace Eigen;
-      using namespace igl;
-#ifndef NDEBUG
-      Vector3d old_at = at();
-#endif
-      const double old_at_dist = m_at_dist;
-      m_at_dist = old_at_dist * s;
-      dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
-      assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
-    }
-
-    // Turn around eye so that rotation is now q
-    //
-    // Inputs:
-    //   q  new rotation as quaternion
-    void turn_eye(const Eigen::Quaterniond & q)
-    {
-      using namespace Eigen;
-      using namespace igl;
-      Vector3d old_eye = eye();
-      // eye should be fixed
-      //
-      // eye_1 = R_1 * t_1 = eye_0
-      // t_1 = R_1' * eye_0
-      m_rotation = q;
-      m_translation = m_rotation.conjugate() * old_eye;
-      assert((old_eye - eye()).squaredNorm() < DOUBLE_EPS);
-    }
-
-    // Orbit around at so that rotation is now q
-    //
-    // Inputs:
-    //   q  new rotation as quaternion
-    void orbit(const Eigen::Quaterniond & q)
-    {
-      using namespace Eigen;
-      using namespace igl;
-#ifndef NDEBUG
-      Vector3d old_at = at();
-#endif
-      // at should be fixed
-      //
-      // at_1 = R_1 * t_1 - R_1 * z = at_0
-      // t_1 = R_1' * (at_0 + R_1 * z)
-      m_rotation = q;
-      m_translation = 
-        m_rotation.conjugate() * 
-          (old_at + 
-             m_rotation * Vector3d(0,0,1) * m_at_dist);
-      assert((old_at - at()).squaredNorm() < DOUBLE_EPS);
-    }
-
-    // Aka "Hitchcock", "Vertigo", "Spielberg" or "Trombone" zoom:
-    // simultaneously dolly while changing angle so that `at` not only stays
-    // put in relative coordinates but also projected coordinates. That is
-    //
-    // Inputs:
-    //   da  change in angle in degrees
-    void dolly_zoom(const double da)
-    {
-      using namespace std;
-      using namespace igl;
-      using namespace Eigen;
-#ifndef NDEBUG
-      Vector3d old_at = at();
-#endif
-      const double old_angle = m_angle;
-      m_angle += da;
-      m_angle = min(89.,max(5.,m_angle));
-      const double s = 
-        (2.*tan(old_angle/2./180.*M_PI)) /
-        (2.*tan(m_angle/2./180.*M_PI)) ;
-      const double old_at_dist = m_at_dist;
-      m_at_dist = old_at_dist * s;
-      dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
-      assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
-    }
-
-    // Return top right corner of unit plane in relative coordinates, that is
-    // (w/2,h/2,1)
-    Eigen::Vector3d unit_plane() const
-    {
-      using namespace igl;
-      // Distance of center pixel to eye
-      const double d = 1.0;
-      const double a = m_aspect;
-      const double theta = m_angle*PI/180.;
-      const double w =
-        2.*sqrt(-d*d/(a*a*pow(tan(0.5*theta),2.)-1.))*a*tan(0.5*theta);
-      const double h = w/a;
-      return Eigen::Vector3d(w*0.5,h*0.5,-d);
-    }
-
-    // Rotate and translate so that camera is situated at "eye" looking at "at"
-    // with "up" pointing up.
-    //
-    // Inputs:
-    //   eye  (x,y,z) coordinates of eye position
-    //   at   (x,y,z) coordinates of at position
-    //   up   (x,y,z) coordinates of up vector
-    void look_at(
-      const Eigen::Vector3d & eye,
-      const Eigen::Vector3d & at,
-      const Eigen::Vector3d & up)
-    {
-      using namespace Eigen;
-      using namespace std;
-      using namespace igl;
-      // http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
-      // Normalize vector from at to eye
-      Vector3d F = eye-at;
-      m_at_dist = F.norm();
-      F.normalize();
-      // Project up onto plane orthogonal to F and normalize
-      const Vector3d proj_up = (up-(up.dot(F))*F).normalized();
-      Quaterniond a,b;
-      a.setFromTwoVectors(Vector3d(0,0,-1),-F);
-      b.setFromTwoVectors(a*Vector3d(0,1,0),proj_up);
-      m_rotation = a*b;
-      m_translation = m_rotation.conjugate() * eye;
-      assert(           (eye-this->eye()).squaredNorm() < DOUBLE_EPS);
-      assert((F-(this->eye()-this->at()).normalized()).squaredNorm() < 
-        DOUBLE_EPS);
-      assert(           (at-this->at()).squaredNorm() < DOUBLE_EPS);
-      assert(        (proj_up-this->up()).squaredNorm() < DOUBLE_EPS);
-    }
-
-};
 
 enum RotationType
 {
@@ -280,6 +66,9 @@ bool is_rotating = false;
 Camera down_camera;
 int down_x,down_y;
 std::stack<State> redo_stack;
+Eigen::MatrixXd V,N;
+Eigen::MatrixXi F;
+Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
 
 void push_undo()
 {
@@ -402,6 +191,24 @@ void reshape(int width, int height)
 }
 
 
+// Set up double-sided lights
+void lights()
+{
+  using namespace std;
+  using namespace Eigen;
+  glEnable(GL_LIGHTING);
+  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+  glEnable(GL_LIGHT0);
+  float WHITE[4] =  {0.8,0.8,0.8,1.};
+  float GREY[4] =  {0.4,0.4,0.4,1.};
+  float BLACK[4] =  {0.,0.,0.,1.};
+  Vector4f pos = light_pos;
+  glLightfv(GL_LIGHT0,GL_AMBIENT,GREY);
+  glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE);
+  glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK);
+  glLightfv(GL_LIGHT0,GL_POSITION,pos.data());
+}
+
 void draw_scene(const Camera & v_camera,
   const bool render_to_texture,
   const GLuint & v_tex_id, 
@@ -428,11 +235,14 @@ void draw_scene(const Camera & v_camera,
   glMultMatrixd(v_camera.projection().data());
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
+  //glLoadIdentity();
+  //gluLookAt(
+  //  v_camera.eye()(0), v_camera.eye()(1), v_camera.eye()(2),
+  //  v_camera.at()(0), v_camera.at()(1), v_camera.at()(2),
+  //  v_camera.up()(0), v_camera.up()(1), v_camera.up()(2));
   glLoadIdentity();
-  gluLookAt(
-    v_camera.eye()(0), v_camera.eye()(1), v_camera.eye()(2),
-    v_camera.at()(0), v_camera.at()(1), v_camera.at()(2),
-    v_camera.up()(0), v_camera.up()(1), v_camera.up()(2));
+  glMultMatrixd(v_camera.inverse().matrix().data());
+
 
   for(int c = 0;c<(int)s.cameras.size();c++)
   {
@@ -512,13 +322,26 @@ void draw_scene(const Camera & v_camera,
     glPopMatrix();
   }
 
+  // Set material properties
+  lights();
+  glEnable(GL_LIGHTING);
+  glDisable(GL_COLOR_MATERIAL);
+  glMaterialfv(GL_FRONT, GL_AMBIENT,  GOLD_AMBIENT);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE,  GOLD_DIFFUSE  );
+  glMaterialfv(GL_FRONT, GL_SPECULAR, GOLD_SPECULAR);
+  glMaterialf (GL_FRONT, GL_SHININESS, 128);
+  glMaterialfv(GL_BACK, GL_AMBIENT,  SILVER_AMBIENT);
+  glMaterialfv(GL_BACK, GL_DIFFUSE,  FAST_GREEN_DIFFUSE  );
+  glMaterialfv(GL_BACK, GL_SPECULAR, SILVER_SPECULAR);
+  glMaterialf (GL_BACK, GL_SHININESS, 128);
+  draw_mesh(V,F,N);
   glDisable(GL_LIGHTING);
   glEnable(GL_COLOR_MATERIAL);
-  glLineWidth(3.f);
-  glColor4f(1,0,1,1);
-  glutWireCube(0.25);
-  glColor4f(1,0.5,0.5,1);
-  //glutWireSphere(0.125,20,20);
+  //glLineWidth(3.f);
+  //glColor4f(1,0,1,1);
+  //glutWireCube(0.25);
+  //glColor4f(1,0.5,0.5,1);
+  ////glutWireSphere(0.125,20,20);
   {
     glPushMatrix();
     glTranslated(0,-1,0);
@@ -818,6 +641,14 @@ int main(int argc, char * argv[])
   cout<<"[Shift+Command+Z]   Redo."<<endl;
   cout<<"[^C,ESC]            Exit."<<endl;
 
+  if(!readOBJ("../shared/cheburashka.obj",V,F))
+  {
+    cerr<<"Failed to read in mesh..."<<endl;
+    return 1;
+  }
+  V.rowwise() -= V.colwise().minCoeff().eval();
+  per_face_normals(V,F,N);
+
   // Init glut
   glutInit(&argc,argv);
   if( !TwInit(TW_OPENGL, NULL) )

+ 2 - 2
include/igl/ReAntTweakBar.cpp

@@ -352,7 +352,7 @@ IGL_INLINE std::string igl::ReTwBar::get_value_as_string(
         // Q: Why does casting to double* work? shouldn't I have to cast to
         // double**?
         double * q = static_cast<double*>(var);
-        sstr << q[0] << " " << q[1] << " " << q[2] << " " << q[3];
+        sstr << std::setprecision(15) << q[0] << " " << q[1] << " " << q[2] << " " << q[3];
         break;
       }
     case TW_TYPE_QUAT4F:
@@ -382,7 +382,7 @@ IGL_INLINE std::string igl::ReTwBar::get_value_as_string(
       {
         sstr << "TW_TYPE_DIR3D" << " ";
         double * d = static_cast<double*>(var);
-        sstr << d[0] << " " << d[1] << " " << d[2];
+        sstr << std::setprecision(15) << d[0] << " " << d[1] << " " << d[2];
         break;
       }
     case TW_TYPE_DIR3F: