Trackball.cc
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:22k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. //############################################################
  2. // Trackball.cc
  3. // Kari Pulli
  4. // 03/06/97
  5. //############################################################
  6. #include <iostream.h>
  7. #include <math.h>
  8. #include "Trackball.h"
  9. #include "Xform.h"
  10. #include "Projector.h"
  11. #include "defines.h"
  12. #include "plvGlobals.h" // for spinning preference...
  13. /*
  14.  * Trackball code:
  15.  *
  16.  * Implementation of a virtual trackball.
  17.  * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
  18.  *   the August '88 issue of Siggraph's "Computer Graphics," 
  19.  *   pp. 121-129.
  20.  *
  21.  * Vector manip code:
  22.  *
  23.  * Original code from:
  24.  * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and 
  25.  * Paul Haeberli
  26.  *
  27.  * Much mucking with by:
  28.  * Gavin Bell
  29.  * Kept most maths, changed just about everything else:
  30.  * Kari Pulli
  31.  */
  32. /*
  33.    This size should really be based on the distance from the 
  34.    center of rotation to the point on the object underneath the 
  35.    mouse.  That point would then track the mouse as closely as 
  36.    possible.  This is a simple example, though, so that is left 
  37.    as an Exercise for the Programmer.
  38. */
  39. #define TRACKBALLSIZE  (0.8)
  40. // Local function prototypes (not defined in trackball.h)
  41. static float tb_project_to_sphere(float, float, float);
  42. static void normalize_quat(float [4]);
  43. //  Given an axis and angle, compute quaternion.
  44. void
  45. axis_to_quat(Pnt3 &a, float phi, float q[4])
  46. {
  47.   a.normalize();
  48.   a *= sin(phi/2.0);
  49.   q[1]=a[0]; q[2]=a[1]; q[3]=a[2];
  50.   q[0] = cos(phi/2.0);
  51. }
  52. /*
  53.    Ok, simulate a track-ball.  Project the points onto the virtual
  54.    trackball, then figure out the axis of rotation, which is the 
  55.    cross product of P1 P2 and O P1 (O is the center of the ball, 
  56.    0,0,0)
  57.    Note:  This is a deformed trackball-- is a trackball in the 
  58.    center, but is deformed into a hyperbolic sheet of rotation 
  59.    away from the center.  This particular function was chosen 
  60.    after trying out several variations.
  61.    It is assumed that the arguments to this routine are in the 
  62.    range (-1.0 ... 1.0)
  63.  */
  64. void
  65. trackball(float q[4], float p1x, float p1y, float p2x, float p2y,
  66.   TbConstraint constraint = CONSTRAIN_NONE)
  67. {
  68.   if (p1x == p2x && p1y == p2y) {
  69.     // Zero rotation 
  70.     q[0] = 1.0;
  71.     q[1] = q[2] = q[3] = 0.0;
  72.     return;
  73.   }
  74.   // First, figure out z-coordinates for projection of P1 and P2 
  75.   // to deformed sphere
  76.   Pnt3 p1(p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
  77.   Pnt3 p2(p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
  78.   
  79.   // get axis of rotation
  80.   Pnt3 a = cross(p1,p2);
  81.   // Figure out how much to rotate around that axis.
  82.   float t = dist(p1,p2) / (2.0*TRACKBALLSIZE);
  83.   
  84.   // Avoid problems with out-of-control values...
  85.   if (t > 1.0) t = 1.0;
  86.   if (t < -1.0) t = -1.0;
  87.   float phi = 2.0 * asin(t); // how much to rotate about axis
  88.   if (constraint == CONSTRAIN_X)
  89.     a[1] = a[2] = 0;
  90.   if (constraint == CONSTRAIN_Y)
  91.     a[0] = a[2] = 0;
  92.   if (constraint == CONSTRAIN_Z)
  93.     a[0] = a[1] = 0;
  94.   
  95.   axis_to_quat(a,phi,q);
  96. }
  97. //
  98. // Project an x,y pair onto a sphere of radius r OR a hyperbolic 
  99. // sheet if we are away from the center of the sphere.
  100. //
  101. static float
  102. tb_project_to_sphere(float r, float x, float y)
  103. {
  104.   float d, t, z;
  105.   
  106.   d = sqrt(x*x + y*y);
  107.   if (d < r * 0.70710678118654752440) {    // Inside sphere
  108.     z = sqrt(r*r - d*d);
  109.   } else {                                 // On hyperbola
  110.     t = r / 1.41421356237309504880;
  111.     z = t*t / d;
  112.   }
  113.   return z;
  114. }
  115. /*
  116.    Given two rotations, e1 and e2, expressed as quaternion 
  117.    rotations, figure out the equivalent single rotation and stuff
  118.    it into dest.
  119.    This routine also normalizes the result every RENORMCOUNT 
  120.    times it is called, to keep error from creeping in.
  121.    NOTE: This routine is written so that q1 or q2 may be the same
  122.    as dest (or each other).
  123. */
  124. #define RENORMCOUNT 97
  125. void
  126. add_quats(float q1[4], float q2[4], float dest[4])
  127. {
  128.   static int count=0;
  129.   Pnt3 t1(&q1[1]);
  130.   t1 *= q2[0];
  131.   
  132.   Pnt3 t2(&q2[1]);
  133.   t2 *= q1[0];
  134.   Pnt3 t3 = cross(&q1[1],&q2[1]);
  135.   t3 += t1; t3 += t2;
  136.   
  137.   // watch out: q1 or q2 may be the same as dest!
  138.   dest[0] = q1[0] * q2[0] - dot(&q1[1],&q2[1]);
  139.   dest[1] = t3[0];
  140.   dest[2] = t3[1];
  141.   dest[3] = t3[2];
  142.   if (++count > RENORMCOUNT) {
  143.     count = 0;
  144.     normalize_quat(dest);
  145.   }
  146. }
  147. /*
  148.    Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
  149.    If they don't add up to 1.0, dividing by their magnitude will
  150.    renormalize them.
  151.    
  152.    Note: See the following for more information on quaternions:
  153.    
  154.    - Shoemake, K., Animating rotation with quaternion curves, 
  155.      Computer Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254,1985.
  156.      - Pletinckx, D., Quaternion calculus as a basic tool in 
  157.        computer graphics, The Visual Computer 5, 2-13, 1989.
  158. */
  159. static void
  160. normalize_quat(float q[4])
  161. {
  162.   float mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
  163.   for (int i = 0; i < 4; i++) q[i] /= mag;
  164. }
  165. // Build a rotation matrix, given a quaternion rotation.
  166. void
  167. build_rotmatrix(float m[4][4], float q[4])
  168. {
  169.   m[0][0] = 1.0 - 2.0 * (q[2] * q[2] + q[3] * q[3]);
  170.   m[1][0] = 2.0 * (q[1] * q[2] - q[3] * q[0]);
  171.   m[2][0] = 2.0 * (q[3] * q[1] + q[2] * q[0]);
  172.   m[3][0] = 0.0;
  173.   m[0][1] = 2.0 * (q[1] * q[2] + q[3] * q[0]);
  174.   m[1][1] = 1.0 - 2.0 * (q[3] * q[3] + q[1] * q[1]);
  175.   m[2][1] = 2.0 * (q[2] * q[3] - q[1] * q[0]);
  176.   m[3][1] = 0.0;
  177.   m[0][2] = 2.0 * (q[3] * q[1] - q[2] * q[0]);
  178.   m[1][2] = 2.0 * (q[2] * q[3] + q[1] * q[0]);
  179.   m[2][2] = 1.0 - 2.0 * (q[2] * q[2] + q[1] * q[1]);
  180.   m[3][2] = 0.0;
  181.   m[0][3] = 0.0;
  182.   m[1][3] = 0.0;
  183.   m[2][3] = 0.0;
  184.   m[3][3] = 1.0;
  185. }
  186. Trackball::Trackball(void)
  187.   : spinning_enabled(true),
  188.     rotating(0), spinning(0), panning(0), zooming(0),
  189.     orthographic(false),
  190.     lastTarget(NULL)
  191. {
  192.   trackball(currQuat, 0.0, 0.0, 0.0, 0.0);
  193.   setTransScale();
  194. }
  195. void
  196. Trackball::setSize(int w, int h)
  197.   W=w; H=h; 
  198. }
  199. // c  is camera center, 
  200. // o  is wherethe camera is looking at,
  201. // up is the up vector of the camera
  202. void
  203. Trackball::setup(float _c[3], float _o[3], float _up[3],
  204.  float r, float fov, float oblique_x, float oblique_y)
  205. {
  206.   o = Pnt3(_o);
  207.   c = Pnt3(_c);
  208.   up = Pnt3(_up);
  209.   radius = r;
  210.   field_of_view = fov;
  211.   oblique_camera_offset_x = oblique_x;
  212.   oblique_camera_offset_y = oblique_y;
  213.   resetXform();
  214.   resetAxes();
  215. }
  216. void
  217. Trackball::resetXform(void)
  218. {
  219.   trans = Pnt3();
  220.   rotating = spinning = panning = zooming = 0;
  221.   constraint = CONSTRAIN_NONE;
  222.   //bUndoable = false;
  223.   trackball(currQuat, 0.0, 0.0, 0.0, 0.0);
  224.   trackball(lastQuat, 0.0, 0.0, 0.0, 0.0);  
  225. }
  226. void
  227. Trackball::newRotationCenter(float _o[3], TbObj* target)
  228. {
  229.   Pnt3 newCtr = Pnt3(_o);
  230.   float distmoved = dist(newCtr, o);
  231.   if (!distmoved)
  232.     return;
  233.   // radius used for clipping planes
  234.   radius += distmoved;
  235.   if (target) {
  236.     target->new_rotation_center(newCtr);
  237.     return;
  238.   }
  239.   Xform<float> newTrans;
  240.   newTrans.translate (trans);
  241.   newTrans.translate (o);
  242.   newTrans.rotQ (-currQuat[0], currQuat[1], currQuat[2], currQuat[3]);
  243.   newTrans.translate (-o);
  244.   newTrans.translate (newCtr);
  245.   newTrans.rotQ (currQuat[0], currQuat[1], currQuat[2], currQuat[3]);
  246.   newTrans.translate (-newCtr);
  247.   newTrans.getTranslation (trans);
  248.   Pnt3 noBasisTrans;
  249.   float tempQ[4];
  250.   getXform (tempQ, noBasisTrans);
  251.   o = newCtr;
  252.   resetAxes();
  253.   Xform<float> newBasis;
  254.   newBasis.fromFrame (u, v, z, Pnt3());
  255.   newBasis.apply (noBasisTrans, trans);
  256. }
  257. void
  258. Trackball::changeFOV(float fov)
  259. {
  260.   field_of_view = fov;
  261. }
  262. float
  263. Trackball::getFOV (void)
  264. {
  265.   return field_of_view;
  266. }
  267. void
  268. Trackball::setObliqueCamera(float x, float y)
  269. {
  270.   oblique_camera_offset_x = x;
  271.   oblique_camera_offset_y = y;
  272. }
  273. void
  274. Trackball::resetAxes(void)
  275. {
  276.   // z is from the object to camera
  277.   z = c - o;
  278.   d = z.norm();
  279.   z /= d;
  280.   // u (x vector) is up X z
  281.   u = cross(up,z);
  282.   u.normalize();
  283.   // v (y vector) is z X u
  284.   v = cross(z,u);
  285.   //clipping planes based on camera and object
  286.   setClippingPlanes (o, radius);
  287. }
  288. void
  289. Trackball::setClippingPlanes(Pnt3 bb_ctr, float bb_r)
  290. {
  291.   // calculate the radius around o
  292.   radius = bb_r + dist(bb_ctr, o);
  293.   // calculate the distance from the camera to center of rotation,
  294.   // down the z axis -- d = distance from o to c along z, then subtract
  295.   // out the translation (also projected onto z axis)
  296.   float d_oc = d - dot (trans, z);
  297.   zfar  = d_oc+radius;
  298.   znear = zfar - 2*radius;
  299.   enforceZlimits();
  300. }
  301. void
  302. Trackball::pressButton(int button, int up, int x, int y, int t,
  303.        TbObj* target)
  304. {
  305.   spinning_enabled = atoi (Tcl_GetVar (g_tclInterp, "spinningAllowed",
  306.        TCL_GLOBAL_ONLY));
  307.   lastTarget = target;
  308.   y = (H-1)-y;
  309.   if (!panning && !spinning && !zooming) {
  310.     // new interaction... save state for undo
  311.     //saveUndoPos (target);
  312.     target->save_for_undo();
  313.   }
  314.   if (button == 1) {
  315.     if (up == 0) {
  316.       if (panning) {
  317. zooming = 1;
  318. panning = 0;
  319.       } else {
  320. rotating = 1;
  321. spinning = 0;
  322.       }
  323.     } else {
  324.       assert(up == 1);
  325.       rotating = 0;
  326.       if (zooming) {
  327. panning = 1;
  328. zooming = 0;
  329. // just stopped zooming.
  330. // clipping planes changed, so translation scale needs to change
  331. resetTransScale();
  332.       }
  333.     }
  334.   } else {
  335.     assert(button == 2);
  336.     if (up == 0) {
  337.       if (rotating) {
  338. zooming = 1;
  339. rotating  = 0;
  340.       } else 
  341. panning = 1;
  342.     } else {
  343.       assert(up == 1);
  344.       panning = 0;
  345.       spinning = 0;
  346.       if (zooming) {
  347. rotating = 1;
  348. zooming = 0;
  349.       }
  350.     }
  351.   }
  352.   // check if mouse was moving as button was released -- 
  353.   // compare timestamps
  354.   // on this event and last event; 
  355.   // if there was any significant delay
  356.   // between the release and the last mousemove, then don't spin.
  357.   if (up) {
  358.     float mousedist = (x-beginx)*(x-beginx) + (y-beginy)*(y-beginy);
  359.     if (mousedist < .2*(t-begint)) { 
  360.       // mouse moving really slow: turn off spin on release
  361.       //printf ("event age: %un", t - begint);
  362.       //fflush (stdout);
  363.       spinning = 0;      // unless they were still moving
  364.     }
  365.   }
  366.   beginx = x;
  367.   beginy = y;
  368.   begint = t;
  369. #if 0 //debug
  370.   cout << "press button" << button << (up?" up":" down") << endl;
  371.   SHOW(zooming);
  372.   SHOW(panning);
  373.   SHOW(rotating);
  374.   SHOW(spinning);
  375.   SHOW(beginx);
  376.   SHOW(beginy);
  377. #endif
  378. }
  379. void
  380. Trackball::move(int x, int y, int t, TbObj *target)
  381. {
  382.   //if (target)  cout << "move obj" << endl;
  383.   //else         cout << "move cam" << endl;
  384.   y = (H-1)-y;
  385.   if (rotating) {
  386.     trackball(lastQuat,
  387.       2.0 * beginx / W - 1.0,
  388.       2.0 * beginy / H - 1.0,
  389.       2.0 * x / W - 1.0,
  390.       2.0 * y / H - 1.0,
  391.       constraint);
  392.     /*
  393.     float phi = 2*asin(lastQuat[0]);
  394.     float mag = Pnt3(lastQuat+1).norm();
  395.     cout << "lastQuat " 
  396.  << 2*asin(lastQuat[0]) << " " 
  397.  << lastQuat[1]/mag << " " 
  398.  << lastQuat[2]/mag << " " 
  399.  << lastQuat[3]/mag << endl;
  400.     */
  401.     // let spin() apply the rotation
  402.     if (target) spin();
  403.     spinning = 1;
  404.   } else {
  405.     Pnt3 dt;
  406.     if (panning) {
  407.       float factor = transScale ? transScale : ((d-trans[2])/W);
  408.       dt[0] = factor*(x-beginx);
  409.       dt[1] = factor*(y-beginy);
  410.     }
  411.     
  412.     if (zooming) {
  413.       float focalDistance = fabs(d - dot (trans, z));
  414.       if (orthographic) {
  415. dt[2] = (beginy-y) * focalDistance / 400;
  416. orthoHeight -= dt[2];
  417. if (orthoHeight <= 1.e-6)
  418.   orthoHeight = 1.e-6;
  419.       } else {
  420. float newFocalDist = focalDistance * powf(2.0, 
  421.   (y-beginy)/80.0);
  422. dt[2] = (focalDistance - newFocalDist);
  423.       }
  424.       if (target == NULL) {
  425. // moving the camera
  426. zfar  -= dt[2];
  427. znear = zfar - 2*radius;
  428. enforceZlimits();
  429.       }
  430.     }
  431.     translateInEyeCoords (dt, target, false);
  432.   }
  433.   beginx = x;
  434.   beginy = y;
  435.   begint = t;
  436. #if 0  //debug
  437.   cout << "move" << endl;
  438.   SHOW(zooming);
  439.   SHOW(panning);
  440.   SHOW(rotating);
  441.   SHOW(spinning);
  442.   SHOW(beginx);
  443.   SHOW(beginy);
  444. #endif
  445. }
  446. void
  447. Trackball::getXform (float quat[4], float t[3])
  448. {
  449.   for (int i = 0; i < 4; i++)
  450.     quat[i] = currQuat[i];
  451.   t[0] = dot (trans, u);
  452.   t[1] = dot (trans, v);
  453.   t[2] = dot (trans, z);
  454. }
  455. void
  456. Trackball::getXform(float m[4][4], float t[3])
  457. {
  458.   float q[4];
  459.   getXform (q, t);
  460.   build_rotmatrix(m, q);
  461. }
  462. void
  463. Trackball::applyXform(int bReinitMatrix)
  464. {
  465.   spin();
  466.   reapplyXform (bReinitMatrix);
  467. }
  468. void
  469. Trackball::reapplyXform (int bReinitMatrix)
  470. {
  471.   float m[4][4], t[3];
  472.   getXform(m,t);
  473.   if (bReinitMatrix) {
  474.     glMatrixMode(GL_MODELVIEW);
  475.     glLoadIdentity();
  476.   }
  477.   glTranslatef(-c[0], -c[1], -c[2]); // move the camera
  478.   glTranslatef(t[0], t[1], t[2]);    // pan and zoom
  479.   glTranslatef(o[0], o[1], o[2]);    // rotate around origin
  480.   glMultMatrixf(&m[0][0]);
  481.   glTranslatef(-o[0], -o[1], -o[2]);
  482. #if 0
  483.   if (this != &tbView)
  484.     //  return;
  485.     cout << "mesh -- ";
  486.   else
  487.     cout << "view -- ";
  488.   cout << "applyXform:nCamera is at ";
  489.   int i;
  490.   for (i = 0; i < 3; i++) cout << c[i] << "t";
  491.   cout << endl;
  492.   cout << "translated by ";
  493.   for (i = 0; i < 3; i++) cout << t[i] << "t";
  494.   cout << endl;
  495.   cout << "Object is at ";
  496.   for (i = 0; i < 3; i++) cout << o[i] << "t";
  497.   cout << endl;
  498.   cout << "rotated by" << endl;
  499.   for (i = 0; i < 4; i++)
  500.     {
  501.       for (int j = 0; j < 4; j++)
  502. cout << m[i][j] << "t";
  503.       cout << endl;
  504.     }
  505.   cout << endl;
  506. #endif
  507. }
  508. void
  509. Trackball::enable_spinning(void)
  510. {
  511.   spinning_enabled = true;
  512. }
  513. void
  514. Trackball::disable_spinning(void)
  515. {
  516.   spinning_enabled = false;
  517. }
  518. void
  519. Trackball::spin(void)
  520. {
  521.   if (rotating || isSpinning()) {
  522.     if (lastTarget == NULL) {
  523.       add_quats(lastQuat, currQuat, currQuat);
  524.     } else {
  525.       // undo view transform, apply inverse rotation, 
  526.       // redo view transform
  527.       float q[4],cq[4],lq[4];
  528.       cq[0] = -currQuat[0]; cq[1] = currQuat[1];
  529.       cq[2] = currQuat[2]; cq[3] = currQuat[3];
  530.       lq[0] = lastQuat[0]; lq[1] = lastQuat[1];
  531.       lq[2] = lastQuat[2]; lq[3] = lastQuat[3];
  532.       add_quats(cq,lq,q);
  533.       cq[0] = -cq[0];
  534.       add_quats(q,cq,q);
  535.       lastTarget->rotate(q[0],q[1],q[2],q[3], false);
  536.     }
  537.   }
  538. }
  539. /*
  540. void
  541. Trackball::undo_last (void)
  542. {
  543.   if (!bUndoable)
  544.     return;
  545.   spinning = false;
  546.   Xform<float> redo;
  547.   if (lastTarget) {
  548.     redo = lastTarget->getXform();
  549.     lastTarget->setXform (undoPosition);
  550.   } else {
  551.     redo = getUndoXform();
  552.     this->setUndoXform (undoPosition);
  553.   }
  554.   undoPosition = redo;
  555. }
  556. */
  557. bool
  558. Trackball::isSpinning (void)
  559. {
  560.   if (!spinning_enabled) return false;
  561.   return spinning && !rotating && !zooming && !panning;
  562. }
  563. bool
  564. Trackball::isManipulating (void)
  565. {
  566.   return (spinning && spinning_enabled) || rotating || zooming || panning;
  567. }
  568. void
  569. Trackball::stop (void)
  570. {
  571.   spinning = false;
  572. }
  573. // return the viewing direction
  574. Pnt3 
  575. Trackball::dir(void)
  576. {
  577.   float m[4][4];
  578.   build_rotmatrix(m, currQuat);
  579.   return Pnt3(m[0][2],m[1][2],m[2][2]);
  580. }
  581. void
  582. Trackball::getRotationCenter (float center[3])
  583. {
  584.   for (int i = 0; i < 3; i++)
  585.     center[i] = o[i];
  586. }
  587. void
  588. Trackball::getCameraCenter (float center[3])
  589. {
  590.   for (int i = 0; i < 3; i++)
  591.     center[i] = c[i];
  592. }
  593. // rotate angle_rads radians around axis: axis can be any vector,
  594. // normalization is not necessary
  595. void
  596. Trackball::rotateAroundAxis (const Pnt3& axis, float angle_rads,
  597.      TbObj* target)
  598. {
  599.   float q[4];
  600.   Pnt3 a = axis;   // mungeable copy
  601.   axis_to_quat (a, angle_rads, q);
  602.   rotateQuat (q, target);
  603. }
  604. void
  605. Trackball::rotateQuat (float q[4], TbObj* target)
  606. {
  607.   if (target == NULL) { // rotate viewer
  608.     target->save_for_undo();
  609.     add_quats (q, currQuat, currQuat);
  610.   } else {
  611.     // undo view transform, apply inverse rotation, 
  612.     // redo view transform
  613.     float *c = currQuat;
  614.     target->rotate(-c[0], c[1], c[2], c[3]);
  615.     target->rotate(q[0], q[1], q[2], q[3], false);
  616.     target->rotate(c[0], c[1], c[2], c[3], false);
  617.   }
  618. }
  619. void
  620. Trackball::constrainRotation (TbConstraint _constraint)
  621. {
  622.   constraint = _constraint;
  623. }
  624. void
  625. Trackball::getBounds (float& left, float& right, float& bottom, float& top)
  626. {
  627.   float halfdiag = tan(0.5 * M_PI/180.0 * field_of_view) * znear;
  628.   top = halfdiag * H / sqrt(W*W+H*H);
  629.   bottom = -top;
  630.   right = W/H * top;
  631.   left = -right;
  632.   right -= oblique_camera_offset_x * znear;
  633.   left -= oblique_camera_offset_x * znear;
  634.   top -= oblique_camera_offset_y * znear;
  635.   bottom -= oblique_camera_offset_y * znear;
  636. }
  637. void
  638. Trackball::getFrustum (float& left, float& right,
  639.        float& bottom, float& top,
  640.        float& _znear, float& _zfar)
  641. {
  642.   getBounds (left, right, bottom, top);
  643.   _znear = znear;
  644.   _zfar = zfar;
  645. }
  646. void
  647. Trackball::setPerspXform(void)
  648. {
  649.   float left, right, bottom, top;
  650.   getBounds (left, right, bottom, top);
  651.   glFrustum(left, right, bottom, top, znear, zfar);
  652. }
  653. float
  654. Trackball::getOrthoHeight(void)
  655. {
  656.   // if not in orthographic mode, value won't be up-to-date
  657.   if (orthographic)
  658.     return orthoHeight;
  659.   else
  660.     return sqrt (znear * zfar);
  661. }
  662. void
  663. Trackball::setOrthoXform(void)
  664. {
  665.   float aspect = (float)W/H;
  666.   
  667.   glOrtho(-orthoHeight/2*aspect, orthoHeight/2*aspect,
  668.   -orthoHeight/2, orthoHeight/2,
  669.   znear, zfar);
  670. }
  671. void
  672. Trackball::applyProjection()
  673. {
  674.   int mm;
  675.   glGetIntegerv (GL_MATRIX_MODE, &mm);
  676.   glMatrixMode(GL_PROJECTION);
  677.   glLoadIdentity();
  678.   if (orthographic)
  679.     setOrthoXform();
  680.   else
  681.     setPerspXform();
  682.   glMatrixMode ((GLenum) mm);
  683. }
  684. void
  685. Trackball::getProjection (float &pFov, float &pAspect, 
  686.   float &pNear, float &pFar)
  687. {
  688.   pFov = field_of_view;
  689.   pAspect = (float)W / H;
  690.   pNear = znear;
  691.   pFar = zfar;
  692. }
  693. void
  694. Trackball::setOrthographic (bool bOrtho)
  695. {
  696.   if (bOrtho == orthographic)
  697.     return;  // nothing to do
  698.   orthographic = bOrtho;
  699.   // need to set new camera params to a reasonable facsimile of old ones
  700.   if (orthographic) {
  701.     // going orthographic
  702.     orthoHeight = sqrt (znear * zfar);
  703.   } else {
  704.     // going perspective
  705.     //znear = orthoHeight - radius;
  706.     //zfar = orthoHeight + radius;
  707.   }
  708.   setClippingPlanes (o, radius);
  709.   enforceZlimits();
  710. }
  711. bool
  712. Trackball::getOrthographic (void)
  713. {
  714.   return orthographic;
  715. }
  716. void
  717. Trackball::enforceZlimits (void)
  718. {
  719.   // znear needs both absolute and relative caps -- absolute because it
  720.   // has to be >0, relative because it needs to be within 24 bits of zfar
  721.   // (actually, notably less than 24 bits) to maintain z-buffer resolution
  722.   if (znear < 1.e-6)  znear = 1.e-6;
  723.   if (znear < zfar / 1.e3)
  724.     znear = zfar / 1.e3;
  725.   if (zfar < znear) zfar  = znear + 1.0;
  726. }
  727. Xform<float>
  728. Trackball::getUndoXform (void)
  729. {
  730.   Xform<float> xf;
  731.   xf.fromQuaternion (currQuat, trans[0], trans[1], trans[2]);
  732.   // need to save zoom...  here's a hack.
  733.   // in the xf, members m[0][3], m[1][3], m[2][3] are unused.
  734.   xf(3,0) = znear;
  735.   xf(3,1) = zfar;
  736.   return xf;
  737. }
  738. void
  739. Trackball::setUndoXform (const Xform<float>& xf)
  740. {
  741.   // hack -- see comments under getUndoXform
  742.   znear = xf(3,0);
  743.   zfar =  xf(3,1);
  744.   xf.toQuaternion (currQuat);
  745.   xf.getTranslation (trans);
  746. }
  747. void
  748. Trackball::translateInEyeCoords (Pnt3& dt, TbObj* target, bool undoable)
  749. {
  750.   if (target) {
  751.     // moving an object
  752.     // translate the mesh instead of camera
  753.     Pnt3 tr;
  754.     tr[0] = dot (dt, u);
  755.     tr[1] = dot (dt, v);
  756.     tr[2] = dot (dt, z);
  757.     Xform<float> xf;
  758.     xf.rotQ(-currQuat[0], currQuat[1], currQuat[2], currQuat[3]);
  759.     xf(tr);
  760.     target->translate(tr[0], tr[1], tr[2], undoable);
  761.     // note that this translate should check the scene and
  762.     // set up the correct clipping planes by calling
  763.     // Trackball::setClippingPlanes(Pnt3 ctr, float r)
  764.   } else {
  765.     // moving the camera
  766.     if (undoable)
  767.       target->save_for_undo();
  768.     trans += dt;
  769.   }
  770. }
  771. /*
  772. void
  773. Trackball::saveUndoPos (TbObj* target)
  774. {
  775.   if (target)
  776.     undoPosition = target->getXform();
  777.   else
  778.     undoPosition = this->getUndoXform();
  779.   lastTarget = target;
  780.   bUndoable = true;
  781. }
  782. */
  783. void
  784. Trackball::getState (Pnt3& _c, Pnt3& _o, Pnt3& _t, float q[4],
  785.         float& _fov, float &_oblique_x, float &_oblique_y)
  786. {
  787.   _c = c;
  788.   _o = o;
  789.   _t = trans;
  790.   for (int i = 0; i < 4; i++)
  791.     q[i] = currQuat[i];
  792.   _fov = field_of_view;
  793.   _oblique_x = oblique_camera_offset_x;
  794.   _oblique_y = oblique_camera_offset_y;
  795. }
  796. void
  797. Trackball::setState (Pnt3& _t, float q[4])
  798. {
  799.   for (int i = 0; i < 4; i++)
  800.     currQuat[i] = q[i];
  801.   trans = _t;
  802. }
  803. void
  804. Trackball::zoomToRect (int x1, int y1, int x2, int y2)
  805. {
  806.   // need to translate (at focal point) in screen plane, such that
  807.   // given rect goes to center of screen, then translate perpendicular
  808.   // to screen plane to zoom in by ratio of area of rect to screen
  809.   int rectW = x2 - x1;
  810.   int rectH = y2 - y1;
  811.   int cx = x1 + rectW / 2;
  812.   int cy = y1 + rectH / 2;
  813.   int dx = W / 2 - cx;
  814.   int dy = H / 2 - cy;
  815.   float zoom = MIN ((float)W / rectW, (float)H / rectH);
  816.   // now need to translate such that in screen coords, we go (dx, dy, dz)
  817.   // where dz gives the desired zoom factor.
  818.   // copied from ::move:
  819.   Pnt3 dt;
  820.   float factor = transScale ? transScale: ((d-trans[2])/W);
  821.   dt[0] = factor*(dx);
  822.   dt[1] = factor*(dy);
  823.   dt[2] = (d - trans[2]) * (1 - 1/zoom);
  824.   // and go there!
  825.   translateInEyeCoords (dt, NULL, true);
  826.   setClippingPlanes (o, radius);
  827. }
  828. void
  829. Trackball::setTransScale (const Pnt3& clickPt)
  830. {
  831.   if (clickPt[2] != 0.) {
  832.     float zeye = znear * zfar / (clickPt[2] * (zfar - znear) - zfar);
  833.     //cerr << "setting near-click-far = " << znear << " " << zeye << " "
  834.     // << zfar << " (" << clickPt[2] << ")" << endl;
  835.     
  836.     // copied from Trackball::setPerspXform
  837.     float halfdiag = tan(0.5 * M_PI/180.0 * field_of_view) * zeye;
  838.     float top = halfdiag / sqrt(W*W + H*H);
  839.     transScale = (-2 * top);
  840.     // save point that we're using as translation reference, in world
  841.     // coords, in case we need to reproject it
  842.     Unprojector unp (this);
  843.     transScaleReference = unp (clickPt[0], clickPt[1], clickPt[2]);
  844.   } else {
  845.     transScale = 0.;
  846.     transScaleReference = Pnt3 (0, 0, 0);
  847.   }
  848. }
  849. void
  850. Trackball::resetTransScale (void)
  851. {
  852.   // recomputes transScale based on previous point passed to
  853.   // setTransScale -- useful if clipping planes change, ie after zoom.
  854.  if (transScaleReference[2]) {
  855.    // it doesn't suffice to recompute the transScale using the new
  856.    // znear/zfar and old zscale value, because the zscale value (read from 
  857.    // depth buffer) will be different for the same pixel with old and new
  858.    // clipping planes.  Need to reproject it.
  859.    Projector proj (this);
  860.    Pnt3 rep = proj (transScaleReference);
  861.    setTransScale (rep);
  862.  }
  863. }