Source

dbsdev / sources / opengl2_samples / test7_simplerenderer / GLTrackball.h

#ifndef __DBGLTrackBall__
#define __DBGLTrackBall__
// /////////////////////////////////////////////////////////////////////// //
// Copyright (c) 2012, Davide Bacchet (davide.bacchet@gmail.com)           //
// All rights reserved.                                                    //
//                                                                         //
// The software is released under the Creative Commons Attribution         //
// NonCommercial (CC BY-NC) license. The full license text is available    //
// at http://creativecommons.org/licenses/by-nc/3.0/legalcode              //
//                                                                         //
// You are free to distribute and adapt the work under the conditions:     //
// * Redistributions of source code must retain the above copyright notice //
// * You must attribute the work in the manner specified by the author     //
// * You may not use this work for commercial purposes.                    //
//                                                                         //
// Commercial Usage:                                                       //
// To use this software as part of a commercial product, you must          //
// obtain a written agreement from the author.                             //
// /////////////////////////////////////////////////////////////////////// //

// trackball manipulator; it's based on the article "Virtual Trackballs Revisited", by K.Henriksen (2002);
// a reference example can be fount at http://image.diku.dk/research/trackballs/index.html
// 2012-10-07: the code has been revisited in the projection-to-sphere section


#include "vmath.h"


/// @class GLTrackball 
/// trackball 
template <typename real_type=float>
class GLTrackball
{
public:

    /// standard constructor
    GLTrackball()
    : screenRadius(1.0)
    {
        incremRot.identity();
        currentRot.identity();
    }
    /// destructor
    virtual ~GLTrackball()
    {}
    
    /// set screen radius
    void setScreenRadius( real_type r )
    { screenRadius = r; }
    
    // mouse interaction: assume to have normalized coords in the range -1.0:1.0
    
    /// begin the dragging
    virtual void beginDrag(real_type x, real_type y)
    {
        // reset angle and axis
        angle = 0.0;
        axis = Vector3<real_type>();
        // set transf matrices
        anchorRot = currentRot;   // to keep previous transform
        // set anchor pos on sphere
        anchor = projectOnSurface(x,y);
        current = anchor;
    }
    
    /// mouse drag
    virtual void drag(real_type x, real_type y)
    {
        current = projectOnSurface(x,y);
        computeIncremental();
        currentRot = incremRot*anchorRot;
    }
    
    /// get current transformation
    Matrix4<real_type> getRotation( void ) 
    {
        return currentRot;
    }

protected:
    
    // member variables
    
    real_type           screenRadius;   ///< trackball screen radius
    Vector3<real_type>  anchor;         ///< anchor position  
    Vector3<real_type>  current;        ///< current position
    Vector3<real_type>  axis;           ///< rotation axis
    real_type           angle;          ///< rotation angle
    Matrix4<real_type>  anchorRot;   ///< transformation matrix of the anchor point
    Matrix4<real_type>  incremRot;   ///< transformation matrix of the increment
    Matrix4<real_type>  currentRot;  ///< current transformation matrix

    
    /// compute the incremental transformation
    virtual void computeIncremental(void)
    {
        axis = anchor.crossProduct(current);
        if (axis.lengthSq()<1E-8)   // no axis
            return;
        angle = atan2(axis.length(), anchor.dotProduct(current) );
        axis.normalize();
        Quaternion<real_type> incremQuat = Quaternion<real_type>::fromAxisRot(axis, angle);
        incremRot = incremQuat.transform();
    }

    /// project the given point on the sphere surface, combined with an hyperbole when dist>R/sqrt(2)
    Vector3<real_type> projectOnSurface(real_type x, real_type y)
    {
        real_type radius2 = screenRadius*screenRadius;
        real_type length2 = x*x+y*y;
        Vector3<real_type> prj(x,y,0.0f);
        if (length2<=radius2/2.0)
            prj.z = sqrt(radius2-length2);  // (x,y) inside the circle: simple pitagora here...
        else
            prj.z = radius2/(real_type)(2.0*sqrt(length2));
        
        prj.normalize();    // normalization is needed, because (x,y) can be outside the sphere; all the calculations from now on
                            // are done in a sphere surface of screenRadius 1.0
        return prj;
    }
};


typedef GLTrackball<float>  GLTrackballf;
typedef GLTrackball<double> GLTrackballd;

#endif