|
@@ -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;
|
|
|
|
+}
|