Thursday, July 26, 2012

Qt Interface for Modifying Parameters in Real-Time

TL;DR Made some cool enhancements to the open source RTPS library checkout the video.

Improved rendering and Qt interface for interactivity from Andrew Young on Vimeo.


I bring some exciting news about my latest developments in the Real-time Particle System (RTPS) library. I created an interface which allows users to modify the system and graphics parameters in real time. I have also improve the screen-space rendering implementation. All the rendering code and shaders are now OpenGL 3.3 compliant.

I have decided to finish graduate school with a M.S. rather than pursuing a PhD. In order to finish my M.S. thesis, I wanted to add an interface to the RTPS library which allows users to modify the parameters in real-time. I chose the Qt library to accomplish the task. Qt works across many platforms which makes it an excellent choice. Qt has a large community and extensive documentation.  Also, they have a license which allows for inclusion into an open-source projects.

Beginning development in Qt wasn't too difficult. Qt has several example applications which use OpenGL widgets. I used these examples as a starting point. However, my venture into Qt was not without it's troubles. One problem I soon ran into was attempting to use GLEW with Qt. I found several posts about the problem and most users suggested the solution was to use QGLWidget instead of QOpenGL. That was indeed part of the problem. The other problem was issue with Qt 4.8. Apparently, when initializing the QGLWidget, but before calling glewinit(), you must call makeCurrent(). My understanding of the problem is limited. However, I believe the problem stems from Qt 4.8's introduction of multi-threaded OpenGL. Without calling make current the GLcontext doesn't become active.

Many of the parameters in the RTPS library are floating point numbers. I wanted a slider which would return floating point numbers. To my surprise, Qt has no native support for a float slider bars. All the solutions I found on the internet suggested simply dividing the result by some scaling factor. The solution made sense. Of course, the approach is rather inconvenient because I can't attach multiple slider signals to a single slot unless the scaling factor was the same on each slot. I decided the best approach was to inherit from the QSlider class to create my own FloatSlider class. Overriding the Qt class was far easier than expected.

//--------- floatslider.h ----------
#ifndef FLOATSLIDER_H
#define FLOATSLIDER_H
#include 

class FloatSlider : public QSlider
{
    Q_OBJECT
public:
    FloatSlider(Qt::Orientation orientation, QWidget* parent = 0);
    void setScale(float scale);
public slots:
    void sliderChange(SliderChange change);
    void setValue(float value);
signals:
    void valueChanged(float value);
private:
    float scale;
};
#endif


//---------- floatslider.cpp ------------
#include "floatslider.h"

FloatSlider::FloatSlider(Qt::Orientation orientation, QWidget* parent)
:QSlider(orientation, parent)
{
    scale =1.0f;
}
void FloatSlider::setValue(float value)
{
    QSlider::setValue(value/scale);
}
void FloatSlider::setScale(float scale)
{
    this->scale=scale;
}
void FloatSlider::sliderChange(SliderChange change)
{
    if(change==QAbstractSlider::SliderValueChange)
    {
         emit valueChanged(value()*scale);
    }
    //for completeness. Also, one could send integer and float signals
    //if desired.
    QSlider::sliderChange(change);
}



The above class signal sends a float upon sliderChange. Therefore you can set a custom scale for the float slider and will be multiplied by the scaling factor. For example, the following code will return a number between 0.01 and 1.00 by increments of 0.01.

xSPHSlider = new FloatSlider(orientation,this);
xSPHSlider->setObjectName("xsph_factor");
xSPHSlider->setTickPosition(QSlider::TicksBelow);
xSPHSlider->setTickInterval(10);
xSPHSlider->setSingleStep(1);
xSPHSlider->setRange(1,100);
xSPHSlider->setValue(15);
xSPHSlider->setScale(0.01);

connect(xSPHSlider,SIGNAL(valueChanged(float)),this,SLOT(triggerValue(float)));


1 comment:

  1. Would be awesome to see this in Blender some day...

    ReplyDelete