Eigene GUI für 3d Spiel
-
Hallo,
ich bin zur Zeit daran, ein 3d Spiel zu programmieren. Das mache ich in C++. Als Graphics Engine benutze ich Ogre3d. Zuerst hatte ich vor, Qt für die GUI zu benutzen, das klppt allerdings nicht so ganz, wie ich mir das vorgestellt habe. Jetzt bin ich auf die Idee gekommen, meine eigene GUI zu schreiben.
Die Input events funktionieren auch schon (sowohl Key events, Mouse events, als auch das Setzen vom Focus.)
Die Basisklasse für alle Widgets ist auch schon fertig.
Jetzt habe ich allerdings ein LineEdit geschrieben, und bin garnicht zufrieden damit. Die performance lässt nämlich sehr zu wünschen übrig. D.h., jedes mal, wenn ich etwas eintippe, hängt das ganze Programm kurz. Ich denke, das liegt am rendern, allerdigs kann ich es nicht wirklich nachvollziehen.Für das Rendern an sich benutze ich die QPainter und QSvgRenderer Klassen von Qt.
Jetzt habe ich in den Ogre docs von "Gorilla" gehört. Das ist eine sehr rudimentäre 2d GUI Renderig library für Ogre3d.
Desshalb ist meine Frage, ob hier jemand schonmal mit Gorilla gearbeitet hat, und eventuell weiss, mit welchem Aufwand es verbunden wäre, mit Gorilla eine komplette GUI auf Basis von SVGs zu rendern.
Vielleicht habe ich auch einfach einen Fehler in meinem Code, der das ganze so extrem langsam macht.
Zur Struktur von meiner GUI ist folgendes zu sagen:
Ich habe eine Basisklasse für die einzelnen Elemente (Widget, von ihr erbt jedes Element). Diese Basisklasse hat ein paar statische member und methods. Diese statischen member beinhalten informationen zu den einzelnen widgets, die hinzugefügt wurden, sowie das aktuell fokussierte Widget, den Status, ob die GUI schon initialisiert wurde, und ob gerade gerendert wird.Zu den statischen Methoden habe ich folgendes:
eine davon initialisiert die komplette GUI, aprich legt ein Ogre3d overlay an, und initialisiert dieses mit default Werten. Eine Andere funktion liefert auf dem Z-index der Widgets basierend, das Widget an einer bestimmten, per parameter übrgebenen position zurück. Ein paar dieser Funktionen sind dafür da, um die Inut events zu übermitteln, und die Letzte rendert das ganze.Soweit zur Struktur.
die static void Widget::render() funktion sieht so aus:
void Widget::render() { if (!cRendering) { if (cInitialized) { cRendering = true; Ogre::TexturePtr texture = static_cast<Ogre::TexturePtr>(Ogre::TextureManager::getSingleton().getByName("UITex")); Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); QImage *tex = new QImage((uchar *)pixelBox.data, pixelBox.getWidth(), pixelBox.getHeight(), QImage::Format_ARGB32); QPainter painter; painter.begin(tex); int lastZ = 0; int z = 1; while (lastZ < z) { bool found = false; for (int i = 0; i < cWidgets.length(); i++) { Widget *act = cWidgets.at(i); bool draw = true; if (act->hasParent() && !act->parent()->visible()) { draw = false; } if (draw) { int actZ = act->zIndex(); if (actZ == z) { Point pos = act->globalPos(); painter.drawImage(QPoint(pos.x(), pos.y()), act->paint()); found = true; } } } lastZ = z; if (found) { z++; } } painter.end(); pixelBuffer->unlock(); cRendering = false; } } }
QImage Widget::paint() ist eine virtuelle Methode, die von jedem Child Widget reimplementiert wird. Für das LineEdit sieht diese im Moment so aus:
QImage LineEdit::paint() { QImage img(QSize(size().width(), size().height()), QImage::Format_ARGB32); QPainter painter(&img); /* Noch habe ich keine SVG, die gerendert werden könnte. Desshalb ist der Teil auskommentiert, und hier hab ich ihn wegen der Übersichtlichkeit rausgenommen. */ painter.setFont(QFont("monospace", size().height() - borderH * 2 - 3)); QRectF rect = painter.boundingRect(QRect(0, 0, 0, 0), "_"); painter.setBrush(QBrush(QColor(0, 0, 0))); painter.drawText(0, size().height()/2, text()); painter.end(); return img; }
Vielleicht fällt jemandem auf, ob ich hier einen Fehler gemacht habe, wodurch das rendern sehr langsam ist. Ich weiß, dass es am rendern liegt, denn wenn ich es nicht rendere, gibt es keine solche "Hänger". Allerdings weiß ich nicht, wo das Problem liegt.
mit freundlichen Grüßen
Matogel
-
Hallo,
am besten benutzt du dafür einen Profiler.
Ich tippe mal darauf, daß das Erzeugen der temp. Images zu viel Zeit kostet.
Generell solltest du beim Echtzeit-Rendern darauf achten, so wenig Zeit wie möglich zu verbrauchen (also keine unnötigen Objekte erzeugen, Caches benutzen etc.)
Selbst die kleinen Objekte wie Font, Brush, Rect etc. würde ich nur einmalig (bzw. bei Änderung) erzeugen.
-
Ich tippe mal darauf, daß das Erzeugen der temp. Images zu viel Zeit kostet.
Genau das war es, ich habe es mittlerweile lösen können.
Und zwar war mein Problem, dass ich jedes mal, wenn sich etwas geändert hat, alles neu gerendert habe.
Jetzt mache ich es so, dass wirklich nur die Widgets neu gerendert werden, die auch verändert wurden.
Ein anderer Punkt war, dass ich einfach drauflos gerendert habe. Jetzt rendere ich die GUI nurnoch, wenn die 3d Scene neu gerendert wird.Und zwar ist es jetzt so mit den temporären Bildern:
Sie werden in einer Klassen Variable abgelegt. Dort wird dann alles hineingeschrieben, wenn sich etwas ändert, bzw. das Widget neu gerendert wird. Wenn sich nichts ändert, wird einfach nur die klassenvariable zurückgegeben, ohne es neu zu rendern.mfg
MatogelEDIT: hier noch ein screenshot, wie es im moment aussieht. Ich habe leider noch keine SVGs, um die Widgets zu stylen, desshalb sind sie einfach nur weiß mit einem alpha wert von 100. http://ompldr.org/vYTliYg/image.png