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 ●
●
Linux virtuális gépen ajánlott ●
● ●
Tesztelve: Linux, Windows BME Cloud: http://cloud.bme.hu/
Probléma esetén:
[email protected] 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: ●
● ● ● ●
●
●
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 ●
●
http://www.qt.io/
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: ●
●
Konstansok: ● ●
●
QSomeClass::EnumType → ValueOne, ValueTwo; Qt::EnumType → Qt::ValueOne, Qt::ValueTwo;
Metódusok: ●
●
QSomeClass (→ #include
)
QSomeClass::someMethod(...)
Tulajdonságok: ● ●
QSomeClass::someProperty() QSomeClass::setSomeProperty(...)
A Signal-Slot kommunikáció (1) ●
Események (event): ● ●
●
●
Cél: ● ●
●
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.) 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 ● ● ● ●
●
Főprogram ● ●
● ●
Egy Qt osztályból származtatva Q_OBJECT Kibővítve (új signalok, slotok) Testreszabva (virtuális metódusok újraírása) 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 class MyWindow : public QMainWindow { Q_OBJECT public: MyWindow(); ~MyWindow(); };
MyWindow.cpp #include #include #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 #include ... 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 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; };
Virtuális függvények ●
void init(): ●
●
void draw(): ●
●
OpenGL kirajzolás
void keyPressEvent(QKeyEvent *): ●
●
OpenGL beállítások inicializáláskor, pl.: glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); [mindkét oldal legyen megvilágítva]
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); ... }