/// \file mainwindow.hpp /// \brief Main window and algorhytms. /// \author smal.root@gmail.com /// \date 2016 #include "mainwindow.hpp" #include "ui_mainwindow.h" // Header files, Assimp. #include #include #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()); } /********************************************************************/ /*********************** Override 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(), aiProcess_FlipUVs); 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( const QModelIndex &) { 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(); }