3D-s számítógépes geometria és alakzatrekonstrukció 3a. Tesztkörnyezet I http://cg.iit.bme.hu/portal/node/312 https://portal.vik.bme.hu/kepzes/targyak/viiima01 Dr. Várady Tamás, Salvi Péter BME, Villamosmérnöki és Informatikai Kar Irányítástechnika és Informatika Tanszék
Tartalom Bevezetés Qt alapismeretek Minimális program felépítése QMainWindow (menük, status bar stb.) QProgressBar libqglviewer/opengl Alapvető OpenGL rajzolás OpenGL ablak beillesztése
Áttekintés Cél: tesztkörnyezet felépítése Segítség az önálló feladatokhoz Sokrétű ismeret szükséges: C++ OpenGL Qt / libqglviewer OpenMesh Kevés elmélet, sok gyakorlat Házifeladat
A tesztkörnyezet Alapvető felhasználói eszközök Ablak- és menühasználat, progress bar stb. 3D tér forgatása, kameraállapot/kép mentése, háromszöghálós és kitöltött megjelenítés Pontok mozgatása a térben Alapvető programozói eszközök Pontok, vektorok, háromszöghálók kezelése Pontok, szakaszok és sokszögek megjelenítése Színezés, textúrázás Néhány algoritmus (pl. görbületszámítás, simítás, Bézier felület kiértékelés)
Demó Forráskód: [Mercurial repo] https://bitbucket.org/salvipeter/sample-framework/ Órai feldolgozás több fázisban A fázisok forráskódja: https://www.iit.bme.hu/~salvi/index.html Platform-független Tesztelve: Linux, Windows Linux virtuális gépen ajánlott BME Cloud: http://cloud.bme.hu/ Probléma esetén: salvi@iit.bme.hu Bugok, javítási javaslatok ugyanide
Első fázis Mit tud? Menük, status bar Négyzet kirajzolása Forgatás, nagyítás, mozgatás Help, teljes képernyő, kamera/kép mentése 'p' megnyomására progress bar teszt Ehhez: Qt QGLViewer OpenGL
Qt Bevezető Honlap: http://www.qt.io/ Trolltech Nokia Digia Qt Company Nem kereskedelmi célra ingyenes Platform-független GUI Szuper dokumentáció (http://doc.qt.io/) Saját IDE: Qt Creator De összekapcsolható a Visual Studióval is Tud Makefile-t generálni [qmake] Online help, tutorialok, demók GUI szerkesztő
Qt és C++ Saját STL könyvtár std::string QString Saját konténerek (QList, QVector, QMap stb.) Smart pointerek (weak/strong) QObject ősosztály Signal-Slot kommunikáció [ld. később] Automatikus fába rendeződés (parent-child) A szülők halálukkor megölik a gyerekeiket tr("...") a szövegek fordításához Ehhez: Q_OBJECT makró (előfeldolgozás: moc) Project fájl (.pro)
Project fájl Benne: Beállítások (pl. Release/Debug) Forrásfájlok nevei Könyvtárak neve, elérési útja Resource-ok Programozható (!) Ebből készülhet: Visual Studio projekt fájl (.vcproj) Makefile moc lefut a megfelelő fájlokra
Qt elnevezési séma Osztályok: QSomeClass ( #include <QSomeClass> ) Konstansok: QSomeClass::EnumType ValueOne, ValueTwo; Qt::EnumType Qt::ValueOne, Qt::ValueTwo; Metódusok: QSomeClass::someMethod(...) Tulajdonságok: QSomeClass::someProperty() QSomeClass::setSomeProperty(...)
A Signal-Slot kommunikáció (1) Események (event): Minden GUI mozgató rugója Felhasználói események (egér mozgatása, klikkelés, gépelés, gomb megnyomása stb.) Független események (időzítés, egy widget állapotának megváltozása stb.) Cél: Reagálás az eseményre Reakció lehet független widgetben Hagyományos megoldás: Callback függvény (paraméterátadás problémás, nem type-safe)
A Signal-Slot kommunikáció (2) Signal: Valamilyen eseményt jelez Osztály deklarálásánál signals: Jelzés az emit paranccsal Paraméterezhető Slot: Osztály deklarálásánál slots: Összekapcsoláshoz connect makró Paramétertípusoknak egyezni kell Mindkét oldalon lehet többszörös (egy signalhoz több slot, egy slothoz több signal)
Programozás Qt-val Fő ablak osztály Egy Qt osztályból származtatva Q_OBJECT Kibővítve (új signalok, slotok) Testreszabva (virtuális metódusok újraírása) Főprogram QApplication meghívása Ez felelős a GUI vezérléséért Projekt fájl készítés Fordítás
A program main.cpp: int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWindow window; window.show(); return app.exec(); } MyWindow.h: #pragma once #include <QMainWindow> class MyWindow : public QMainWindow { Q_OBJECT public: MyWindow(); ~MyWindow(); };
MyWindow.cpp #include <QMenuBar> #include <QStatusBar> #include "MyWindow.h" MyWindow::MyWindow() : QMainWindow() { setwindowtitle(tr("sample 3D Framework")); setstatusbar(new QStatusBar); ///////////////////////// // Setup actions/menus // ///////////////////////// QAction *quitaction = new QAction(tr("&Quit"), this); quitaction->setshortcut(tr("ctrl+q")); quitaction->setstatustip(tr("quit the program")); connect(quitaction, SIGNAL(triggered()), this, SLOT(close())); } QMenu *filemenu = menubar()->addmenu(tr("&file")); filemenu->addaction(quitaction); MyWindow::~MyWindow() { }
Progress Bar hozzáadása A program eddig: Progress Bar kezelés: Csak a folyamat alatt jelenik meg Három slot: start, mid, end Nem kell mindegyiket használni (pl. gomb megnyomása / számolás vége) Időt kell szakítani a grafika frissítésére (QApplication::processEvents(...))
Változások MyWindow.h:... class QApplication ; class QProgressBar ; class MyWindow : public QMainWindow { Q_OBJECT public: MyWindow(QApplication *parent);... private slots : void startcomputation (QString message); void midcomputation (int percent); void endcomputation (); private: QApplication *parent; MyViewer *viewer; QProgressBar *progress; };
Változások MyWindow.cpp: #include <QApplication> #include <QProgressBar>... MyWindow::MyWindow(QApplication *parent) : QMainWindow(), parent(parent) {... progress = new QProgressBar; progress->setminimum(0); progress->setmaximum(100); progress->hide(); statusbar()->addpermanentwidget(progress);... } void MyWindow::startComputation(QString message) { statusbar()->showmessage(message); progress->setvalue(0); progress->show(); parent->processevents(qeventloop::excludeuserinputevents); } void MyWindow::midComputation(int percent) { progress->setvalue(percent); parent->processevents(qeventloop::excludeuserinputevents); } void MyWindow::endComputation() { progress->hide(); statusbar()->clearmessage(); }
libqglviewer Honlap: http://www.libqglviewer.com/ Kiegészítő könyvtár Qt-hez OpenGL ablakkezelés Qt környezetben Vektor osztály (egyben pont osztály is) Kamera osztály (mátrixtranszformációk) Sok hasznos funkció Kameramozgatás 3D kiválasztás Teljes képernyőre váltás Stb. (ld. Help)
Származtatás a QGLViewerből MyViewer.h: #pragma once #include <QGLViewer/qglviewer.h> class MyViewer : public QGLViewer { Q_OBJECT public: MyViewer(QWidget *parent); virtual ~MyViewer(); signals: void startcomputation(qstring message); void midcomputation(int percent); void endcomputation(); protected: virtual void init(); virtual void draw(); virtual void keypressevent(qkeyevent *e); virtual QString helpstring() const; };
void init(): Virtuális függvények OpenGL beállítások inicializáláskor, pl.: gllightmodeli(gl_light_model_two_side, 1); [mindkét oldal legyen megvilágítva] void draw(): OpenGL kirajzolás void keypressevent(qkeyevent *): Eseménykezelő billentyű leütésekor QString helpstring() const: A helpben megjelenítendő szöveg
OpenGL rajzolás Nézet (camera) kezelése libqglviewer Alapból [-1,1]x[-1,1] XY síkra néz Általános struktúra: glbegin(objektum_típus); glvertex3f(float, float, float);... glend(); GL_POINTS, GL_LINES, GL_POLYGON stb....[234][sifd]v? 2/3/4: dimenzió (4: homogén koordináták) s/i/f/d: short, int, float, double v: vektor, pl. 2fv float[2]-t adunk át
MyViewer.cpp... void MyViewer::draw() { glpolygonmode(gl_front_and_back, GL_FILL); glbegin(gl_polygon); glvertex3f(-0.5, -0.5, 0.0); glvertex3f( 0.5, -0.5, 0.0); glvertex3f( 0.5, 0.5, 0.0); glvertex3f(-0.5, 0.5, 0.0); glend(); } void MyViewer::keyPressEvent(QKeyEvent *e) { if(e->modifiers() == Qt::NoModifier) switch(e->key()) { case Qt::Key_P: emit startcomputation(tr("testing progress bar...")); for(size_t i = 1; i <= 10; ++i) { usleep(300000); emit midcomputation(i * 10); } emit endcomputation(); break; default: QGLViewer::keyPressEvent(e); } else QGLViewer::keyPressEvent(e); }
Az OpenGL ablak beillesztése MyWindow.h: class MyWindow : public QMainWindow {... private:... MyViewer *viewer; }; MyWindow.cpp: MyWindow::MyWindow(QApplication *parent) : QMainWindow(), parent(parent) {... viewer = new MyViewer(this); connect(viewer, SIGNAL(startComputation(QString)), this, SLOT(startComputation(QString))); connect(viewer, SIGNAL(midComputation(int)), this, SLOT(midComputation(int))); connect(viewer, SIGNAL(endComputation()), this, SLOT(endComputation())); setcentralwidget(viewer);... }