stevenlovegrove / Pangolin

Pangolin is a lightweight portable rapid development library for managing OpenGL display / interaction and abstracting video input.
MIT License
2.39k stars 855 forks source link

Correct way to use Manipulator #278

Open mauriliodc opened 7 years ago

mauriliodc commented 7 years ago

Hello, I needed a "low effort" manipulation gizmo for translation and rotation of objects. I used the "scene" example as boilerlate, basically I defined a Cube class (instead of the Axis one) to be added to the renderable tree.

SPOILER, Results: Video demo

From Pangolin/include/pangolin/scene/interactive.h I've modified the Manipulator class to make it looks like

struct Manipulator : public Interactive
{
    virtual void Render(const RenderParams& params) = 0;
    pangolin::OpenGlMatrix* oglptr;
};

My manipulator is:

struct Gizmo : public Manipulator
{
    Gizmo()
        : axis_length(5.0),
          label_x(InteractiveIndex::I().Store(this)),
          label_y(InteractiveIndex::I().Store(this)),
          label_z(InteractiveIndex::I().Store(this))
    {
    }

    void Render(const RenderParams&) override {
        glColor4f(1,0,0,1);
        glPushName(label_x.Id());
        glDrawLine(0,0,0, axis_length,0,0);
        glPopName();

        glColor4f(0,1,0,1);
        glPushName(label_y.Id());
        glDrawLine(0,0,0, 0,axis_length,0);
        glPopName();

        glColor4f(0,0,1,1);
        glPushName(label_z.Id());
        glDrawLine(0,0,0, 0,0,axis_length);
        glPopName();
    }

    bool Mouse(
            int button,
            const GLprecision /*win*/[3], const GLprecision /*obj*/[3], const GLprecision /*normal*/[3],
    bool /*pressed*/, int button_state, int pickId
    ) override
    {
#ifdef HAVE_EIGEN
        if((button == MouseWheelUp || button == MouseWheelDown) ) {
            float scale = (button == MouseWheelUp) ? 0.01f : -0.01f;
            if(button_state & KeyModifierShift) scale /= 10;
            Eigen::Vector3d rot = Eigen::Vector3d::Zero();
            Eigen::Vector3d xyz = Eigen::Vector3d::Zero();

            if(button_state & KeyModifierCtrl) {
                // rotate
                if(pickId == label_x.Id()) {
                    rot << 1,0,0;
                }else if(pickId == label_y.Id()) {
                    rot << 0,1,0;
                }else if(pickId == label_z.Id()) {
                    rot << 0,0,1;
                }else{
                    return false;
                }
            }else if(button_state & KeyModifierShift){
                // translate
                if(pickId == label_x.Id()) {
                    xyz << 1,0,0;
                }else if(pickId == label_y.Id()) {
                    xyz << 0,1,0;
                }else if(pickId == label_z.Id()) {
                    xyz << 0,0,1;
                }else{
                    return false;
                }
            }else{
                return false;
            }

            // old from new
            Eigen::Matrix<double,4,4> T_on = Eigen::Matrix<double,4,4>::Identity();
            T_on.block<3,3>(0,0) = Eigen::AngleAxis<double>(scale,rot).toRotationMatrix();
            T_on.block<3,1>(0,3) = scale*xyz;

            // Update
            *oglptr = (ToEigen<double>(*oglptr) * T_on.inverse()).eval();

            return true;
        }
#endif // HAVE_EIGEN

        return false;
    }

    virtual bool MouseMotion(
            const GLprecision /*win*/w[3], const GLprecision /*obj*/[3], const GLprecision /*normal*/[3],
    int button_state, int pickId
    ) override
    {
        if(prev_w==-1){
            prev_w=w[0];
        }
        float scale = (prev_w < w[0]) ? 0.01f : -0.01f;
        prev_w=w[0];
        Eigen::Vector3d rot = Eigen::Vector3d::Zero();
        Eigen::Vector3d xyz = Eigen::Vector3d::Zero();
        if(button_state & KeyModifierCtrl) {
            // rotate
            if(pickId == label_x.Id()) {
                rot << 1,0,0;
            }else if(pickId == label_y.Id()) {
                rot << 0,1,0;
            }else if(pickId == label_z.Id()) {
                rot << 0,0,1;
            }else{
                return false;
            }
        }else if(button_state & KeyModifierShift){
            // translate
            if(pickId == label_x.Id()) {
                xyz << 1,0,0;
            }else if(pickId == label_y.Id()) {
                xyz << 0,1,0;
            }else if(pickId == label_z.Id()) {
                xyz << 0,0,1;
            }else{
                return false;
            }
        }else{
            return false;
        }
        Eigen::Matrix<double,4,4> T_on = Eigen::Matrix<double,4,4>::Identity();
        T_on.block<3,3>(0,0) = Eigen::AngleAxis<double>(scale,rot).toRotationMatrix();
        T_on.block<3,1>(0,3) = scale*xyz;
        // Update
        *oglptr = (ToEigen<double>(*oglptr) * T_on.inverse()).eval();

    }

    float axis_length;
    const InteractiveIndex::Token label_x;
    const InteractiveIndex::Token label_y;
    const InteractiveIndex::Token label_z;
    int prev_w=-1;
};

Thus I can:

pangolin::Renderable tree;
 std::shared_ptr<pangolin::Cube> c = std::make_shared<pangolin::Cube>();
  c->manipulator = std::make_shared<pangolin::Gizmo>();
  c->manipulator->oglptr=&(c->T_pc);
 tree.Add(c);

Results: Video demo

Now, it's a quick and dirty solution which works but, I wonder:

stevenlovegrove commented 7 years ago

The 'Scene' stuff is a bit of a work-in-progress. The idea of the manipulator is that it would allow a visual method of interaction for objects in the scene graph, perhaps only visible when ctrl or shift is held down, or something. The logic may not all be hooked up right now for it to function as intended.