/* --------------------------------------------------------------------------- Open Asset Import Library (assimp) --------------------------------------------------------------------------- Copyright (c) 2006-2018, assimp team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the assimp team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the assimp team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ #include "glview.hpp" // Header files, Qt. #include // Header files, OpenGL. #if defined(__APPLE__) # include #else # include #endif // Header files, DevIL. // Header files, Assimp. #include #define STB_IMAGE_IMPLEMENTATION #include "contrib/stb_image/stb_image.h" 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() { delete [] Index_Point; delete [] Index_Line; delete [] Index_Triangle; } 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); } static void 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; } static void 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; } 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; ///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 ai_real 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 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); } // multiply all matrices in reverse order for ( std::list::reverse_iterator rit = mat_list.rbegin(); rit != mat_list.rend(); ++rit) { pOutMatrix = pOutMatrix * (*rit); } } void CGLView::ImportTextures(const QString& scenePath) { auto LoadTexture = [&](const QString& pFileName) -> bool ///TODO: IME texture mode, operation. { GLuint id_ogl_texture;// OpenGL texture ID. if(!pFileName.startsWith(AI_EMBEDDED_TEXNAME_PREFIX)) { QString basepath = scenePath.left(scenePath.lastIndexOf('/') + 1);// path with '/' at the end. QString fileloc = (basepath + pFileName); fileloc.replace('\\', "/"); int x, y, n; unsigned char *data = stbi_load(fileloc.toLocal8Bit(), &x, &y, &n, STBI_rgb_alpha ); if ( nullptr == data ) { LogError(QString("Couldn't load Image: %1").arg(fileloc)); return false; } // Convert every colour component into unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA. glGenTextures(1, &id_ogl_texture);// Texture ID generation. mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map glBindTexture(GL_TEXTURE_2D, id_ogl_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, n, x, y, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, data );// Texture specification. stbi_image_free(data); // Cleanup } else { struct SPixel_Description { const char* FormatHint; const GLint Image_InternalFormat; const GLint Pixel_Format; }; constexpr SPixel_Description Pixel_Description[] = { {"rgba8880", GL_RGB, GL_RGB}, {"rgba8888", GL_RGBA, GL_RGBA} }; constexpr size_t Pixel_Description_Count = sizeof(Pixel_Description) / sizeof(SPixel_Description); size_t idx_description; // Get texture index. bool ok; size_t idx_texture = pFileName.right(strlen(AI_EMBEDDED_TEXNAME_PREFIX)).toULong(&ok); if(!ok) { LogError("Can not get index of the embedded texture from path in material."); return false; } // Create alias for conveniance. const aiTexture& als = *mScene->mTextures[idx_texture]; if(als.mHeight == 0)// Compressed texture. { LogError("IME: compressed embedded textures are not implemented."); } else { ok = false; for(size_t idx = 0; idx < Pixel_Description_Count; idx++) { if(als.CheckFormat(Pixel_Description[idx].FormatHint)) { idx_description = idx; ok = true; break; } } if(!ok) { LogError(QString("Unsupported format hint for embedded texture: [%1]").arg(als.achFormatHint)); return false; } glGenTextures(1, &id_ogl_texture);// Texture ID generation. mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map glBindTexture(GL_TEXTURE_2D, id_ogl_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. // Texture specification. glTexImage2D(GL_TEXTURE_2D, 0, Pixel_Description[idx_description].Image_InternalFormat, als.mWidth, als.mHeight, 0, Pixel_Description[idx_description].Pixel_Format, GL_UNSIGNED_BYTE, (uint8_t*)als.pcData); }// if(als.mHeight == 0) else }// if(!filename.startsWith(AI_EMBEDDED_TEXNAME_PREFIX)) else return true; };// auto LoadTexture = [&](const aiString& pPath) if(mScene == nullptr) { LogError("Trying to load textures for empty scene."); return; } // // Load textures. // // 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; } LoadTexture(QString(path.C_Str())); idx_texture++; } while(true); }// for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++) // Textures list is empty, exit. if(mTexture_IDMap.empty()) { LogInfo("No textures for import."); } } 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 ai_real x = pVertices[idx_vert].x; const ai_real y = pVertices[idx_vert].y; const ai_real 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); } } void CGLView::LogInfo(const QString& pMessage) { Assimp::DefaultLogger::get()->info(pMessage.toStdString()); } void CGLView::LogError(const QString& pMessage) { Assimp::DefaultLogger::get()->error(pMessage.toStdString()); } void CGLView::Draw_Node(const aiNode* pNode) { aiMatrix4x4 mat_node = pNode->mTransformation; // Apply node transformation matrix. mat_node.Transpose(); glPushMatrix(); #ifdef ASSIMP_DOUBLE_PRECISION glMultMatrixd((GLdouble*)mat_node[0]); #else glMultMatrixf((GLfloat*)&mat_node); #endif // ASSIMP_DOUBLE_PRECISION // 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); #if ASSIMP_DOUBLE_PRECISION glVertexPointer(3, GL_DOUBLE, 0, mesh_cur.mVertices); #else glVertexPointer(3, GL_FLOAT, 0, mesh_cur.mVertices); #endif // ASSIMP_DOUBLE_PRECISION if(mesh_cur.HasVertexColors(0)) { glEnable(GL_COLOR_MATERIAL);///TODO: cache glEnableClientState(GL_COLOR_ARRAY); #ifdef ASSIMP_DOUBLE_PRECISION glColorPointer(4, GL_DOUBLE, 0, mesh_cur.mColors[0]); #else glColorPointer(4, GL_FLOAT, 0, mesh_cur.mColors[0]); #endif // ASSIMP_DOUBLE_PRECISION } // // Texture coordinates array // if(mesh_cur.HasTextureCoords(0)) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); #ifdef ASSIMP_DOUBLE_PRECISION glTexCoordPointer(2, GL_DOUBLE, sizeof(aiVector3D), mesh_cur.mTextureCoords[0]); #else glTexCoordPointer(2, GL_FLOAT, sizeof(aiVector3D), mesh_cur.mTextureCoords[0]); #endif // ASSIMP_DOUBLE_PRECISION } // // Normals array // if(mesh_cur.HasNormals()) { glEnableClientState(GL_NORMAL_ARRAY); #ifdef ASSIMP_DOUBLE_PRECISION glNormalPointer(GL_DOUBLE, 0, mesh_cur.mNormals); #else glNormalPointer(GL_FLOAT, 0, mesh_cur.mNormals); #endif // ASSIMP_DOUBLE_PRECISION } // // 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); const QColor c_w(Qt::white); glColor3f(c_w.redF(), c_w.greenF(), c_w.blueF()); glBegin(GL_LINE_STRIP); # ifdef ASSIMP_DOUBLE_PRECISION glVertex3dv(&vertex[0][0]), glVertex3dv(&vertex[1][0]), glVertex3dv(&vertex[2][0]), glVertex3dv(&vertex[3][0]), glVertex3dv(&vertex[0][0]);// "Minimum" side. glVertex3dv(&vertex[4][0]), glVertex3dv(&vertex[5][0]), glVertex3dv(&vertex[6][0]), glVertex3dv(&vertex[7][0]), glVertex3dv(&vertex[4][0]);// Edge and "maximum" side. # else 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. # endif // ASSIMP_DOUBLE_PRECISION glEnd(); glBegin(GL_LINES); # ifdef ASSIMP_DOUBLE_PRECISION glVertex3dv(&vertex[1][0]), glVertex3dv(&vertex[5][0]); glVertex3dv(&vertex[2][0]), glVertex3dv(&vertex[6][0]); glVertex3dv(&vertex[3][0]), glVertex3dv(&vertex[7][0]); # else glVertex3fv(&vertex[1][0]), glVertex3fv(&vertex[5][0]); glVertex3fv(&vertex[2][0]), glVertex3fv(&vertex[6][0]); glVertex3fv(&vertex[3][0]), glVertex3fv(&vertex[7][0]); # endif // ASSIMP_DOUBLE_PRECISION 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); } } void CGLView::initializeGL() { mGLContext_Current = true; initializeOpenGLFunctions(); glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_TEXTURE_2D); glEnable( GL_MULTISAMPLE ); 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 width, int height) { mCamera_Viewport_AspectRatio = (GLdouble)width / height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(mCamera_FOVY, mCamera_Viewport_AspectRatio, 1.0, 100000.0);///TODO: znear/zfar depend on scene size. } void CGLView::drawCoordSystem() { // Disable lighting. Colors must be bright and colorful) if ( mLightingEnabled ) glDisable( GL_LIGHTING );///TODO: display list // For same reason - disable textures. glBindTexture(GL_TEXTURE_1D, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_3D, 0); glEnable(GL_COLOR_MATERIAL); glBegin(GL_LINES); // X, -X glColor3f(1.0f, 0.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(100000.0, 0.0, 0.0); glColor3f(0.5f, 0.5f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(-100000.0, 0.0, 0.0); // Y, -Y glColor3f(0.0f, 1.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 100000.0, 0.0); glColor3f(1.0f, 0.0f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, -100000.0, 0.0); // Z, -Z glColor3f(0.0f, 0.0f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, 100000.0); glColor3f(1.0f, 1.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, -100000.0); glColor3f(1.0f, 1.0f, 1.0f); glEnd(); // Restore previous state of lighting. if (mLightingEnabled) { glEnable( GL_LIGHTING ); } } 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. #if ASSIMP_DOUBLE_PRECISION glMultMatrixd((GLdouble*)&mHelper_Camera.Rotation_AroundCamera); glTranslated(-mHelper_Camera.Translation_ToScene.x, -mHelper_Camera.Translation_ToScene.y, -mHelper_Camera.Translation_ToScene.z); glMultMatrixd((GLdouble*)&mHelper_Camera.Rotation_Scene); #else 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); #endif // ASSIMP_DOUBLE_PRECISION // Coordinate system if ( mScene_AxesEnabled ) { drawCoordSystem(); } glDisable(GL_COLOR_MATERIAL); // 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()); } CGLView::CGLView( QWidget *pParent ) : QOpenGLWidget( pParent ) , mGLContext_Current( false ) { // set initial view mHelper_CameraDefault.SetDefault(); Camera_Set( 0 ); } CGLView::~CGLView() { FreeScene(); } 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::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 through 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 splitted 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 through 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 // Use index if name is empty. if (name.isEmpty()) { name += QString( "%1" ).arg( idx_light ); } 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 } 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; // Ambient color farr[0] = pLightParameters.Ambient.r; farr[1] = pLightParameters.Ambient.g; farr[2] = pLightParameters.Ambient.b; farr[3] = pLightParameters.Ambient.a; glLightfv(light_num, GL_AMBIENT, farr); // Diffuse color farr[0] = pLightParameters.Diffuse.r; farr[1] = pLightParameters.Diffuse.g; farr[2] = pLightParameters.Diffuse.b; farr[3] = pLightParameters.Diffuse.a; glLightfv(light_num, GL_DIFFUSE, farr); // Specular color farr[0] = pLightParameters.Specular.r; farr[1] = pLightParameters.Specular.g; farr[2] = pLightParameters.Specular.b; farr[3] = pLightParameters.Specular.a; glLightfv(light_num, GL_SPECULAR, farr); // Other parameters switch(pLightParameters.Type) { case aiLightSource_DIRECTIONAL: // Direction farr[0] = pLightParameters.For.Directional.Direction.x, farr[1] = 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[1] = 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[1] = 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[1] = 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[1] = 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); } 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, const aiMatrix4x4* pMatrix_Rotation_Initial) { auto deg2rad = [](const GLfloat pDegree) -> GLfloat { return pDegree * AI_MATH_PI / 180.0f; }; aiMatrix4x4 mat_rot; mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z)); if(pMatrix_Rotation_Initial != nullptr) mHelper_Camera.Rotation_Scene = *pMatrix_Rotation_Initial * mat_rot; else mHelper_Camera.Rotation_Scene *= mat_rot; } void CGLView::Camera_Rotate(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z, const aiMatrix4x4* pMatrix_Rotation_Initial) { auto deg2rad = [](const GLfloat pDegree) -> GLfloat { return pDegree * AI_MATH_PI / 180.0; }; aiMatrix4x4 mat_rot; mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z)); if(pMatrix_Rotation_Initial != nullptr) mHelper_Camera.Rotation_AroundCamera = *pMatrix_Rotation_Initial * mat_rot; else 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; } void CGLView::Camera_Matrix(aiMatrix4x4& pRotation_Camera, aiMatrix4x4& pRotation_Scene, aiVector3D& pTranslation_Camera) { pRotation_Camera = mHelper_Camera.Rotation_AroundCamera; pRotation_Scene = mHelper_Camera.Rotation_Scene; pTranslation_Camera = mHelper_Camera.Translation_ToScene; }