27 September 2012

Tinia Tutorial 1: Hello World


This is the first tutorial of five which describes how to get started with Tinia for OpenGL applications. In this tutorial we demonstrate how to create a simple OpenGL Hello World-application in Tinia. We will draw a simple triangle and allow trackball rotation.

Familiarity with basic OpenGL and C++ with object orientation is assumed.

The program created in this tutorial will run both as a desktop program and as a server/client program.




The program consists of two files: The Job class definition and the main file. One main file will be created for the desktop program, and one for the web program.

The Job class

The main component in a Tinia program is an a subclass of the Job class. The subclass of the Job class defines the methods which will be called to interact with our program.

We subclass OpenGLJob as we're going to make an OpenGL program.

Tinia provides the convenience header tinia/tinia.hpp which includes everything we need. We also include the glew header since we're going to do OpenGL rendering. The user is free to choose whatever OpenGL wrangler he wants.


#include <tinia/tinia.hpp>
#include <GL/glew.h>


Notice our class, TutorialJob is a subclass of OpenGLJob, as we're going to do OpenGL rendering. To utilize the superclass, we need to override renderFrame. We're not using proxy geometry for this tutorial, so we don't need to reimplement getRenderList.


class Tutorial1Job : public tinia::jobcontroller::OpenGLJob {
public:
    Tutorial1Job();
    bool renderFrame( const std::string &session,
                      const std::string &key,
                      unsigned int fbo,
                      const size_t width,
                      const size_t height );
};


The ExposedModel


Every subclass of Job has an instance of ExposedModel named m_model, hereafter referenced to as the model. The model defines the variables which are exposed to the user interface. Some variables in the model will not be directly visible to user, others will typically be visible through GUI widgets such as textboxes and spinboxes.

In the constructor of Tutorial1Job we add an element of type Viewer to the model which we inherited from Job. Objects of type Viewer contains the necessary information to do OpenGL rendering. The method addElement takes two parameters: the key and the value. The key is completely user defined; the user is free to choose any string as a key, as long as the key is unique within the model. The key will later be used for looking up the value.

We also add a key with name "boundingbox". The default viewer in Tinia will look for an element with this name to find the boundingbox for the geometry. The boundingbox is specified as a string with the lower left corner first, then the upper right corner.


Tutorial1Job::Tutorial1Job()
{
    m_model->addElement( "myViewer", tinia::model::Viewer() );
    m_model->addElement("boundingbox", "0 0 0 1 1 1");
}


We're utilizing Tinia's ability to automatically generate a GUI based on the model, so we don't need to specify a GUI.

Rendering OpenGL


The method renderFrame will be called whenever there's a change in the model. This happens, for instance, when the user interacts with the OpenGL canvas using the mouse.

In our implementation of renderFrame we first obtain the Viewer object we defined in the constructor.


tinia::model::Viewer viewer;
m_model->getElementValue("myViewer", viewer);


Objects of type Viewer contain the ModelView and Projection matrices. The matrices are stored as row-major in a std::array<float, 16>. You may treat the data() any way you'd like. In this example, we hand them directly to the glLoadMatrixf function, though a more typical use would be to use them as uniform values to a shader.


glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(viewer.modelviewMatrix.data());
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(viewer.projectionMatrix.data());


The rest of the renderFrame method is just simple OpenGL rendering. Any OpenGL call is allowed, as long as the final framebuffer is rendered to the framebuffer specified by the fbo parameter.


glClearColor(0, 0, 0 ,0 );
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, width, height);
glBegin(GL_TRIANGLES);
glColor3f(1, 0, 0);
glVertex2f(0, 0);
glVertex2f(1, 0);
glVertex2f(1, 1);
glEnd();


Lastly we return true to signalize everything went OK:


return true;


The full renderFrame thus becomes


bool Tutorial1Job::renderFrame( const std::string &session,
                                const std::string &key,
                                unsigned int fbo,
                                const size_t width,
                                const size_t height )
{
    tinia::model::Viewer viewer;
    m_model->getElementValue("myViewer", viewer);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(viewer.modelviewMatrix.data());
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(viewer.projectionMatrix.data());
    glClearColor(0, 0, 0 ,0 );
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, width, height);
    glBegin(GL_TRIANGLES);
    glColor3f(1, 0, 0);
    glVertex2f(0, 0);
    glVertex2f(1, 0);
    glVertex2f(1, 1);
    glEnd();
    return true;
}


The Desktop Main File


Every Tinia program is controlled by a subclass of Controller. The controller is responsible for creating a GUI and handling interactions with the user. For desktop programs, one should use QTController.

First we include the the code for the Job we've written, then we include the desktop controller tinia/qtcontroller/QTController.hpp.


#include "Tutorial1_Job.hpp"
#include "tinia/qtcontroller/QTController.hpp"


We create an instance of our Tutorial1Job class

    tinia::tutorial::Tutorial1Job job;

We also create an instance of QTController


tinia::qtcontroller::QTController controller;


Then we need to hand the job to the controller


controller.setJob(&job);


And lastly we run the program

return controller.run(argc, argv);

The whole desktop main is then


/* Copyright STIFTELSEN SINTEF 2012
 *
 * This file is part of the Tinia Framework.
 *
 * The Tinia Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * The Tinia Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with the Tinia Framework.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Tutorial1_Job.hpp"
#include "tinia/qtcontroller/QTController.hpp"
int main(int argc, char** argv) {
    tinia::tutorial::Tutorial1Job job;
    tinia::qtcontroller::QTController controller;
    controller.setJob(&job);
    return controller.run(argc, argv);
}


Running the desktop program


Starting the program should show something similar to this:


Screenshot of the desktop job from Tutorial1.

Notice how the first line displays our boundingbox. This is a caveat of the default GUI generated by Tinia, as it will display all the elements in the model. See Tutorial 2 for how to specify your own GUI.

The Web Main File


The main file for the web application is quite similar to the dekstop main file. We only show the main differences here.

For web programs we use the IPCGLJobController as our controller.

First you need to include the tinia/trell/IPCGLJobController.hpp header file instead of the QTController header file:


#include "tinia/trell/IPCGLJobController.hpp"


Then we specify the controller:


tinia::trell::IPCGLJobController controller;


The whole main file is then


/* Copyright STIFTELSEN SINTEF 2012
 *
 * This file is part of the Tinia Framework.
 *
 * The Tinia Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * The Tinia Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with the Tinia Framework.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Tutorial1_Job.hpp"
#include "tinia/trell/IPCGLJobController.hpp"
int main(int argc, char** argv) {
    tinia::tutorial::Tutorial1Job job;
    tinia::trell::IPCGLJobController controller;
    controller.setJob(&job);
    return controller.run(argc, argv);
}


Running the web program


If you've successfully installed Tinia you should be able to run the web program as tutorial1_web through the mod_trell web interface.

The program should look something like this:
Screenshot of the web job from Tutorial1.

No comments:

Post a Comment