Browse Source

Merge pull request #960 from smalcom/assimp_qt_viewer

[+] 3D viewer which uses Assimp and Qt4.
Kim Kulling 9 years ago
parent
commit
6003616b07

+ 26 - 0
CMakeLists.txt

@@ -306,6 +306,32 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   ENDIF ( WIN32 )
 
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
+
+  # Check dependencies for assimp_qt_viewer.
+  # Why here? Maybe user do not want Qt viewer and have no Qt.
+  # Why assimp_qt_viewer/CMakeLists.txt still contain similar check?
+  # Because viewer can be build independently of Assimp.
+  FIND_PACKAGE(Qt4 QUIET)
+  FIND_PACKAGE(DevIL QUIET)
+  FIND_PACKAGE(OpenGL QUIET)
+  IF ( Qt4_FOUND AND IL_FOUND AND OPENGL_FOUND)
+    ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )
+  ELSE()
+    SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "")
+    IF (NOT Qt4_FOUND)
+      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} Qt4")
+    ENDIF (NOT Qt4_FOUND)
+
+    IF (NOT IL_FOUND)
+      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} DevIL")
+    ENDIF (NOT IL_FOUND)
+
+    IF (NOT OPENGL_FOUND)
+      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} OpengGL")
+    ENDIF (NOT OPENGL_FOUND)
+
+    MESSAGE (WARNING "Build of assimp_qt_viewer is disabled. Unsatisfied dendencies: ${ASSIMP_QT_VIEWER_DEPENDENCIES}")
+  ENDIF ( Qt4_FOUND AND IL_FOUND AND OPENGL_FOUND)
 ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 
 option ( ASSIMP_BUILD_SAMPLES

+ 44 - 0
tools/assimp_qt_viewer/CMakeLists.txt

@@ -0,0 +1,44 @@
+project(assimp_qt_viewer)
+set(PROJECT_VERSION "")
+
+cmake_minimum_required(VERSION 2.6)
+
+find_package(Qt4 REQUIRED)
+find_package(DevIL REQUIRED)
+find_package(OpenGL REQUIRED)
+
+include_directories(
+	${QT_INCLUDES}
+	${Assimp_SOURCE_DIR}/include
+	${Assimp_SOURCE_DIR}/code
+	${CMAKE_CURRENT_BINARY_DIR}
+	${CMAKE_SOURCE_DIR}
+	${OPENGL_INCLUDE_DIR}
+	${IL_INCLUDE_DIR}
+)
+
+link_directories(${Assimp_BINARY_DIR})
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wall")
+
+set(assimp_qt_viewer_SRCS main.cpp loggerview.cpp glview.cpp mainwindow.cpp)
+qt4_wrap_ui(UISrcs mainwindow.ui)
+qt4_wrap_cpp(MOCrcs mainwindow.hpp glview.hpp)
+
+add_executable(${PROJECT_NAME} ${assimp_qt_viewer_SRCS} ${UISrcs} ${MOCrcs})
+target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTOPENGL_LIBRARY} ${IL_LIBRARIES} ${OPENGL_LIBRARIES} assimp)
+
+if(WIN32) # Check if we are on Windows
+	if(MSVC) # Check if we are using the Visual Studio compiler
+		set_target_properties(TestProject PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
+	elseif(CMAKE_COMPILER_IS_GNUCXX)
+		# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows") # Not tested
+	else()
+		message(SEND_ERROR "You are using an unsupported Windows compiler! (Not MSVC or GCC)")
+	endif()
+elseif(UNIX)
+	# Nothing special required
+else()
+	message(SEND_ERROR "You are on an unsupported platform! (Not Win32 or Unix)")
+endif()
+
+set_property(TARGET ${PROJECT_NAME} PROPERTY DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})

BIN
tools/assimp_qt_viewer/doc/Assimp_qt_viewer. Manual (en).odt


BIN
tools/assimp_qt_viewer/doc/Assimp_qt_viewer. Manual (ru).odt


+ 1043 - 0
tools/assimp_qt_viewer/glview.cpp

@@ -0,0 +1,1043 @@
+/// \file   glview.cpp
+/// \brief  OpenGL visualisation. Implementation file.
+/// \author [email protected]
+/// \date   2016
+
+#include "glview.hpp"
+
+// Header files, OpenGL.
+#include <GL/glu.h>
+
+// Header files, DevIL.
+#include <IL/il.h>
+
+// Header files, Assimp.
+#include <assimp/DefaultLogger.hpp>
+
+#ifndef __unused
+	#define __unused	__attribute__((unused))
+#endif // __unused
+
+/**********************************/
+/********** SHelper_Mesh **********/
+/**********************************/
+
+CGLView::SHelper_Mesh::SHelper_Mesh(const size_t pQuantity_Point, const size_t pQuantity_Line, const size_t pQuantity_Triangle, const SBBox& pBBox)
+	: Quantity_Point(pQuantity_Point), Quantity_Line(pQuantity_Line), Quantity_Triangle(pQuantity_Triangle), BBox(pBBox)
+{
+	Index_Point = pQuantity_Point ? new GLuint[pQuantity_Point * 1] : nullptr;
+	Index_Line = pQuantity_Line ? new GLuint[pQuantity_Line * 2] : nullptr;
+	Index_Triangle = pQuantity_Triangle ? new GLuint[pQuantity_Triangle * 3] : nullptr;
+}
+
+CGLView::SHelper_Mesh::~SHelper_Mesh()
+{
+	if(Index_Point != nullptr) delete [] Index_Point;
+	if(Index_Line != nullptr) delete [] Index_Line;
+	if(Index_Triangle != nullptr) delete [] Index_Triangle;
+}
+
+/**********************************/
+/********** SHelper_Mesh **********/
+/**********************************/
+
+void CGLView::SHelper_Camera::SetDefault()
+{
+	Position.Set(0, 0, 0);
+	Target.Set(0, 0, -1);
+	Rotation_AroundCamera = aiMatrix4x4();
+	Rotation_Scene = aiMatrix4x4();
+	Translation_ToScene.Set(0, 0, 2);
+}
+
+/**********************************/
+/************ CGLView *************/
+/**********************************/
+
+void CGLView::Material_Apply(const aiMaterial* pMaterial)
+{
+GLfloat tcol[4];
+aiColor4D taicol;
+unsigned int max;
+int ret1, ret2;
+int texture_index = 0;
+aiString texture_path;
+
+auto set_float4 = [](float f[4], float a, float b, float c, float d) { f[0] = a, f[1] = b, f[2] = c, f[3] = d; };
+auto color4_to_float4 = [](const aiColor4D *c, float f[4]) { f[0] = c->r, f[1] = c->g, f[2] = c->b, f[3] = c->a; };
+
+	///TODO: cache materials
+	// Disable color material because glMaterial is used.
+	glDisable(GL_COLOR_MATERIAL);///TODO: cache
+	// Set texture. If assigned.
+	if(AI_SUCCESS == pMaterial->GetTexture(aiTextureType_DIFFUSE, texture_index, &texture_path))
+	{
+		//bind texture
+		unsigned int texture_ID = mTexture_IDMap.value(texture_path.data, 0);
+
+		glBindTexture(GL_TEXTURE_2D, texture_ID);
+	}
+	//
+	// Set material parameters from scene or default values.
+	//
+	// Diffuse
+	set_float4(tcol, 0.8f, 0.8f, 0.8f, 1.0f);
+	if(AI_SUCCESS == aiGetMaterialColor(pMaterial, AI_MATKEY_COLOR_DIFFUSE, &taicol)) color4_to_float4(&taicol, tcol);
+
+	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, tcol);
+	// Specular
+	set_float4(tcol, 0.0f, 0.0f, 0.0f, 1.0f);
+	if(AI_SUCCESS == aiGetMaterialColor(pMaterial, AI_MATKEY_COLOR_SPECULAR, &taicol)) color4_to_float4(&taicol, tcol);
+
+	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, tcol);
+	// Ambient
+	set_float4(tcol, 0.2f, 0.2f, 0.2f, 1.0f);
+	if(AI_SUCCESS == aiGetMaterialColor(pMaterial, AI_MATKEY_COLOR_AMBIENT, &taicol)) color4_to_float4(&taicol, tcol);
+
+	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, tcol);
+	// Emission
+	set_float4(tcol, 0.0f, 0.0f, 0.0f, 1.0f);
+	if(AI_SUCCESS == aiGetMaterialColor(pMaterial, AI_MATKEY_COLOR_EMISSIVE, &taicol)) color4_to_float4(&taicol, tcol);
+
+	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, tcol);
+	// Shininess
+	float shininess, strength;
+
+	max = 1;
+	ret1 = aiGetMaterialFloatArray(pMaterial, AI_MATKEY_SHININESS, &shininess, &max);
+	// Shininess strength
+	max = 1;
+	ret2 = aiGetMaterialFloatArray(pMaterial, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
+	if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
+	{
+		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);///TODO: cache
+	}
+	else
+	{
+		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);///TODO: cache
+		set_float4(tcol, 0.0f, 0.0f, 0.0f, 0.0f);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, tcol);
+	}
+
+	// Fill mode
+	GLenum fill_mode;
+	int wireframe;
+
+	max = 1;
+	if(AI_SUCCESS == aiGetMaterialIntegerArray(pMaterial, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
+		fill_mode = wireframe ? GL_LINE : GL_FILL;
+	else
+		fill_mode = GL_FILL;
+
+	glPolygonMode(GL_FRONT_AND_BACK, fill_mode);///TODO: cache
+	// Fill side
+	int two_sided;
+
+	max = 1;
+	if((AI_SUCCESS == aiGetMaterialIntegerArray(pMaterial, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)///TODO: cache
+		glDisable(GL_CULL_FACE);
+	else
+		glEnable(GL_CULL_FACE);
+}
+
+void CGLView::Matrix_NodeToRoot(const aiNode* pNode, aiMatrix4x4& pOutMatrix)
+{
+const aiNode* node_cur;
+std::list<aiMatrix4x4> mat_list;
+
+	pOutMatrix = aiMatrix4x4();
+	// starting walk from current element to root
+	node_cur = pNode;
+	if(node_cur != nullptr)
+	{
+		do
+		{
+			// if cur_node is group then store group transformation matrix in list.
+			mat_list.push_back(node_cur->mTransformation);
+			node_cur = node_cur->mParent;
+		} while(node_cur != nullptr);
+	}
+
+	// multiplicate all matrices in reverse order
+	for(std::list<aiMatrix4x4>::reverse_iterator rit = mat_list.rbegin(); rit != mat_list.rend(); rit++) pOutMatrix = pOutMatrix * (*rit);
+}
+
+void CGLView::ImportTextures(const QString& pScenePath)
+{
+ILboolean success;
+
+	if(mScene == nullptr)
+	{
+		LogError("Trying to load textures for empty scene.");
+
+		return;
+	}
+
+	// Before calling ilInit() version should be checked.
+	if(ilGetInteger(IL_VERSION_NUM) < IL_VERSION)
+	{
+		LogError("Wrong DevIL version.");
+
+		return;
+	}
+
+	ilInit();// Initialization of DevIL.
+	//
+	// Load embedded textures
+	//
+	if(mScene->HasTextures()) LogError("Support for meshes with embedded textures is not implemented.");
+
+	//
+	// Load textures from external files.
+	//
+	// Get textures file names and number of textures.
+	for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++)
+	{
+		int idx_texture = 0;
+		aiString path;
+
+		do
+		{
+			if(mScene->mMaterials[idx_material]->GetTexture(aiTextureType_DIFFUSE, idx_texture, &path) != AI_SUCCESS) break;
+
+			mTexture_IDMap[path.data] = 0;// Fill map with invalid ID's.
+			idx_texture++;
+		} while(true);
+	}// for(size_t idx_mat = 0; idx_mat < scene->mNumMaterials; idx_mat++)
+
+	// Textures list is empty, exit.
+	if(mTexture_IDMap.size() == 0)
+	{
+		LogInfo("No textures for import.");
+
+		return;
+	}
+
+	size_t num_textures = mTexture_IDMap.size();
+
+
+	ILuint* id_images = nullptr;// Array with DevIL image ID's.
+	GLuint* id_textures = nullptr;// Array with OpenGL textures ID's.
+
+	// Generate DevIL image IDs.
+	id_images = new ILuint[num_textures];
+	ilGenImages(num_textures, id_images);// Generation of 'num_textures' image names.
+	// Create and fill array with OpenGL texture ID's.
+	id_textures = new GLuint[num_textures];
+	///TODO: if can not load textures then will stay orphande texture ID's in OpenGL. Generate OpenGL ID's after successfull loading of image.
+	glGenTextures(num_textures, id_textures);// Texture ID's generation.
+
+	QMap<QString, GLuint>::iterator map_it = mTexture_IDMap.begin();// Get iterator
+	QString basepath = pScenePath.left(pScenePath.lastIndexOf('/') + 1);// path with '/' at the end.
+
+	for(size_t idx_texture = 0; idx_texture < num_textures; idx_texture++)
+	{
+		//save IL image ID
+		QString filename = map_it.key();// get filename
+
+		mTexture_IDMap[filename] = id_textures[idx_texture];// save texture ID for filename in map
+		map_it++;// next texture
+		ilBindImage(id_images[idx_texture]);// Binding of DevIL image name.
+
+		QString fileloc = basepath + filename;	/* Loading of image */
+
+		fileloc.replace('\\', "/");
+		success = ilLoadImage(fileloc.toLocal8Bit());
+		if(!success)
+		{
+			LogError(QString("Couldn't load Image: %1").arg(fileloc));
+			goto it_for_err;
+		}
+
+		// Convert every colour component into unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA.
+		success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
+		if(!success)
+		{
+			LogError("Couldn't convert image.");
+			goto it_for_err;
+		}
+
+		glBindTexture(GL_TEXTURE_2D, id_textures[idx_texture]);// Binding of texture ID.
+		// Redefine standard texture values
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
+		glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0,
+						ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());// Texture specification.
+		continue;
+
+it_for_err:
+
+		LogError(QString("DevIL error: %1, [%2]").arg(ilGetError()).arg(ilGetString(ilGetError())));
+		mTexture_IDMap.remove(filename);
+	}// for(size_t idx_texture = 0; idx_texture < num_textures; i++)
+
+	// Because we have already copied image data into texture data we can release memory used by image.
+	ilDeleteImages(num_textures, id_images);
+
+	//Cleanup
+	delete [] id_images;
+	delete [] id_textures;
+}
+
+void CGLView::BBox_GetForNode(const aiNode& pNode, const aiMatrix4x4& pParent_TransformationMatrix, SBBox& pNodeBBox, bool& pFirstAssign)
+{
+aiMatrix4x4 mat_trans = pParent_TransformationMatrix * pNode.mTransformation;
+
+	// Check if node has meshes
+	for(size_t idx_idx_mesh = 0; idx_idx_mesh < pNode.mNumMeshes; idx_idx_mesh++)
+	{
+		size_t idx_mesh;
+		SBBox bbox_local;
+		aiVector3D bbox_vertices[8];
+
+		idx_mesh = pNode.mMeshes[idx_idx_mesh];
+		// Get vertices of mesh BBox
+		BBox_GetVertices(mHelper_Mesh[idx_mesh]->BBox, bbox_vertices);
+		// Transform vertices
+		for(size_t idx_vert = 0; idx_vert < 8; idx_vert++) bbox_vertices[idx_vert] *= mat_trans;
+
+		// And create BBox for transformed mesh
+		BBox_GetFromVertices(bbox_vertices, 8, bbox_local);
+
+		if(!pFirstAssign)
+		{
+			BBox_Extend(bbox_local, pNodeBBox);
+		}
+		else
+		{
+			pFirstAssign = false;
+			pNodeBBox = bbox_local;
+		}
+	}// for(size_t idx_idx_mesh = 0; idx_idx_mesh < pNode.mNumMeshes; idx_idx_mesh++)
+
+	for(size_t idx_node = 0; idx_node < pNode.mNumChildren; idx_node++)
+	{
+		BBox_GetForNode(*pNode.mChildren[idx_node], mat_trans, pNodeBBox, pFirstAssign);
+	}
+}
+
+void CGLView::BBox_Extend(const SBBox& pChild, SBBox& pParent)
+{
+	// search minimal...
+	AssignIfLesser(&pParent.Minimum.x, pChild.Minimum.x);
+	AssignIfLesser(&pParent.Minimum.y, pChild.Minimum.y);
+	AssignIfLesser(&pParent.Minimum.z, pChild.Minimum.z);
+	// and maximal values
+	AssignIfGreater(&pParent.Maximum.x, pChild.Maximum.x);
+	AssignIfGreater(&pParent.Maximum.y, pChild.Maximum.y);
+	AssignIfGreater(&pParent.Maximum.z, pChild.Maximum.z);
+}
+
+void CGLView::BBox_GetVertices(const SBBox& pBBox, aiVector3D pVertex[8])
+{
+	pVertex[0] = pBBox.Minimum;
+	pVertex[1].Set(pBBox.Minimum.x, pBBox.Minimum.y, pBBox.Maximum.z);
+	pVertex[2].Set(pBBox.Minimum.x, pBBox.Maximum.y, pBBox.Maximum.z);
+	pVertex[3].Set(pBBox.Minimum.x, pBBox.Maximum.y, pBBox.Minimum.z);
+
+	pVertex[4].Set(pBBox.Maximum.x, pBBox.Minimum.y, pBBox.Minimum.z);
+	pVertex[5].Set(pBBox.Maximum.x, pBBox.Minimum.y, pBBox.Maximum.z);
+	pVertex[6] = pBBox.Maximum;
+	pVertex[7].Set(pBBox.Maximum.x, pBBox.Maximum.y, pBBox.Minimum.z);
+
+}
+
+void CGLView::BBox_GetFromVertices(const aiVector3D* pVertices, const size_t pVerticesQuantity, SBBox& pBBox)
+{
+	if(pVerticesQuantity == 0)
+	{
+		pBBox.Maximum.Set(0, 0, 0);
+		pBBox.Minimum.Set(0, 0, 0);
+
+		return;
+	}
+
+	// Assign first values.
+	pBBox.Minimum = pVertices[0];
+	pBBox.Maximum = pVertices[0];
+
+	for(size_t idx_vert = 1; idx_vert < pVerticesQuantity; idx_vert++)
+	{
+		const GLfloat x = pVertices[idx_vert].x;
+		const GLfloat y = pVertices[idx_vert].y;
+		const GLfloat z = pVertices[idx_vert].z;
+
+		// search minimal...
+		AssignIfLesser(&pBBox.Minimum.x, x);
+		AssignIfLesser(&pBBox.Minimum.y, y);
+		AssignIfLesser(&pBBox.Minimum.z, z);
+		// and maximal values
+		AssignIfGreater(&pBBox.Maximum.x, x);
+		AssignIfGreater(&pBBox.Maximum.y, y);
+		AssignIfGreater(&pBBox.Maximum.z, z);
+	}
+}
+
+/********************************************************************/
+/************************ Logging functions *************************/
+/********************************************************************/
+
+void CGLView::LogInfo(const QString& pMessage)
+{
+	Assimp::DefaultLogger::get()->info(pMessage.toStdString());
+}
+
+void CGLView::LogError(const QString& pMessage)
+{
+	Assimp::DefaultLogger::get()->error(pMessage.toStdString());
+}
+
+/********************************************************************/
+/************************** Draw functions **************************/
+/********************************************************************/
+
+void CGLView::Draw_Node(const aiNode* pNode)
+{
+aiMatrix4x4 mat_node = pNode->mTransformation;
+
+	// Apply node transformation matrix.
+	mat_node.Transpose();
+	glPushMatrix();
+	glMultMatrixf((GLfloat*)&mat_node);
+	// Draw all meshes assigned to this node
+	for(size_t idx_mesh_arr = 0; idx_mesh_arr < pNode->mNumMeshes; idx_mesh_arr++) Draw_Mesh(pNode->mMeshes[idx_mesh_arr]);
+
+	// Draw all children nodes
+	for(size_t idx_node = 0; idx_node < pNode->mNumChildren; idx_node++) Draw_Node(pNode->mChildren[idx_node]);
+
+	// Restore transformation matrix.
+	glPopMatrix();
+}
+
+void CGLView::Draw_Mesh(const size_t pMesh_Index)
+{
+	// Check argument
+	if(pMesh_Index >= mHelper_Mesh_Quantity) return;
+
+	aiMesh& mesh_cur = *mScene->mMeshes[pMesh_Index];
+
+	if(!mesh_cur.HasPositions()) return;// Nothing to draw.
+
+	// If mesh use material then apply it
+	if(mScene->HasMaterials()) Material_Apply(mScene->mMaterials[mesh_cur.mMaterialIndex]);
+
+	//
+	// Vertices array
+	//
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glVertexPointer(3, GL_FLOAT, 0, mesh_cur.mVertices);
+
+	if(mesh_cur.HasVertexColors(0))
+	{
+		glEnable(GL_COLOR_MATERIAL);///TODO: cache
+		glEnableClientState(GL_COLOR_ARRAY);
+		glColorPointer(4, GL_FLOAT, 0, mesh_cur.mColors[0]);
+	}
+
+	//
+	// Texture coordinates array
+	//
+	if(mesh_cur.HasTextureCoords(0))
+	{
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glTexCoordPointer(2, GL_FLOAT, sizeof(aiVector3D), mesh_cur.mTextureCoords[0]);
+	}
+
+	//
+	// Normals array
+	//
+	if(mesh_cur.HasNormals())
+	{
+		glEnableClientState(GL_NORMAL_ARRAY);
+		glNormalPointer(GL_FLOAT, 0, mesh_cur.mNormals);
+	}
+
+	//
+	// Draw arrays
+	//
+	SHelper_Mesh& helper_cur = *mHelper_Mesh[pMesh_Index];
+
+	if(helper_cur.Quantity_Triangle > 0) glDrawElements(GL_TRIANGLES, helper_cur.Quantity_Triangle * 3, GL_UNSIGNED_INT, helper_cur.Index_Triangle);
+	if(helper_cur.Quantity_Line > 0) glDrawElements(GL_LINES,helper_cur.Quantity_Line * 2, GL_UNSIGNED_INT, helper_cur.Index_Line);
+	if(helper_cur.Quantity_Point > 0) glDrawElements(GL_POINTS, helper_cur.Quantity_Point, GL_UNSIGNED_INT, helper_cur.Index_Point);
+
+	//
+	// Clean up
+	//
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+void CGLView::Draw_BBox(const SBBox& pBBox)
+{
+aiVector3D vertex[8];
+
+	BBox_GetVertices(pBBox, vertex);
+	// Draw
+	if(mLightingEnabled) glDisable(GL_LIGHTING);///TODO: display list
+
+	glEnable(GL_COLOR_MATERIAL);
+	glBindTexture(GL_TEXTURE_1D, 0);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glBindTexture(GL_TEXTURE_3D, 0);
+	qglColor(QColor(Qt::white));
+	glBegin(GL_LINE_STRIP);
+		glVertex3fv(&vertex[0][0]), glVertex3fv(&vertex[1][0]), glVertex3fv(&vertex[2][0]), glVertex3fv(&vertex[3][0]), glVertex3fv(&vertex[0][0]);// "Minimum" side.
+		glVertex3fv(&vertex[4][0]), glVertex3fv(&vertex[5][0]), glVertex3fv(&vertex[6][0]), glVertex3fv(&vertex[7][0]), glVertex3fv(&vertex[4][0]);// Edge and "maximum" side.
+	glEnd();
+	glBegin(GL_LINES);
+		glVertex3fv(&vertex[1][0]), glVertex3fv(&vertex[5][0]);
+		glVertex3fv(&vertex[2][0]), glVertex3fv(&vertex[6][0]);
+		glVertex3fv(&vertex[3][0]), glVertex3fv(&vertex[7][0]);
+	glEnd();
+	glDisable(GL_COLOR_MATERIAL);
+	if(mLightingEnabled) glEnable(GL_LIGHTING);
+}
+
+void CGLView::Enable_Textures(const bool pEnable)
+{
+	if(pEnable)
+	{
+		glEnable(GL_TEXTURE_1D);
+		glEnable(GL_TEXTURE_2D);
+		glEnable(GL_TEXTURE_3D);
+	}
+	else
+	{
+		glDisable(GL_TEXTURE_1D);
+		glDisable(GL_TEXTURE_2D);
+		glDisable(GL_TEXTURE_3D);
+	}
+}
+
+/********************************************************************/
+/*********************** Overrided functions ************************/
+/********************************************************************/
+
+void CGLView::initializeGL()
+{
+	qglClearColor(Qt::gray);
+	glShadeModel(GL_SMOOTH);
+
+	glEnable(GL_DEPTH_TEST);
+	glEnable(GL_NORMALIZE);
+	glEnable(GL_TEXTURE_2D);
+
+	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
+	glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+	glDisable(GL_COLOR_MATERIAL);
+
+	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+	glEnable(GL_CULL_FACE);
+	glCullFace(GL_BACK);
+
+	glFrontFace(GL_CCW);
+}
+
+void CGLView::resizeGL(int pWidth, int pHeight)
+{
+	mCamera_Viewport_AspectRatio = (GLdouble)pWidth / pHeight;
+	glViewport(0, 0, pWidth, pHeight);
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	gluPerspective(mCamera_FOVY, mCamera_Viewport_AspectRatio, 1.0, 100000.0);///TODO: znear/zfar depend on scene size.
+}
+
+void CGLView::paintGL()
+{
+QTime time_paintbegin;
+
+	time_paintbegin = QTime::currentTime();
+
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	// Apply current camera transformations.
+	glMultMatrixf((GLfloat*)&mHelper_Camera.Rotation_AroundCamera);
+	glTranslatef(-mHelper_Camera.Translation_ToScene.x, -mHelper_Camera.Translation_ToScene.y, -mHelper_Camera.Translation_ToScene.z);
+	glMultMatrixf((GLfloat*)&mHelper_Camera.Rotation_Scene);
+	// Coordinate system
+	if(mLightingEnabled) glDisable(GL_LIGHTING);///TODO: display list
+
+	glBindTexture(GL_TEXTURE_1D, 0);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glBindTexture(GL_TEXTURE_3D, 0);
+	glEnable(GL_COLOR_MATERIAL);
+	glBegin(GL_LINES);
+		// X, -X
+		qglColor(QColor(Qt::red)),     glVertex3f(0.0, 0.0, 0.0), glVertex3f(100000.0, 0.0, 0.0);
+		qglColor(QColor(Qt::cyan)),    glVertex3f(0.0, 0.0, 0.0), glVertex3f(-100000.0, 0.0, 0.0);
+		// Y, -Y
+		qglColor(QColor(Qt::green)),   glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 100000.0, 0.0);
+		qglColor(QColor(Qt::magenta)), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, -100000.0, 0.0);
+		// Z, -Z
+		qglColor(QColor(Qt::blue)),    glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, 100000.0);
+		qglColor(QColor(Qt::yellow)),  glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, -100000.0);
+	glEnd();
+	glDisable(GL_COLOR_MATERIAL);
+	if(mLightingEnabled) glEnable(GL_LIGHTING);
+
+	// Scene
+	if(mScene != nullptr)
+	{
+		Draw_Node(mScene->mRootNode);
+		// Scene BBox
+		if(mScene_DrawBBox) Draw_BBox(mScene_BBox);
+	}
+
+	emit Paint_Finished((size_t)time_paintbegin.msecsTo(QTime::currentTime()), mHelper_Camera.Translation_ToScene.Length());
+}
+
+/********************************************************************/
+/********************** Constructor/Destructor **********************/
+/********************************************************************/
+
+CGLView::CGLView(QWidget *pParent)
+	: QGLWidget(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer), pParent)
+{
+	static_assert(sizeof(GLfloat) == sizeof(ai_real), "ai_real in Assimp must be equal to GLfloat/float.");///TODO: may be templates can be used.
+
+	// set initial view
+	mHelper_CameraDefault.SetDefault();
+	Camera_Set(0);
+}
+
+CGLView::~CGLView()
+{
+	FreeScene();
+}
+
+/********************************************************************/
+/********************* Scene control functions **********************/
+/********************************************************************/
+
+void CGLView::FreeScene()
+{
+	// Set scene to null and after that \ref paintGL will not try to render it.
+	mScene = nullptr;
+	// Clean helper objects.
+	if(mHelper_Mesh != nullptr)
+	{
+		for(size_t idx_mesh = 0; idx_mesh < mHelper_Mesh_Quantity; idx_mesh++) delete mHelper_Mesh[idx_mesh];
+
+		delete [] mHelper_Mesh;
+		mHelper_Mesh = nullptr;
+	}
+
+	mHelper_Mesh_Quantity = 0;
+	// Delete textures
+	const int id_tex_size = mTexture_IDMap.size();
+
+	if(id_tex_size)
+	{
+		GLuint* id_tex = new GLuint[id_tex_size];
+		QMap<QString, GLuint>::iterator it = mTexture_IDMap.begin();
+
+		for(int idx = 0; idx < id_tex_size; idx++, it++)
+		{
+			id_tex[idx] = it.value();
+		}
+
+		glDeleteTextures(id_tex_size, id_tex);
+		mTexture_IDMap.clear();
+		delete [] id_tex;
+	}
+}
+
+void CGLView::SetScene(const aiScene *pScene, const QString& pScenePath)
+{
+	FreeScene();// Clear old data
+	// Why checking here, not at begin of function. Because old scene may not exist at know. So, need cleanup.
+	if(pScene == nullptr) return;
+
+	mScene = pScene;// Copy pointer of new scene.
+
+	//
+	// Meshes
+	//
+	// Create helper objects for meshes. This allow to render meshes as OpenGL arrays.
+	if(mScene->HasMeshes())
+	{
+		// Create mesh helpers array.
+		mHelper_Mesh_Quantity = mScene->mNumMeshes;
+		mHelper_Mesh = new SHelper_Mesh*[mScene->mNumMeshes];
+
+		// Walk thru the meshes and extract needed data and, also calculate BBox.
+		for(size_t idx_mesh = 0; idx_mesh < mScene->mNumMeshes; idx_mesh++)
+		{
+			aiMesh& mesh_cur = *mScene->mMeshes[idx_mesh];
+
+			//
+			// Calculate BBox
+			//
+			SBBox mesh_bbox;
+
+			BBox_GetFromVertices(mesh_cur.mVertices, mesh_cur.mNumVertices, mesh_bbox);
+			//
+			// Create vertices indices arrays splited by primitive type.
+			//
+			size_t indcnt_p = 0;// points quantity
+			size_t indcnt_l = 0;// lines quantity
+			size_t indcnt_t = 0;// triangles quantity
+
+			if(mesh_cur.HasFaces())
+			{
+				// Usual way: all faces are triangles
+				if(mesh_cur.mPrimitiveTypes == aiPrimitiveType_TRIANGLE)
+				{
+					indcnt_t = mesh_cur.mNumFaces;
+				}
+				else
+				{
+					// Calculate count of primitives by types.
+					for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
+					{
+						if(mesh_cur.mFaces[idx_face].mNumIndices == 3)
+							indcnt_t++;
+						else if(mesh_cur.mFaces[idx_face].mNumIndices == 2)
+							indcnt_l++;
+						else if(mesh_cur.mFaces[idx_face].mNumIndices == 1)
+							indcnt_p++;
+					}
+				}// if(mesh_cur.mPrimitiveTypes == aiPrimitiveType_TRIANGLE) else
+
+				// Create helper
+				mHelper_Mesh[idx_mesh] = new SHelper_Mesh(indcnt_p, indcnt_l, indcnt_t, mesh_bbox);
+				// Fill indices arrays
+				indcnt_p = 0, indcnt_l = 0, indcnt_t = 0;// Reuse variables as indices
+				for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
+				{
+					if(mesh_cur.mFaces[idx_face].mNumIndices == 3)
+					{
+						mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[0];
+						mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[1];
+						mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[2];
+					}
+					else if(mesh_cur.mFaces[idx_face].mNumIndices == 2)
+					{
+						mHelper_Mesh[idx_mesh]->Index_Line[indcnt_l++] = mesh_cur.mFaces[idx_face].mIndices[0];
+						mHelper_Mesh[idx_mesh]->Index_Line[indcnt_l++] = mesh_cur.mFaces[idx_face].mIndices[1];
+					}
+					else if(mesh_cur.mFaces[idx_face].mNumIndices == 1)
+					{
+						mHelper_Mesh[idx_mesh]->Index_Point[indcnt_p++] = mesh_cur.mFaces[idx_face].mIndices[0];
+					}
+				}// for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
+			}// if(mesh_cur.HasFaces())
+			else
+			{
+				// If mesh has no faces then vertices can be just points set.
+				indcnt_p = mesh_cur.mNumVertices;
+				// Create helper
+				mHelper_Mesh[idx_mesh] = new SHelper_Mesh(indcnt_p, 0, 0, mesh_bbox);
+				// Fill indices arrays
+				for(size_t idx = 0; idx < indcnt_p; idx++) mHelper_Mesh[idx_mesh]->Index_Point[idx] = idx;
+
+			}// if(mesh_cur.HasFaces()) else
+		}// for(size_t idx_mesh = 0; idx_mesh < mScene->mNumMeshes; idx_mesh++)
+	}// if(mScene->HasMeshes())
+
+	//
+	// Scene BBox
+	//
+	// For calculating right BBox we must walk thru all nodes and apply transformation to meshes BBoxes
+	if(mHelper_Mesh_Quantity > 0)
+	{
+		bool first_assign = true;
+		aiMatrix4x4 mat_root;
+
+		BBox_GetForNode(*mScene->mRootNode, mat_root, mScene_BBox, first_assign);
+		mScene_Center = mScene_BBox.Maximum + mScene_BBox.Minimum;
+		mScene_Center /= 2;
+	}
+	else
+	{
+		mScene_BBox = {{0, 0, 0}, {0, 0, 0}};
+		mScene_Center = {0, 0, 0};
+	}// if(mHelper_Mesh_Count > 0) else
+
+	//
+	// Textures
+	//
+	ImportTextures(pScenePath);
+
+	//
+	// Light sources
+	//
+	Lighting_Enable();
+	// If scene has no lights then enable default
+	if(!mScene->HasLights())
+	{
+		const GLfloat col_amb[4] = { 0.2, 0.2, 0.2, 1.0 };
+		SLightParameters lp;
+
+		lp.Type = aiLightSource_POINT;
+		lp.Ambient.r = col_amb[0], lp.Ambient.g = col_amb[1], lp.Ambient.b = col_amb[2], lp.Ambient.a = col_amb[3];
+		lp.Diffuse = { 1.0, 1.0, 1.0, 1.0 };
+		lp.Specular = lp.Diffuse;
+		lp.For.Point.Position = mScene_Center;
+		lp.For.Point.Attenuation_Constant = 1;
+		lp.For.Point.Attenuation_Linear = 0;
+		lp.For.Point.Attenuation_Quadratic = 0;
+		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col_amb);
+		Lighting_EditSource(0, lp);
+		emit SceneObject_LightSource("_default");// Light source will be enabled in signal handler.
+	}
+	else
+	{
+		for(size_t idx_light = 0; idx_light < mScene->mNumLights; idx_light++)
+		{
+			SLightParameters lp;
+			QString name;
+			const aiLight& light_cur = *mScene->mLights[idx_light];
+
+			auto col3_to_col4 = [](const aiColor3D& pCol3) -> aiColor4D { return aiColor4D(pCol3.r, pCol3.g, pCol3.b, 1.0); };
+
+			///TODO: find light source node and apply all transformations
+			// General properties
+			name = light_cur.mName.C_Str();
+			lp.Ambient = col3_to_col4(light_cur.mColorAmbient);
+			lp.Diffuse = col3_to_col4(light_cur.mColorDiffuse);
+			lp.Specular = col3_to_col4(light_cur.mColorSpecular);
+			lp.Type = light_cur.mType;
+			// Depend on type properties
+			switch(light_cur.mType)
+			{
+				case aiLightSource_DIRECTIONAL:
+					lp.For.Directional.Direction = light_cur.mDirection;
+					break;
+				case aiLightSource_POINT:
+					lp.For.Point.Position = light_cur.mPosition;
+					lp.For.Point.Attenuation_Constant = light_cur.mAttenuationConstant;
+					lp.For.Point.Attenuation_Linear = light_cur.mAttenuationLinear;
+					lp.For.Point.Attenuation_Quadratic = light_cur.mAttenuationQuadratic;
+					break;
+				case aiLightSource_SPOT:
+					lp.For.Spot.Position = light_cur.mPosition;
+					lp.For.Spot.Direction = light_cur.mDirection;
+					lp.For.Spot.Attenuation_Constant = light_cur.mAttenuationConstant;
+					lp.For.Spot.Attenuation_Linear = light_cur.mAttenuationLinear;
+					lp.For.Spot.Attenuation_Quadratic = light_cur.mAttenuationQuadratic;
+					lp.For.Spot.CutOff = light_cur.mAngleOuterCone;
+					break;
+				case aiLightSource_AMBIENT:
+					lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
+					name.append("_unsup_ambient");
+					break;
+				case aiLightSource_AREA:
+					lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
+					name.append("_unsup_area");
+					break;
+				case aiLightSource_UNDEFINED:
+					lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
+					name.append("_unsup_undefined");
+					break;
+				default:
+					lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
+					name.append("_unsupported_invalid");
+					break;
+			}// switch(light_cur.mType)
+
+			// Add light source
+			if(name.isEmpty()) name += QString("%1").arg(idx_light);// Use index if name is empty.
+
+			Lighting_EditSource(idx_light, lp);
+			emit SceneObject_LightSource(name);// Light source will be enabled in signal handler.
+		}// for(size_t idx_light = 0; idx_light < mScene->mNumLights; idx_light++)
+	}// if(!mScene->HasLights()) else
+
+	//
+	// Cameras
+	//
+	if(!mScene->HasCameras())
+	{
+		mCamera_DefaultAdded = true;
+		mHelper_CameraDefault.SetDefault();
+		// Calculate distance from camera to scene. Distance must be enoguh for that viewport contain whole scene.
+		const GLfloat tg_angle = tan(mCamera_FOVY / 2);
+
+		GLfloat val_x = ((mScene_BBox.Maximum.x - mScene_BBox.Minimum.x) / 2) / (mCamera_Viewport_AspectRatio * tg_angle);
+		GLfloat val_y = ((mScene_BBox.Maximum.y - mScene_BBox.Minimum.y) / 2) / tg_angle;
+		GLfloat val_step = val_x;
+
+		AssignIfGreater(val_step, val_y);
+		mHelper_CameraDefault.Translation_ToScene.Set(mScene_Center.x, mScene_Center.y, val_step + mScene_BBox.Maximum.z);
+		emit SceneObject_Camera("_default");
+	}
+	else
+	{
+		mCamera_DefaultAdded = false;
+		for(size_t idx_cam = 0; idx_cam < mScene->mNumCameras; idx_cam++)
+		{
+			emit SceneObject_Camera(mScene->mCameras[idx_cam]->mName.C_Str());
+		}
+	}// if(!mScene->HasCameras()) else
+}
+
+/********************************************************************/
+/******************** Lighting control functions ********************/
+/********************************************************************/
+
+void CGLView::Lighting_Enable()
+{
+	mLightingEnabled = true;
+	glEnable(GL_LIGHTING);
+}
+
+void CGLView::Lighting_Disable()
+{
+	glDisable(GL_LIGHTING);
+	mLightingEnabled = false;
+}
+
+void CGLView::Lighting_EditSource(const size_t pLightNumber, const SLightParameters& pLightParameters)
+{
+const size_t light_num = GL_LIGHT0 + pLightNumber;
+
+GLfloat farr[4];
+
+	if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
+
+	glLightfv(light_num, GL_AMBIENT, &pLightParameters.Ambient.r);// Ambient color
+	glLightfv(light_num, GL_DIFFUSE, &pLightParameters.Diffuse.r);// Diffuse color
+	glLightfv(light_num, GL_SPECULAR, &pLightParameters.Specular.r);// Specular color
+	// Other parameters
+	switch(pLightParameters.Type)
+	{
+		case aiLightSource_DIRECTIONAL:
+			// Direction
+			farr[0] = pLightParameters.For.Directional.Direction.x, farr[2] = pLightParameters.For.Directional.Direction.y;
+			farr[2] = pLightParameters.For.Directional.Direction.z; farr[3] = 0;
+			glLightfv(light_num, GL_POSITION, farr);
+			break;
+		case aiLightSource_POINT:
+			// Position
+			farr[0] = pLightParameters.For.Point.Position.x, farr[2] = pLightParameters.For.Point.Position.y;
+			farr[2] = pLightParameters.For.Point.Position.z; farr[3] = 1;
+			glLightfv(light_num, GL_POSITION, farr);
+			// Attenuation
+			glLightf(light_num, GL_CONSTANT_ATTENUATION, pLightParameters.For.Point.Attenuation_Constant);
+			glLightf(light_num, GL_LINEAR_ATTENUATION, pLightParameters.For.Point.Attenuation_Linear);
+			glLightf(light_num, GL_QUADRATIC_ATTENUATION, pLightParameters.For.Point.Attenuation_Quadratic);
+			glLightf(light_num, GL_SPOT_CUTOFF, 180.0);
+			break;
+		case aiLightSource_SPOT:
+			// Position
+			farr[0] = pLightParameters.For.Spot.Position.x, farr[2] = pLightParameters.For.Spot.Position.y, farr[2] = pLightParameters.For.Spot.Position.z; farr[3] = 1;
+			glLightfv(light_num, GL_POSITION, farr);
+			// Attenuation
+			glLightf(light_num, GL_CONSTANT_ATTENUATION, pLightParameters.For.Spot.Attenuation_Constant);
+			glLightf(light_num, GL_LINEAR_ATTENUATION, pLightParameters.For.Spot.Attenuation_Linear);
+			glLightf(light_num, GL_QUADRATIC_ATTENUATION, pLightParameters.For.Spot.Attenuation_Quadratic);
+			// Spot specific
+			farr[0] = pLightParameters.For.Spot.Direction.x, farr[2] = pLightParameters.For.Spot.Direction.y, farr[2] = pLightParameters.For.Spot.Direction.z; farr[3] = 0;
+			glLightfv(light_num, GL_SPOT_DIRECTION, farr);
+			glLightf(light_num, GL_SPOT_CUTOFF, pLightParameters.For.Spot.CutOff);
+			break;
+		default:// For unknown light source types use point source.
+			// Position
+			farr[0] = pLightParameters.For.Point.Position.x, farr[2] = pLightParameters.For.Point.Position.y;
+			farr[2] = pLightParameters.For.Point.Position.z; farr[3] = 1;
+			glLightfv(light_num, GL_POSITION, farr);
+			// Attenuation
+			glLightf(light_num, GL_CONSTANT_ATTENUATION, 1);
+			glLightf(light_num, GL_LINEAR_ATTENUATION, 0);
+			glLightf(light_num, GL_QUADRATIC_ATTENUATION, 0);
+			glLightf(light_num, GL_SPOT_CUTOFF, 180.0);
+			break;
+	}// switch(pLightParameters.Type)
+}
+
+void CGLView::Lighting_EnableSource(const size_t pLightNumber)
+{
+	if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
+
+	glEnable(GL_LIGHT0 + pLightNumber);
+}
+
+void CGLView::Lighting_DisableSource(const size_t pLightNumber)
+{
+	if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
+
+	glDisable(GL_LIGHT0 + pLightNumber);
+}
+
+/********************************************************************/
+/******************** Cameras control functions *********************/
+/********************************************************************/
+
+void CGLView::Camera_Set(const size_t pCameraNumber)
+{
+SHelper_Camera& hcam = mHelper_Camera;// reference with short name for conveniance.
+aiVector3D up;
+
+	if(mCamera_DefaultAdded || (pCameraNumber >= mScene->mNumCameras))// If default camera used then 'pCameraNumber' doesn't matter.
+	{
+		// Transformation parameters
+		hcam = mHelper_CameraDefault;
+		up.Set(0, 1, 0);
+	}
+	else
+	{
+		const aiCamera& camera_cur = *mScene->mCameras[pCameraNumber];
+		const aiNode* camera_node;
+
+		aiMatrix4x4 camera_mat;
+		aiQuaternion camera_quat_rot;
+		aiVector3D camera_tr;
+
+		up = camera_cur.mUp;
+		//
+		// Try to get real coordinates of the camera.
+		//
+		// Find node
+		camera_node = mScene->mRootNode->FindNode(camera_cur.mName);
+		if(camera_node != nullptr) Matrix_NodeToRoot(camera_node, camera_mat);
+
+		hcam.Position = camera_cur.mLookAt;
+		hcam.Target = camera_cur.mPosition;
+		hcam.Rotation_AroundCamera = aiMatrix4x4(camera_quat_rot.GetMatrix());
+		hcam.Rotation_AroundCamera.Transpose();
+		// get components of transformation matrix.
+		camera_mat.DecomposeNoScaling(camera_quat_rot, camera_tr);
+		hcam.Rotation_Scene = aiMatrix4x4();
+		hcam.Translation_ToScene = camera_tr;
+	}
+
+	// Load identity matrix - travel to world begin.
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	// Set camera and update picture
+	gluLookAt(hcam.Position.x, hcam.Position.y, hcam.Position.z, hcam.Target.x, hcam.Target.y, hcam.Target.z, up.x, up.y, up.z);
+}
+
+void CGLView::Camera_RotateScene(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z)
+{
+auto deg2rad = [](const GLfloat pDegree) -> GLfloat { return pDegree * M_PI / 180.0; };
+
+	aiMatrix4x4 mat_rot;
+
+	mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z));
+	mHelper_Camera.Rotation_Scene *= mat_rot;
+}
+
+void CGLView::Camera_Rotate(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z)
+{
+auto deg2rad = [](const GLfloat pDegree) -> GLfloat { return pDegree * M_PI / 180.0; };
+
+	aiMatrix4x4 mat_rot;
+
+	mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z));
+	mHelper_Camera.Rotation_AroundCamera *= mat_rot;
+}
+
+void CGLView::Camera_Translate(const GLfloat pTranslate_X, const GLfloat pTranslate_Y, const GLfloat pTranslate_Z)
+{
+aiVector3D vect_tr(pTranslate_X, pTranslate_Y, pTranslate_Z);
+
+	vect_tr *= mHelper_Camera.Rotation_AroundCamera;
+	mHelper_Camera.Translation_ToScene += vect_tr;
+}

+ 382 - 0
tools/assimp_qt_viewer/glview.hpp

@@ -0,0 +1,382 @@
+/// \file   glview.hpp
+/// \brief  OpenGL visualisation.
+/// \author [email protected]
+/// \date   2016
+
+#pragma once
+
+// Header files, Qt.
+#include <QtOpenGL>
+
+// Header files Assimp
+#include <assimp/scene.h>
+
+/// \class CGLView
+/// Class which hold and render scene.
+class CGLView : public QGLWidget
+{
+	Q_OBJECT
+
+	/**********************************/
+	/************* Types **************/
+	/**********************************/
+
+private:
+
+	/// \struct SBBox
+	/// Bounding box for object.
+	struct SBBox
+	{
+		aiVector3D Minimum;///< Minimum values of coordinates.
+		aiVector3D Maximum;///< Maximum values of coordinates.
+	};
+
+	/// \struct SHelper_Mesh
+	/// Helper object for fast rendering of mesh (\ref aiMesh).
+	struct SHelper_Mesh
+	{
+		const size_t Quantity_Point;///< Quantity of points.
+		const size_t Quantity_Line;///< Quantity of lines.
+		const size_t Quantity_Triangle;///< Quantity of triangles.
+		GLuint* Index_Point;///< Array of indices for drawing points.
+		GLuint* Index_Line;///< Array of indices for drawing lines.
+		GLuint* Index_Triangle;///< Array of indices for drawing triangles.
+
+		const SBBox BBox;///< BBox of mesh.
+
+		/// \fn explicit SHelper_Mesh(const size_t pQuantity_Point, const size_t pQuantity_Line, const size_t pQuantity_Triangle, const SBBox& pBBox = {{0, 0, 0}, {0, 0, 0}})
+		/// Constructor.
+		/// \param [in] pQuantity_Point - quantity of points.
+		/// \param [in] pQuantity_Line - quantity of lines.
+		/// \param [in] pQuantity_Triangle - quantity of triangles.
+		/// \param [in] pBBox - BBox of mesh.
+		explicit SHelper_Mesh(const size_t pQuantity_Point, const size_t pQuantity_Line, const size_t pQuantity_Triangle, const SBBox& pBBox = {{0, 0, 0}, {0, 0, 0}});
+
+		/// \fn ~SHelper_Mesh()
+		/// Destructor.
+		~SHelper_Mesh();
+	};
+
+	/// \struct SHelper_Camera
+	/// Information about position of the camera in space.
+	struct SHelper_Camera
+	{
+		aiVector3D Position;///< Coordinates of the camera.
+		aiVector3D Target;///< Target point of the camera.
+		// Transformation path:
+		// set Camera -> Rotation_AroundCamera -> Translation_ToScene -> Rotation_Scene -> draw Scene
+		aiMatrix4x4 Rotation_AroundCamera;///< Rotation matrix which set rotation angles of the scene around camera.
+		aiMatrix4x4 Rotation_Scene;///< Rotation matrix which set rotation angles of the scene around own center.
+		aiVector3D Translation_ToScene;///< Translation vector from camera to the scene.
+
+		/// \fn void SetDefault()
+		/// Set default parameters of camera.
+		void SetDefault();
+	};
+
+public:
+
+	/// \enum ELightType
+	/// Type of light source.
+	enum class ELightType { Directional, Point, Spot };
+
+	/// \struct SLightParameters
+	/// Parameters of light source.
+	struct SLightParameters
+	{
+		aiLightSourceType Type;///< Type of light source.
+
+		aiColor4D Ambient;///< Ambient RGBA intensity of the light.
+		aiColor4D Diffuse;///< Diffuse RGBA intensity of the light.
+		aiColor4D Specular;///< Specular RGBA intensity of the light.
+
+		union UFor
+		{
+			/// \struct SDirectional
+			/// Parameters of directional light source.
+			struct SDirectional
+			{
+				aiVector3D Direction;
+
+				SDirectional() {}
+			} Directional;
+
+			/// \struct SPoint
+			/// Parameters of point light source.
+			struct SPoint
+			{
+				aiVector3D Position;
+				GLfloat Attenuation_Constant;
+				GLfloat Attenuation_Linear;
+				GLfloat Attenuation_Quadratic;
+
+				SPoint() {}
+			} Point;
+
+			/// \struct SSpot
+			/// Parameters of spot light source.
+			struct SSpot
+			{
+				aiVector3D Position;
+				GLfloat Attenuation_Constant;
+				GLfloat Attenuation_Linear;
+				GLfloat Attenuation_Quadratic;
+				aiVector3D Direction;
+				GLfloat CutOff;
+
+				SSpot() {}
+			} Spot;
+
+			UFor() {}
+		} For;
+
+		SLightParameters() {}
+	};
+
+	/**********************************/
+	/************ Variables ***********/
+	/**********************************/
+
+private:
+
+	// Scene
+	const aiScene* mScene = nullptr;///< Copy of pointer to scene (\ref aiScene).
+	SBBox mScene_BBox;///< Bounding box of scene.
+	aiVector3D mScene_Center;///< Coordinates of center of the scene.
+	bool mScene_DrawBBox = false;///< Flag which control drawing scene BBox.
+	// Meshes
+	size_t mHelper_Mesh_Quantity = 0;///< Quantity of meshes in scene.
+	SHelper_Mesh** mHelper_Mesh = nullptr;///< Array of pointers to helper objects for drawing mesh. Sequence of meshes are equivalent to \ref aiScene::mMeshes.
+	// Cameras
+	SHelper_Camera mHelper_Camera;///< Information about current camera placing in space.
+	SHelper_Camera mHelper_CameraDefault;///< Information about default camera initial placing in space.
+	bool mCamera_DefaultAdded = true;///< If true then scene has no defined cameras and default was added, if false - scene has defined cameras.
+	GLdouble mCamera_FOVY = 45.0;///< Specifies the field of view angle, in degrees, in the y direction.
+	GLdouble mCamera_Viewport_AspectRatio;///< Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
+	// Lighting
+	bool mLightingEnabled = false;///< If true then OpenGL lighting is enabled (glEnable(GL_LIGHTING)), if false - disabled.
+	// Textures
+	QMap<QString, GLuint> mTexture_IDMap;///< Map image filenames to textures ID's.
+
+	/**********************************/
+	/************ Functions ***********/
+	/**********************************/
+
+private:
+
+	// Why in some cases pointers are used? Because: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566
+	template<typename TArg> void AssignIfLesser(TArg* pBaseValue, const TArg pTestValue) { if(pTestValue < *pBaseValue) *pBaseValue = pTestValue; }
+	template<typename TArg> void AssignIfGreater(TArg* pBaseValue, const TArg pTestValue) { if(pTestValue > *pBaseValue) *pBaseValue = pTestValue; }
+
+	template<typename TArg> void AssignIfLesser(TArg& pBaseValue, const TArg pTestValue) { if(pTestValue < pBaseValue) pBaseValue = pTestValue; }
+	template<typename TArg> void AssignIfGreater(TArg& pBaseValue, const TArg pTestValue) { if(pTestValue > pBaseValue) pBaseValue = pTestValue; }
+
+	/// \fn void Material_Apply(const aiMaterial* pMaterial)
+	/// Enable pointed material.
+	/// \param [in] pMaterial - pointer to material which must be used.
+	void Material_Apply(const aiMaterial* pMaterial);
+
+	/// \fn void Matrix_NodeToRoot(const aiNode* pNode, aiMatrix4x4& pOutMatrix)
+	/// Calculate matrix for transforming coordinates from pointed node to root node (read as "global coordinate system").
+	/// \param [in] pNode - pointer initial node from which relative coordintaes will be taken,
+	/// \param [out] pOutMatrix - matrix for transform relative coordinates in \ref pNode to coordinates in root node (\ref aiScene::mRootNode).
+	void Matrix_NodeToRoot(const aiNode* pNode, aiMatrix4x4& pOutMatrix);
+
+	/// \fn void ImportTextures()
+	/// Import textures.
+	/// \param [in] pScenePath - path to the file of the scene.
+	void ImportTextures(const QString& pScenePath);
+
+	/// \fn void BBox_GetForNode(const aiNode& pNode, const aiMatrix4x4& pParentNode_TransformationMatrix, SBBox& pNodeBBox, bool& pFirstAssign)
+	/// Calculate BBox for pointed node. Function walk thru child nodes and apply all transformations.
+	/// \param [in] pNode - reference to node for which needed BBox.
+	/// \param [in] pParent_TransformationMatrix - reference to parent (parent for pNode) transformation matrix.
+	/// \param [in,out] pNodeBBox - reference to where pNode BBox will be placed. It will expanded by child nodes BBoxes.
+	/// \param [in] pFirstAssign - means that pNodeBBox not contain valid BBox at now and assign ('=') will used for setting new value, If
+	/// false then \ref BBox_Extend will be used for setting new BBox.
+	void BBox_GetForNode(const aiNode& pNode, const aiMatrix4x4& pParent_TransformationMatrix, SBBox& pNodeBBox, bool& pFirstAssign);
+
+	/// \fn void BBox_Extend(const SBBox& pChild, SBBox& pParent)
+	/// Check and if need - extend current node BBox with BBox of child node.
+	/// \param [in] pChild - reference to BBox which used for extend parent BBox.
+	/// \param [in.out] pParent - BBox which will be extended using child BBox.
+	void BBox_Extend(const SBBox& pChild, SBBox& pParent);
+
+	/// \fn void BBox_GetVertices(const SBBox& pBBox, aiVector3D pVertices[8])
+	/// Get vertices of a parallelepiped which is described by BBox.
+	/// \param [in] pBBox - input BBox.
+	/// \param [out] pVertices - array of vertices.
+	void BBox_GetVertices(const SBBox& pBBox, aiVector3D pVertices[8]);
+
+	/// \fn void BBox_GetFromVertices(const aiVector3D* pVertices, const size_t pVerticesQuantity, SBBox& pBBox)
+	/// Calculate BBox for vertices array.
+	/// \param [in] pVertices - vertices array.
+	/// \param [in] pVerticesQuantity - quantity of vertices in array. If 0 then pBBox will be assigned with {{0, 0, 0}, {0, 0, 0}}.
+	/// \param [out] pBBox - calculated BBox.
+	void BBox_GetFromVertices(const aiVector3D* pVertices, const size_t pVerticesQuantity, SBBox& pBBox);
+
+	/********************************************************************/
+	/************************ Logging functions *************************/
+	/********************************************************************/
+
+	/// \fn void LogInfo(const QString& pMessage)
+	/// Add message with severity "Warning" to log.
+	void LogInfo(const QString& pMessage);
+
+	/// \fn void LogError(const QString& pMessage)
+	/// Add message with severity "Error" to log.
+	void LogError(const QString& pMessage);
+
+	/********************************************************************/
+	/************************** Draw functions **************************/
+	/********************************************************************/
+
+	/// \fn void Draw_Node(const aiNode* pNode)
+	/// Apply node transformation and draw meshes assigned to this node.
+	/// \param [in] pNode - pointer to node for drawing (\ref aiNode).
+	void Draw_Node(const aiNode* pNode);
+
+	/// \fn void Draw_Mesh(const size_t pMesh_Index)
+	/// Draw mesh.
+	/// \param [in] pMesh_Index - index of mesh which must be drawn. Index point to mesh in \ref mHelper_Mesh.
+	void Draw_Mesh(const size_t pMesh_Index);
+
+	/// \fn void Draw_BBox(const SBBox& pBBox)
+	/// Draw bounding box using lines.
+	/// \param [in] pBBox - bounding box for drawing.
+	void Draw_BBox(const SBBox& pBBox);
+
+	/********************************************************************/
+	/*********************** Overrided functions ************************/
+	/********************************************************************/
+
+protected:
+
+	/// \fn void initializeGL() override
+	/// Overrided function for initialise OpenGL.
+	void initializeGL() override;
+
+	/// \fn void resizeGL(int pWidth, int pHeight) override
+	/// \param [in] pWidth - new width of viewport.
+	/// \param [in] pHeight - new height of viewport.
+	void resizeGL(int pWidth, int pHeight) override;
+
+	/// \fn void paintGL() override
+	/// Overrided function for rendering.
+	void paintGL() override;
+
+public:
+
+	/********************************************************************/
+	/********************** Constructor/Destructor **********************/
+	/********************************************************************/
+
+	/// \fn explicit CGLView(QWidget* pParent)
+	/// Constructor.
+	/// \param [in] pParent - parent widget.
+	explicit CGLView(QWidget* pParent);
+
+	/// \fn virtual ~CGLView()
+	/// Destructor.
+	virtual ~CGLView();
+
+	/********************************************************************/
+	/********************* Scene control functions **********************/
+	/********************************************************************/
+
+	/// \fn void FreeScene()
+	/// Free all helper objects data.
+	void FreeScene();
+
+	/// \fn void SetScene(const aiScene* pScene)
+	/// Set scene for rendering.
+	/// \param [in] pScene - pointer to scene.
+	/// \param [in] pScenePath - path to the file of the scene.
+	void SetScene(const aiScene* pScene, const QString& pScenePath);
+
+	/// \fn void Enable_SceneBBox(const bool pEnable)
+	/// Enable drawing scene bounding box.
+	/// \param [in] pEnable - if true then bbox will be drawing, if false - will not be drawing.
+	void Enable_SceneBBox(const bool pEnable) { mScene_DrawBBox = pEnable; }
+
+	/// \fn void Enable_Textures(const bool pEnable)
+	/// Control textures drawing.
+	/// \param [in] pEnable - if true then enable textures, false - disable textures.
+	void Enable_Textures(const bool pEnable);
+
+	/********************************************************************/
+	/******************** Lighting control functions ********************/
+	/********************************************************************/
+
+	/// \fn void Lighting_Enable()
+	/// Enable OpenGL lighting.
+	void Lighting_Enable();
+
+	/// \fn void Lighting_Disable()
+	/// Disable OpenGL lighting.
+	void Lighting_Disable();
+
+	/// \fn void Lighting_EditSource(const size_t pLightNumber, const SLightParameters& pLightParameters)
+	/// Edit light source properties.
+	/// \param [in] pLightNumber - light source number. \ref aiScene::mLights.
+	/// \param [in] pLightParameters - light source parameters.
+	void Lighting_EditSource(const size_t pLightNumber, const SLightParameters& pLightParameters);///TODO: function set
+
+	/// \fn void Lighting_EnableSource(const size_t pLightNumber)
+	/// Enable light source.
+	/// \param [in] pLightNumber - light source number. \ref aiScene::mLights.
+	void Lighting_EnableSource(const size_t pLightNumber);
+
+	///void Lighting_DisableSource(const size_t pLightNumber)
+	/// Disable light source,
+	/// \param [in] pLightNumber - light source number. \ref aiScene::mLights.
+	void Lighting_DisableSource(const size_t pLightNumber);
+
+	/********************************************************************/
+	/******************** Cameras control functions *********************/
+	/********************************************************************/
+
+	/// \fn void Camera_Set(const size_t pCameraNumber)
+	/// Set view from pointed camera.
+	/// \param [in] pCamera_Index - index of the camera (\ref aiScene::mCameras).
+	void Camera_Set(const size_t pCameraNumber);
+
+	/// \fn void Camera_RotateScene(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z)
+	/// Rotate scene around axisees.
+	/// \param [in] pAngle_X - specifies the angle of rotation around axis oX, in degrees.
+	/// \param [in] pAngle_Y - specifies the angle of rotation around axis oY, in degrees.
+	/// \param [in] pAngle_Z - specifies the angle of rotation around axis oZ, in degrees.
+	void Camera_RotateScene(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z);
+
+	/// \fn void Camera_Rotate(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z)
+	/// Rotate camera around axisees.
+	/// \param [in] pAngle_X - specifies the angle of rotation around axis oX, in degrees.
+	/// \param [in] pAngle_Y - specifies the angle of rotation around axis oY, in degrees.
+	/// \param [in] pAngle_Z - specifies the angle of rotation around axis oZ, in degrees.
+	void Camera_Rotate(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z);
+
+	/// \fn void Camera_Translate(const size_t pTranslate_X, const size_t pTranslate_Y, const size_t pTranslate_Z)
+	/// Translate camera along axises. In local coordinates.
+	/// \param [in] pTranslate_X - specifies the X coordinate of translation vector.
+	/// \param [in] pTranslate_Y - specifies the Y coordinate of translation vector.
+	/// \param [in] pTranslate_Z - specifies the Z coordinate of translation vector.
+	void Camera_Translate(const GLfloat pTranslate_X, const GLfloat pTranslate_Y, const GLfloat pTranslate_Z);
+
+signals:
+
+	/// \fn void Paint_Finished(const size_t pPaintTime, const GLfloat pDistance)
+	///< Signal. Emits when execution of \ref paintGL is end.
+	/// \param [out] pPaintTime_ms - time spent for rendering, in milliseconds.
+	/// \param [out] pDistance - distance between current camera and center of the scene. \sa SHelper_Camera::Translation_ToScene.
+	void Paint_Finished(const size_t pPaintTime_ms, const GLfloat pDistance);
+
+	/// \fn void SceneObject_Camera(const QString& pName)
+	/// Signal. Emit for every camera found in scene. Also for default camera.
+	/// \param [out] pName - name of the camera.
+	void SceneObject_Camera(const QString& pName);
+
+	/// \fn void SceneObject_LightSource(const QString& pName)
+	/// Signal. Emit for every light source found in scene. Also for default light source.
+	/// \param [out] pName - name of the light source.
+	void SceneObject_LightSource(const QString& pName);
+};// class CGLView

+ 19 - 0
tools/assimp_qt_viewer/loggerview.cpp

@@ -0,0 +1,19 @@
+/// \file   loggerview.cpp
+/// \brief  Stream for Assimp logging subsystem.
+/// \author [email protected]
+/// \date   2016
+
+#include "loggerview.hpp"
+
+// Header files, Qt.
+#include <QTime>
+
+CLoggerView::CLoggerView(QTextBrowser* pOutputWidget)
+	: mOutputWidget(pOutputWidget)
+{
+}
+
+void CLoggerView::write(const char *pMessage)
+{
+	mOutputWidget->insertPlainText(QString("[%1] %2").arg(QTime::currentTime().toString()).arg(pMessage));
+}

+ 33 - 0
tools/assimp_qt_viewer/loggerview.hpp

@@ -0,0 +1,33 @@
+/// \file   loggerview.hpp
+/// \brief  Stream for Assimp logging subsystem.
+/// \author [email protected]
+/// \date   2016
+
+#pragma once
+
+// Header files, Qt.
+#include <QTextBrowser>
+
+// Header files, Assimp.
+#include <assimp/DefaultLogger.hpp>
+
+/// \class CLoggerView
+/// GUI-stream for Assimp logging subsytem. Get data for logging and write it to output widget.
+class CLoggerView final : public Assimp::LogStream
+{
+private:
+
+	QTextBrowser* mOutputWidget;///< Widget for displaying messages.
+
+public:
+
+	/// \fn explicit CLoggerView(QTextBrowser* pOutputWidget)
+	/// Constructor.
+	/// \param [in] pOutputWidget - pointer to output widget.
+	explicit CLoggerView(QTextBrowser* pOutputWidget);
+
+	/// \fn virtual void write(const char *pMessage)
+	/// Write message to output widget. Used by Assimp.
+	/// \param [in] pMessage - message for displaying.
+	virtual void write(const char *pMessage);
+};

+ 21 - 0
tools/assimp_qt_viewer/main.cpp

@@ -0,0 +1,21 @@
+/// \file   main.cpp
+/// \brief  Start-up file which contain function "main".
+/// \author [email protected]
+/// \date   2016
+// Thanks to acorn89 for support.
+
+// Header files, project.
+#include "mainwindow.hpp"
+
+// Header files, Qt.
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+QApplication a(argc, argv);
+MainWindow w;
+
+	w.show();
+
+	return a.exec();
+}

+ 344 - 0
tools/assimp_qt_viewer/mainwindow.cpp

@@ -0,0 +1,344 @@
+/// \file   mainwindow.hpp
+/// \brief  Main window and algorhytms.
+/// \author [email protected]
+/// \date   2016
+
+#include "mainwindow.hpp"
+#include "ui_mainwindow.h"
+
+// Header files, Assimp.
+#include <assimp/Exporter.hpp>
+#include <assimp/postprocess.h>
+
+#ifndef __unused
+	#define __unused	__attribute__((unused))
+#endif // __unused
+
+/**********************************/
+/************ Functions ***********/
+/**********************************/
+
+/********************************************************************/
+/********************* Import/Export functions **********************/
+/********************************************************************/
+
+void MainWindow::ImportFile(const QString &pFileName)
+{
+using namespace Assimp;
+
+QTime time_begin = QTime::currentTime();
+
+	if(mScene != nullptr)
+	{
+		mImporter.FreeScene();
+		mGLView->FreeScene();
+	}
+
+	// Try to import scene.
+	mScene = mImporter.ReadFile(pFileName.toStdString(), aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_ValidateDataStructure | \
+															aiProcess_GenUVCoords | aiProcess_TransformUVCoords | aiProcess_FlipUVs);
+	if(mScene != nullptr)
+	{
+		ui->lblLoadTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime())));
+		LogInfo("Import done: " + pFileName);
+		// Prepare widgets for new scene.
+		ui->leFileName->setText(pFileName.right(pFileName.length() - pFileName.lastIndexOf('/') - 1));
+		ui->lstLight->clear();
+		ui->lstCamera->clear();
+		ui->cbxLighting->setChecked(true), mGLView->Lighting_Enable();
+		ui->cbxBBox->setChecked(false); mGLView->Enable_SceneBBox(false);
+		ui->cbxTextures->setChecked(true), mGLView->Enable_Textures(true);
+		//
+		// Fill info labels
+		//
+		// Cameras
+		ui->lblCameraCount->setText(QString("%1").arg(mScene->mNumCameras));
+		// Lights
+		ui->lblLightCount->setText(QString("%1").arg(mScene->mNumLights));
+		// Meshes, faces, vertices.
+		size_t qty_face = 0;
+		size_t qty_vert = 0;
+
+		for(size_t idx_mesh = 0; idx_mesh < mScene->mNumMeshes; idx_mesh++)
+		{
+			qty_face += mScene->mMeshes[idx_mesh]->mNumFaces;
+			qty_vert += mScene->mMeshes[idx_mesh]->mNumVertices;
+		}
+
+		ui->lblMeshCount->setText(QString("%1").arg(mScene->mNumMeshes));
+		ui->lblFaceCount->setText(QString("%1").arg(qty_face));
+		ui->lblVertexCount->setText(QString("%1").arg(qty_vert));
+		// Animation
+		if(mScene->mNumAnimations)
+			ui->lblHasAnimation->setText("yes");
+		else
+			ui->lblHasAnimation->setText("no");
+
+		//
+		// Set scene for GL viewer.
+		//
+		mGLView->SetScene(mScene, pFileName);
+		// Select first camera
+		ui->lstCamera->setCurrentRow(0);
+		mGLView->Camera_Set(0);
+		// Scene is loaded, do first rendering.
+		LogInfo("Scene is ready for rendering.");
+		mGLView->updateGL();
+	}
+	else
+	{
+		ui->lblLoadTime->clear();
+		LogError(QString("Error parsing \'%1\' : \'%2\'").arg(pFileName).arg(mImporter.GetErrorString()));
+	}// if(mScene != nullptr)
+}
+
+/********************************************************************/
+/************************ Logging functions *************************/
+/********************************************************************/
+
+void MainWindow::LogInfo(const QString& pMessage)
+{
+	Assimp::DefaultLogger::get()->info(pMessage.toStdString());
+}
+
+void MainWindow::LogError(const QString& pMessage)
+{
+	Assimp::DefaultLogger::get()->error(pMessage.toStdString());
+}
+
+/********************************************************************/
+/*********************** Overrided functions ************************/
+/********************************************************************/
+
+void MainWindow::mousePressEvent(QMouseEvent* pEvent)
+{
+	if(pEvent->button() & Qt::LeftButton)
+		mPosition_Pressed_LMB = pEvent->pos();
+	else if(pEvent->button() & Qt::RightButton)
+		mPosition_Pressed_RMB = pEvent->pos();
+}
+
+void MainWindow::mouseMoveEvent(QMouseEvent* pEvent)
+{
+	if(pEvent->buttons() & Qt::LeftButton)
+	{
+		GLfloat dx = 180 * GLfloat(pEvent->x() - mPosition_Pressed_LMB.x()) / mGLView->width();
+		GLfloat dy = 180 * GLfloat(pEvent->y() - mPosition_Pressed_LMB.y()) / mGLView->height();
+
+		if(pEvent->modifiers() & Qt::ShiftModifier)
+			mGLView->Camera_RotateScene(dy, 0, dx);// Rotate around oX and oZ axises.
+		else
+			mGLView->Camera_RotateScene(dy, dx, 0);// Rotate around oX and oY axises.
+
+		mGLView->updateGL();
+		mPosition_Pressed_LMB = pEvent->pos();
+	}
+
+	if(pEvent->buttons() & Qt::RightButton)
+	{
+		GLfloat dx = 180 * GLfloat(pEvent->x() - mPosition_Pressed_RMB.x()) / mGLView->width();
+		GLfloat dy = 180 * GLfloat(pEvent->y() - mPosition_Pressed_RMB.y()) / mGLView->height();
+
+		if(pEvent->modifiers() & Qt::ShiftModifier)
+			mGLView->Camera_Rotate(dy, 0, dx);// Rotate around oX and oZ axises.
+		else
+			mGLView->Camera_Rotate(dy, dx, 0);// Rotate around oX and oY axises.
+
+		mGLView->updateGL();
+		mPosition_Pressed_RMB = pEvent->pos();
+	}
+}
+
+void MainWindow::keyPressEvent(QKeyEvent* pEvent)
+{
+GLfloat step;
+
+	if(pEvent->modifiers() & Qt::ControlModifier)
+		step = 10;
+	else if(pEvent->modifiers() & Qt::AltModifier)
+		step = 100;
+	else
+		step = 1;
+
+	if(pEvent->key() == Qt::Key_A)
+		mGLView->Camera_Translate(-step, 0, 0);
+	else if(pEvent->key() == Qt::Key_D)
+		mGLView->Camera_Translate(step, 0, 0);
+	else if(pEvent->key() == Qt::Key_W)
+		mGLView->Camera_Translate(0, step, 0);
+	else if(pEvent->key() == Qt::Key_S)
+		mGLView->Camera_Translate(0, -step, 0);
+	else if(pEvent->key() == Qt::Key_Up)
+		mGLView->Camera_Translate(0, 0, -step);
+	else if(pEvent->key() == Qt::Key_Down)
+		mGLView->Camera_Translate(0, 0, step);
+
+	mGLView->updateGL();
+}
+
+/********************************************************************/
+/********************** Constructor/Destructor **********************/
+/********************************************************************/
+
+MainWindow::MainWindow(QWidget *parent)
+	: QMainWindow(parent), ui(new Ui::MainWindow),
+		mScene(nullptr)
+{
+using namespace Assimp;
+
+	ui->setupUi(this);
+	// Create OpenGL widget
+	mGLView = new CGLView(this);
+	mGLView->setMinimumSize(800, 600);
+	mGLView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
+	mGLView->setFocusPolicy(Qt::StrongFocus);
+	// Connect to GLView signals.
+	connect(mGLView, SIGNAL(Paint_Finished(size_t, GLfloat)), SLOT(Paint_Finished(size_t, GLfloat)));
+	connect(mGLView, SIGNAL(SceneObject_Camera(QString)), SLOT(SceneObject_Camera(QString)));
+	connect(mGLView, SIGNAL(SceneObject_LightSource(QString)), SLOT(SceneObject_LightSource(QString)));
+	// and add it to layout
+	ui->hlMainView->insertWidget(0, mGLView, 4);
+	// Create logger
+	mLoggerView = new CLoggerView(ui->tbLog);
+	DefaultLogger::create("", Logger::VERBOSE);
+	DefaultLogger::get()->attachStream(mLoggerView, DefaultLogger::Debugging | DefaultLogger::Info | DefaultLogger::Err | DefaultLogger::Warn);
+}
+
+MainWindow::~MainWindow()
+{
+using namespace Assimp;
+
+	DefaultLogger::get()->detatchStream(mLoggerView, DefaultLogger::Debugging | DefaultLogger::Info | DefaultLogger::Err | DefaultLogger::Warn);
+	DefaultLogger::kill();
+
+	if(mScene != nullptr) mImporter.FreeScene();
+	if(mLoggerView != nullptr) delete mLoggerView;
+	if(mGLView != nullptr) delete mGLView;
+	delete ui;
+}
+
+/********************************************************************/
+/****************************** Slots *******************************/
+/********************************************************************/
+
+void MainWindow::Paint_Finished(const size_t pPaintTime_ms, const GLfloat pDistance)
+{
+	ui->lblRenderTime->setText(QString("%1").arg(pPaintTime_ms));
+	ui->lblDistance->setText(QString("%1").arg(pDistance));
+}
+
+void MainWindow::SceneObject_Camera(const QString& pName)
+{
+	ui->lstCamera->addItem(pName);
+}
+
+void MainWindow::SceneObject_LightSource(const QString& pName)
+{
+	ui->lstLight->addItem(pName);
+	// After item added "currentRow" is still contain old value (even '-1' if first item added). Because "currentRow"/"currentItem" is changed by user interaction,
+	// not by "addItem". So, "currentRow" must be set manually.
+	ui->lstLight->setCurrentRow(ui->lstLight->count() - 1);
+	// And after "selectAll" handler of "signal itemSelectionChanged" will get right "currentItem" and "currentRow" values.
+	ui->lstLight->selectAll();
+}
+
+void MainWindow::on_butOpenFile_clicked()
+{
+aiString filter_temp;
+QString filename, filter;
+
+	mImporter.GetExtensionList(filter_temp);
+	filter = filter_temp.C_Str();
+	filter.replace(';', ' ');
+	filter.append(" ;; All (*.*)");
+	filename = QFileDialog::getOpenFileName(this, "Choose the file", "", filter);
+
+	if(!filename.isEmpty()) ImportFile(filename);
+}
+
+
+void MainWindow::on_butExport_clicked()
+{
+using namespace Assimp;
+
+QString filename, filter, format_id;
+Exporter exporter;
+QTime time_begin;
+aiReturn rv;
+
+	if(mScene == nullptr)
+	{
+		QMessageBox::critical(this, "Export error", "Scene is empty");
+
+		return;
+	}
+
+	// build filter
+	{
+		aiString filter_temp;
+
+		mImporter.GetExtensionList(filter_temp);
+		filter = filter_temp.C_Str();
+		filter.replace(';', ' ');
+	}
+
+	// get file path
+	filename = QFileDialog::getSaveFileName(this, "Set file name", "", filter);
+	// extract format ID
+	format_id = filename.right(filename.length() - filename.lastIndexOf('.') - 1);
+	if(format_id.isEmpty())
+	{
+		QMessageBox::critical(this, "Export error", "File name must has extension.");
+
+		return;
+	}
+
+	// begin export
+	time_begin = QTime::currentTime();
+	rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit());
+	ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime())));
+	if(rv == aiReturn_SUCCESS)
+		LogInfo("Export done: " + filename);
+	else
+		LogError("Export failed: " + filename);
+}
+
+void MainWindow::on_cbxLighting_clicked(bool pChecked)
+{
+	if(pChecked)
+		mGLView->Lighting_Enable();
+	else
+		mGLView->Lighting_Disable();
+
+	mGLView->updateGL();
+}
+
+void MainWindow::on_lstLight_itemSelectionChanged()
+{
+bool selected = ui->lstLight->isItemSelected(ui->lstLight->currentItem());
+
+	if(selected)
+		mGLView->Lighting_EnableSource(ui->lstLight->currentRow());
+	else
+		mGLView->Lighting_DisableSource(ui->lstLight->currentRow());
+
+	mGLView->updateGL();
+}
+
+void MainWindow::on_lstCamera_clicked(__unused const QModelIndex &index)
+{
+	mGLView->Camera_Set(ui->lstLight->currentRow());
+	mGLView->updateGL();
+}
+
+void MainWindow::on_cbxBBox_clicked(bool checked)
+{
+	mGLView->Enable_SceneBBox(checked);
+	mGLView->updateGL();
+}
+
+void MainWindow::on_cbxTextures_clicked(bool checked)
+{
+	mGLView->Enable_Textures(checked);
+	mGLView->updateGL();
+}

+ 131 - 0
tools/assimp_qt_viewer/mainwindow.hpp

@@ -0,0 +1,131 @@
+/// \file   mainwindow.hpp
+/// \brief  Main window and algorhytms.
+/// \author [email protected]
+/// \date   2016
+
+#pragma once
+
+// Header files, Qt.
+#include <QMainWindow>
+
+// Header files, project.
+#include "glview.hpp"
+#include "loggerview.hpp"
+
+// Header files, Assimp.
+#include <assimp/Importer.hpp>
+#include <assimp/scene.h>
+
+namespace Ui { class MainWindow; }
+
+/// \class MainWindow
+/// Main window and algorhytms.
+class MainWindow : public QMainWindow
+{
+	Q_OBJECT
+
+	/**********************************/
+	/************ Variables ***********/
+	/**********************************/
+
+private:
+
+	Ui::MainWindow *ui;
+
+	CGLView* mGLView;///< Pointer to OpenGL render.
+	CLoggerView* mLoggerView;///< Pointer to logging object.
+	Assimp::Importer mImporter;///< Assimp importer.
+	const aiScene* mScene;///< Pointer to loaded scene (\ref aiScene).
+	QPoint mPosition_Pressed_LMB;///< Position where was pressed left mouse button.
+	QPoint mPosition_Pressed_RMB;///< Position where was pressed right mouse button.
+
+	/**********************************/
+	/************ Functions ***********/
+	/**********************************/
+
+	/********************************************************************/
+	/********************* Import/Export functions **********************/
+	/********************************************************************/
+
+	/// \fn void ImportFile(const QString& pFileName)
+	/// Import scene from file.
+	/// \param [in] pFileName - path and name of the file.
+	void ImportFile(const QString& pFileName);
+
+	/********************************************************************/
+	/************************ Logging functions *************************/
+	/********************************************************************/
+
+	/// \fn void LogInfo(const QString& pMessage)
+	/// Add message with severity "Warning" to log.
+	void LogInfo(const QString& pMessage);
+
+	/// \fn void LogError(const QString& pMessage)
+	/// Add message with severity "Error" to log.
+	void LogError(const QString& pMessage);
+
+	/********************************************************************/
+	/*********************** Overrided functions ************************/
+	/********************************************************************/
+
+protected:
+
+	/// \fn void mousePressEvent(QMouseEvent* pEvent) override
+	/// Overrided function which handle mouse event "button pressed".
+	/// \param [in] pEvent - pointer to event data.
+	void mousePressEvent(QMouseEvent* pEvent) override;
+
+	/// \fn void mouseMoveEvent(QMouseEvent* pEvent) override
+	/// Overrided function which handle mouse event "move".
+	/// \param [in] pEvent - pointer to event data.
+	void mouseMoveEvent(QMouseEvent* pEvent) override;
+
+	/// \fn void keyPressEvent(QKeyEvent* pEvent) override
+	/// Overrided function which handle key event "key pressed".
+	/// \param [in] pEvent - pointer to event data.
+	void keyPressEvent(QKeyEvent* pEvent) override;
+
+
+public:
+
+	/********************************************************************/
+	/********************** Constructor/Destructor **********************/
+	/********************************************************************/
+
+	/// \fn explicit MainWindow(QWidget* pParent = 0)
+	/// \param [in] pParent - pointer to parent widget.
+	explicit MainWindow(QWidget* pParent = 0);
+
+	/// \fn ~MainWindow()
+	/// Destructor.
+	~MainWindow();
+
+	/********************************************************************/
+	/****************************** Slots *******************************/
+	/********************************************************************/
+
+private slots:
+
+	/// \fn void Paint_Finished(const int pPaintTime)
+	/// Show paint/render time and distance between camera and center of the scene.
+	/// \param [in] pPaintTime_ms - paint time in milliseconds.
+	void Paint_Finished(const size_t pPaintTime_ms, const GLfloat pDistance);
+
+	/// \fn void SceneObject_Camera(const QString& pName)
+	/// Add camera name to list.
+	/// \param [in] pName - name of the camera.
+	void SceneObject_Camera(const QString& pName);
+
+	/// \fn void SceneObject_LightSource(const QString& pName)
+	/// Add lighting source name to list.
+	/// \param [in] pName - name of the light source,
+	void SceneObject_LightSource(const QString& pName);
+
+	void on_butOpenFile_clicked();
+	void on_butExport_clicked();
+	void on_cbxLighting_clicked(bool pChecked);
+	void on_lstLight_itemSelectionChanged();
+	void on_lstCamera_clicked(const QModelIndex &index);
+	void on_cbxBBox_clicked(bool checked);
+	void on_cbxTextures_clicked(bool checked);
+};

+ 516 - 0
tools/assimp_qt_viewer/mainwindow.ui

@@ -0,0 +1,516 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>641</width>
+    <height>734</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QVBoxLayout" name="verticalLayout_2">
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout" stretch="5,1">
+      <item>
+       <layout class="QHBoxLayout" name="hlMainView" stretch="0">
+        <property name="sizeConstraint">
+         <enum>QLayout::SetDefaultConstraint</enum>
+        </property>
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QGroupBox" name="grpFile">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="title">
+             <string>File</string>
+            </property>
+            <layout class="QFormLayout" name="formLayout_2">
+             <item row="0" column="0" colspan="2">
+              <widget class="QPushButton" name="butOpenFile">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Open file</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QLabel" name="lblFileName_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>File name</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="1">
+              <widget class="QLineEdit" name="leFileName">
+               <property name="maximumSize">
+                <size>
+                 <width>160</width>
+                 <height>16777215</height>
+                </size>
+               </property>
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="frame">
+                <bool>false</bool>
+               </property>
+               <property name="readOnly">
+                <bool>true</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QLabel" name="lblLoadTime_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Load time, s</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="1">
+              <widget class="QLabel" name="lblLoadTime">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="4" column="0" colspan="2">
+              <widget class="Line" name="line">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+              </widget>
+             </item>
+             <item row="5" column="0" colspan="2">
+              <widget class="QPushButton" name="butExport">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Export</string>
+               </property>
+              </widget>
+             </item>
+             <item row="6" column="0">
+              <widget class="QLabel" name="lblExportTime_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Export time, s</string>
+               </property>
+              </widget>
+             </item>
+             <item row="6" column="1">
+              <widget class="QLabel" name="lblExportTime">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="grpInfo">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="title">
+             <string>Info</string>
+            </property>
+            <layout class="QFormLayout" name="formLayout_4">
+             <item row="0" column="0">
+              <widget class="QLabel" name="lblRenderTime_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Render time, ms</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="1">
+              <widget class="QLabel" name="lblRenderTime">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QLabel" name="lblMeshCount_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Meshes</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QLabel" name="lblFaceCount_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Faces</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="1">
+              <widget class="QLabel" name="lblMeshCount">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="1">
+              <widget class="QLabel" name="lblFaceCount">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="3" column="0">
+              <widget class="QLabel" name="lblVertexCount_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Vertices</string>
+               </property>
+              </widget>
+             </item>
+             <item row="3" column="1">
+              <widget class="QLabel" name="lblVertexCount">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="4" column="0">
+              <widget class="QLabel" name="lblLightCount_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Lights</string>
+               </property>
+              </widget>
+             </item>
+             <item row="5" column="0">
+              <widget class="QLabel" name="lblCameraCount_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Cameras</string>
+               </property>
+              </widget>
+             </item>
+             <item row="7" column="0">
+              <widget class="QLabel" name="lblHasAnimation_Label">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Animation</string>
+               </property>
+              </widget>
+             </item>
+             <item row="6" column="0">
+              <widget class="QLabel" name="lblShaderCount_Label">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Shaders</string>
+               </property>
+              </widget>
+             </item>
+             <item row="4" column="1">
+              <widget class="QLabel" name="lblLightCount">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+             <item row="5" column="1">
+              <widget class="QLabel" name="lblCameraCount">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+             <item row="6" column="1">
+              <widget class="QLabel" name="lblShaderCount">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+             <item row="7" column="1">
+              <widget class="QLabel" name="lblHasAnimation">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+             <item row="8" column="0">
+              <widget class="QLabel" name="lblDistance_Label">
+               <property name="text">
+                <string>Distance</string>
+               </property>
+              </widget>
+             </item>
+             <item row="8" column="1">
+              <widget class="QLabel" name="lblDistance">
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="grpDynamics">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="title">
+             <string>Dynamics</string>
+            </property>
+            <layout class="QGridLayout" name="gridLayout">
+             <item row="0" column="0">
+              <widget class="QPushButton" name="butAnimationStart">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Animation start</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QPushButton" name="butAnimationStop">
+               <property name="focusPolicy">
+                <enum>Qt::NoFocus</enum>
+               </property>
+               <property name="text">
+                <string>Animation stop</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QTabWidget" name="tabInfoAndControl">
+        <property name="focusPolicy">
+         <enum>Qt::NoFocus</enum>
+        </property>
+        <property name="currentIndex">
+         <number>2</number>
+        </property>
+        <widget class="QWidget" name="tab">
+         <attribute name="title">
+          <string>Log</string>
+         </attribute>
+         <layout class="QGridLayout" name="gridLayout_2">
+          <item row="0" column="0">
+           <widget class="QTextBrowser" name="tbLog">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>0</height>
+             </size>
+            </property>
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="tab_2">
+         <attribute name="title">
+          <string>Lights and cameras</string>
+         </attribute>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <widget class="QListWidget" name="lstLight">
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="toolTip">
+             <string>Light sources of the scene</string>
+            </property>
+            <property name="editTriggers">
+             <set>QAbstractItemView::SelectedClicked</set>
+            </property>
+            <property name="showDropIndicator" stdset="0">
+             <bool>false</bool>
+            </property>
+            <property name="selectionMode">
+             <enum>QAbstractItemView::MultiSelection</enum>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QListWidget" name="lstCamera">
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="toolTip">
+             <string>Cameras of the scene</string>
+            </property>
+            <property name="editTriggers">
+             <set>QAbstractItemView::NoEditTriggers</set>
+            </property>
+            <property name="showDropIndicator" stdset="0">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="tab_3">
+         <attribute name="title">
+          <string>Control</string>
+         </attribute>
+         <layout class="QGridLayout" name="gridLayout_3">
+          <item row="0" column="0">
+           <widget class="QCheckBox" name="cbxLighting">
+            <property name="toolTip">
+             <string>Enable/Disable OpenGL lighting</string>
+            </property>
+            <property name="text">
+             <string>Lighting</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QCheckBox" name="cbxBBox">
+            <property name="text">
+             <string>Scene BBox</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QCheckBox" name="cbxTextures">
+            <property name="text">
+             <string>Textures</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>