6.1 Framework choice
Because of the constraints that the project must be developed using C++ under Qt IDE, we used the Qt Widgets that are mature and feature rich user interface elements suitable for mostly static user interfaces. Besides, since Qt Widget are native C++ elements it is easier to merge UI with the application logic. The application UI is connected to all other parts of the application through the class Logic. The logic handles all the data interchange between the UI and algorithms. So it is possible to split the application in separate parts.
6.2 Basic elements of UI
The UI is straightforward and easy to use. There are two main parts – openGL screen and sidebar. It is possible to extract many cases of the application usage from the task:
- Load of the file
- Adjust the camera properties
- Adjust the light properties
- Adjust the displaying mode
- Calculate Laplacian Matrix and set new colors according to it
- Find the shortest path from one node to another
When you start the application, it will appear in its default layout.
The application includes these panels:
- 6.2.1 – OpenGL Screen
- 6.2.2 – Sidebar
- 18.104.22.168 – File Panel
- 22.214.171.124 – Setting Panel
- 126.96.36.199.1 – Camera Tab
- 188.8.131.52.2 – Light Tab
- 184.108.40.206.3 – Other Tab
- 220.127.116.11 – History Panel
6.2.1 – OpenGL screen
OpenGL screen is created by the QGLWidget class which is a widget for rendering OpenGL graphics. QGLWidget provides functionality for displaying OpenGL graphics integrated into a Qt application. It is very simple to use. You inherit from it and use the subclass like any other QWidget.
QGLWidget provides three convenient virtual functions that you can re-implement in your subclass to perform the typical OpenGL tasks:
- paintGL() - Renders the OpenGL scene. Gets called whenever the widget needs to be updated.
- resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets called whenever the widget has been resized.
- initializeGL() - Sets up the OpenGL rendering context, defines display lists, etc. Gets called once before the first time resizeGL() or paintGL() is called.
6.2.2 - Sidebar
18.104.22.168 - File panel
First, the user should choose the file from the computer. When you press the start button, the selected file will be loaded and displayed. You can also see that the mesh contains how many nodes and faces. If you want to take picture of the current scene. You should press the screenshot button.
22.214.171.124 – Setting panel
From this panel you can manipulate the camera, light and displaying mode. You can also compute the Laplacian Matrix and find the shortest path from one node to another.
- Camera Tab — Adjust the camera position, rotation and zoom
- Light Tab — Adjust the light position, intensity and colors
- Display Tab — Adjust the display mode
- Other Tab — Calculate the Laplacian matrix, set the color according the eigenvectors of the Laplacian matrix and calculate the shortest path from one node to another
126.96.36.199.1 – Camera tab
Moving, orbiting and zooming are key operations in Scene View navigation, so our application provides several alternative ways to perform them for maximum convenience. From this tab, you can change the position, the rotation and the zoom level of the camera. Either by writing the exact value to the text area or using the spin box’s up and down buttons.
188.8.131.52.1.1 – Camera position
Demonstrates how to position the camera so that all objects in a scene are within the view frustum while maintaining the camera's original orientation. You can control the camera's position by using the W, A, S, D keys on your keyboard or click-drag to manipulate the position of the camera.
184.108.40.206.1.1 – Camera rotation
Demonstrates how to rotate and move a camera in a 3D environment. You can control the camera's rotation by using right-click-drag to orbit the camera around the current pivot point.
220.127.116.11.1.1 – Camera zoom
When you want to make to zoom the scene view, you can adjust spin box of the zoom or you can use the mouse wheels.
18.104.22.168.2 – Light tab
From this tab, you can change the position and the intensity of the light. You can also change the color of the ambient, specular and diffusion components. It is important to understand what effect each of these types of light create on the surface of rendered 3D objects.
Surfaces can be considered to have two lighting characteristics: diffuse reflection and specular reflection. Diffuse reflection reflects light in all directions, regardless of where it came from. Specular reflection reflects more light in the mirror direction. A perfect mirror has no diffuse reflection and tons of specular reflection. Perfectly flat paint has diffuse reflection and no specular reflection. Most things are in-between. This is generally the property you'll use to set the color of a surface.
Ambient light is light that comes from all directions. Systems like OpenGL only directly simulate light coming from some light source. They don't simulate the natural occurrence of light bouncing off of other sources or being diffused by the atmosphere. Consequently, any surface that does not have a light shining on it directly is not lit at all by that light. A hack to deal with this problem is ambient light. If you set an ambient color the same as the diffuse color and use a small amount of ambient lighting, you'll be able to see all surfaces no matter where the light is.
This property sets the specular color. Note that the specular color for most surfaces is white, even if the surface is a different color.
22.214.171.124.3 – Display tab
From this tab, you can adjust the drawing mode. There are 3 types of drawing mode which are ‘General Mode’, ‘Neighborhood Mode’ and ‘Neighborhood Computation Mode’.
126.96.36.199 – General drawing mode
The first one is the general drawing mode that contains:
- Only Node Mode
- Node Normal Mode
- Node Normal with RGB Mode
- Face Normal Mode
- Face Normal with RGB Mode
188.8.131.52.1.1.1 Only node mode
A node is primarily a single point or position in 3D space. It is usually invisible in rendering and in Object mode which are Node Normal, Node Normal with RGB, Face Normal and Face Normal with RGB Modes. So you can only see the node by choosing Only Node Mode.
184.108.40.206.1.1.2 Node normal mode
To apply lighting to a terrain, either using OpenGL lights, or simulating them, it is necessary to first compute normals. A normal is a vector that defines how a surface responds to lighting, i.e. how it is lit. The amount of light reflected by a surface is proportional to the angle between the lights direction and the normal. The smaller the angle the brighter the surface will look. Normals in OpenGL can be defined per face or per node. In that drawing mode we use normals that are defined per node which means each vertex of a face has a different normal.
220.127.116.11.1.1.3 Node normal mode with RGB
It is same as ‘Node Normal Mode’, but this time during rendering you can also see the RGB color of the each node.
18.104.22.168.1.1.4 Face normal mode
A face is the highest level structure in a mesh. Faces are used to build the actual surface of the object. They are what you see when you render the mesh. A face is defined as the area between either three (triangles) or four (quadrangles) vertices, with an edge on every side. In our project we used triangles that are always flat and easy to calculate. If defining a normal per face then the normal is commonly defined as a vector which is perpendicular to the surface. In order to find a perpendicular vector to a face, two vectors coplanar with the face are needed. Afterwards the cross product will provide the normal vector, i.e. a perpendicular vector to the face. In that drawing mode we render the mesh by using faces and normals that are defined per face which means each vertex gets the same normal value.
22.214.171.124.1.1.5 Face normal mode with RGB
It is same as ‘Face Normal Mode’, but this time during rendering you can also see the RGB colors of the each face.
126.96.36.199.1.2 – Neighborhood drawing mode
The second one is the drawing mode that shows the neighborhood relationship that contains:
- Node to Node Relationship
- Node to Face Relationship
- Face to Face Relationship
188.8.131.52.1.2.1 Node to node melationship
Node to node relationship show the selected node and its neighbor nodes.
184.108.40.206.1.2.2 Node to face relationship
Node to face relationship show the selected node and its neighbor faces whose nodes contain the selected node.
220.127.116.11.1.2.3 Face to face relationship
Face to face relationship show the selected face and its neighbor faces.
18.104.22.168.1.3 – Neighborhood computation mode
The last one is the drawing mode that shows the neighborhood computation that contains:
- Node Computation
- Face Computation
22.214.171.124.3.3.1 Node computation
126.96.36.199.3.3.2 Face computation
188.8.131.52.4 – Other tab
From this tab, you can calculate the Laplacian Matrix of the current mesh on the scene. You can calculate the Laplacian Matrix either it is the uniformly weighted or not If you want to see the matrix, you have option to write it to file and you can also set the new colors of the each node according to the selected eigenvector of the Laplacian Matrix of the current mesh on the scene.
You can calculate the shortest path from one node to another and to display the path you should click ‘Display the Shortest Path’ button. If you want to remove the path from the scene you can press ‘Clear the Shortest Path’ button.
184.108.40.206 - History panel
When you carry out a series of actions in the application, you may want to see them. This is where using the History Panel comes into effect. The History Panel enables you to see all the actions that you have carried out.
6.3 Mesh rendering in C++
Drawing a mesh is a rough task. In order to achieve it, we decided to use powerful, cross-language, multi-platform API called OpenGL (Open Graphics Library) for our mesh rendering.
Creating user interface was another important task. We used Qt framework and Qt-OpenGL integration. Qt has QtOpenGL module offering classes to make it easy to use OpenGL in Qt applications. Since we started to use Qt Widgets as UI elements to create classic desktop-style user interface, we created our own GLWidget class inherited from QGLWidget to access basic OpenGL functions. So we override its methods like initializeGL, paintGL, resizeGL, key press events, mouse press and wheel event etc. This give us easy access to OpenGL API to draw our mesh and process on it.
Since the task was mesh drawing and processing it, we used model view projection to draw mesh in 3D. We also created Camera class to provide virtual camera in our OpenGL scene. In class Camera, we keep track of the camera position, rotation and zoom level.