Hi everyone,
first off - congratulations on this engine. I was kinda skepictal when I decided to switch from Ogre3d a few days ago and was surprised how easy and accessible Horde3D is. I really want to use it which makes this problem a very frustrating one.
I have an existing Qt Application with lots of code and other dependencies that make it impossible for me to switch to any other framework. Following the Qt "OpenGL Window Example" (
http://doc.qt.io/qt-5/qtgui-openglwindow-example.html), integrating the Knight-example from the Horde3D source was straightforward and painless. But then an I realized, that the offical example binary (using glfw) looked a lot better than what was displayed in the QWindow
Most visibly, the particles emitted from the tip of the sword are missing. So is the glow of the armor and the soft specular on the ground.
Here is a comparison:
Original Display (source example using glfw)QWindow DisplayThere is an existing thread on Stackoverflow reporting the same problem 3 years ago, but it is still unresolved:
http://stackoverflow.com/questions/8594805/horde3d-particle-system-not-rendering-in-qt-openglI have attached the complete source at the bottom - just like in the SO post above, it is the exact same code used in the Knight example but with a bit of Qt framework around. My machine is running Ubuntu 14.10, I am using the current NVIDIA driver version 311.11, Qt 5.4 and the latest Horde3D, freshly built from the Github repo.
Here are some things I tried out already, but didn't work:
- Made sure that the particles exist. If you turn on the debug view, their bounding box is clearly visible and updated.
- Define the default QSurfaceFormat to use OpenGL version 4.3 (compatibility profile). The correct context is created, but
the change has no visible effect. - Copied all values I found in the glfw initializing code to the QSurfaceFormat with no effect.
- Set the Qt::AA_UseDesktopOpenGL attribute in order to force the application NOT to use OpenGL ES - no effect (as expected with the correct OpenGL context in place).
- Tried using the QOpenGLWidget instead of QWindow as the base class for integrating Horde3D - no effect.
- Replaced the new-style QOpenGLWidget with an old-style QGLWidget - no effect.
- Downloaded the official Horde Editor (since the newest version also uses Qt) and looked at its code, but found no significant functionality that I was missing.
- Rebuild Qt from source specifying "-opengl desktop", which I think does nothing under Linux but just in case
- Tried to use GLEW for OpenGL function resolving, not sure that it actually worked as it (again) did not change anything...
So right now, I am ready to give up
Does anybody here have a working Qt-integration of Horde3D that looks as it should? Any help would be greatly appreciated.
Cheers and thank you,
- Clemens
For reference, here is the complete code of the Qt Knight example application:
KnightExample.proCode:
QT += core gui widgets
CONFIG += c++11
TARGET = KnightExample
TEMPLATE = app
SOURCES += main.cpp \
hordewindow.cpp
HEADERS += \
hordewindow.h
INCLUDEPATH += >>>HARDCODED PATH TO:/Horde3D/Horde3D/Bindings/C++/
LIBS += -L>>>HARDCODED PATH TO:/Horde3D/libs -lHorde3D -lHorde3DUtils # I created the libs folder for convenience
LIBS += -L/usr/lib/nvidia-331-updates/ # fixes a linker bug in Ubuntu 14.10
main.cppCode:
#include "horde3dwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
// doesn't change anything...
QApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
// changes the context version, but nothing visually...
QSurfaceFormat format;
format.setVersion(4,3);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
QSurfaceFormat::setDefaultFormat(format);
QApplication a(argc, argv);
Horde3DWindow w;
w.setAnimating(true);
w.show();
w.resize(1024, 576); // same as the example
return a.exec();
}
horde3dwindow.hCode:
#ifndef HORDE3DWINDOW_H
#define HORDE3DWINDOW_H
#include <sstream>
#include <string>
#include <QWindow>
#include "Horde3D.h"
class QOpenGLContext;
class Horde3DWindow : public QWindow
{
Q_OBJECT
public:
explicit Horde3DWindow(QWindow *parent = nullptr);
~Horde3DWindow();
void initialize();
void render();
void setAnimating(bool animating);
public slots:
void renderNow();
void renderLater();
protected:
bool event(QEvent *event) override;
void exposeEvent(QExposeEvent *event) override;
private:
QOpenGLContext *m_context;
bool m_isUpdateRequested;
bool m_isAnimated;
private: // knight example members
float _x, _y, _z, _rx, _ry; // Viewer position and orientation
float _velocity; // Velocity for movement
float _curFPS;
std::stringstream _text;
int _statMode;
int _freezeMode;
bool _debugViewMode, _wireframeMode;
float _animTime, _weight;
// Engine objects
H3DRes _fontMatRes, _panelMatRes;
H3DRes _pipeRes, _logoMatRes, _hdrPipeRes, _forwardPipeRes;
H3DNode _cam, _knight, _particleSys;
std::string _contentDir;
};
#endif // HORDE3DWINDOW_H
horde3dwindow.cppCode:
#include "horde3dwindow.h"
#include <math.h>
#include <iomanip>
#include <QCoreApplication>
#include <QDebug>
#include <QOpenGLContext>
#include <QSurfaceFormat>
#include <Horde3DUtils.h>
Horde3DWindow::Horde3DWindow(QWindow *parent)
: QWindow(parent)
, m_context(nullptr)
, m_isUpdateRequested(false)
, m_isAnimated(false)
{
// indicate that the window is to be used for OpenGL rendering
setSurfaceType(QWindow::OpenGLSurface);
// knight example initializations
_x = 5; _y = 3; _z = 19; _rx = 7; _ry = 15; _velocity = 10.0f;
_curFPS = 30;
_statMode = 1;
_freezeMode = 0; _debugViewMode = false; _wireframeMode = false;
_animTime = 0; _weight = 1.0f;
_cam = 0;
_contentDir = >>>HARDCODED PATH TO:"/Horde3D/Horde3D/Binaries/Content";
}
Horde3DWindow::~Horde3DWindow()
{
// Release engine
h3dutDumpMessages();
h3dRelease();
}
// This is an exact copy of the original example's source
void Horde3DWindow::initialize()
{
h3dInit();
// Set options
h3dSetOption( H3DOptions::LoadTextures, 1 );
h3dSetOption( H3DOptions::TexCompression, 0 );
h3dSetOption( H3DOptions::FastAnimation, 0 );
h3dSetOption( H3DOptions::MaxAnisotropy, 4 );
h3dSetOption( H3DOptions::ShadowMapSize, 2048 );
// Add resources
// Pipelines
_hdrPipeRes = h3dAddResource( H3DResTypes::Pipeline, "pipelines/hdr.pipeline.xml", 0 );
_forwardPipeRes = h3dAddResource( H3DResTypes::Pipeline, "pipelines/forward.pipeline.xml", 0 );
// Overlays
_fontMatRes = h3dAddResource( H3DResTypes::Material, "overlays/font.material.xml", 0 );
_panelMatRes = h3dAddResource( H3DResTypes::Material, "overlays/panel.material.xml", 0 );
_logoMatRes = h3dAddResource( H3DResTypes::Material, "overlays/logo.material.xml", 0 );
// Environment
H3DRes envRes = h3dAddResource( H3DResTypes::SceneGraph, "models/sphere/sphere.scene.xml", 0 );
// Knight
H3DRes knightRes = h3dAddResource( H3DResTypes::SceneGraph, "models/knight/knight.scene.xml", 0 );
H3DRes knightAnim1Res = h3dAddResource( H3DResTypes::Animation, "animations/knight_order.anim", 0 );
H3DRes knightAnim2Res = h3dAddResource( H3DResTypes::Animation, "animations/knight_attack.anim", 0 );
// Particle system
H3DRes particleSysRes = h3dAddResource( H3DResTypes::SceneGraph, "particles/particleSys1/particleSys1.scene.xml", 0 );
// Load resources
h3dutLoadResourcesFromDisk( _contentDir.c_str() );
// Add scene nodes
// Add camera
_cam = h3dAddCameraNode( H3DRootNode, "Camera", _hdrPipeRes );
//h3dSetNodeParamI( _cam, H3DCamera::OccCullingI, 1 );
// Add environment
H3DNode env = h3dAddNodes( H3DRootNode, envRes );
h3dSetNodeTransform( env, 0, -20, 0, 0, 0, 0, 20, 20, 20 );
// Add knight
_knight = h3dAddNodes( H3DRootNode, knightRes );
h3dSetNodeTransform( _knight, 0, 0, 0, 0, 180, 0, 0.1f, 0.1f, 0.1f );
h3dSetupModelAnimStage( _knight, 0, knightAnim1Res, 0, "", false );
h3dSetupModelAnimStage( _knight, 1, knightAnim2Res, 0, "", false );
// Attach particle system to hand joint
h3dFindNodes( _knight, "Bip01_R_Hand", H3DNodeTypes::Joint );
H3DNode hand = h3dGetNodeFindResult( 0 );
_particleSys = h3dAddNodes( hand, particleSysRes );
h3dSetNodeTransform( _particleSys, 0, 40, 0, 90, 0, 0, 1, 1, 1 );
// Add light source
H3DNode light = h3dAddLightNode( H3DRootNode, "Light1", 0, "LIGHTING", "SHADOWMAP" );
h3dSetNodeTransform( light, 0, 15, 10, -60, 0, 0, 1, 1, 1 );
h3dSetNodeParamF( light, H3DLight::RadiusF, 0, 30 );
h3dSetNodeParamF( light, H3DLight::FovF, 0, 90 );
h3dSetNodeParamI( light, H3DLight::ShadowMapCountI, 1 );
h3dSetNodeParamF( light, H3DLight::ShadowMapBiasF, 0, 0.01f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 0, 1.0f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 1, 0.8f );
h3dSetNodeParamF( light, H3DLight::ColorF3, 2, 0.7f );
h3dSetNodeParamF( light, H3DLight::ColorMultiplierF, 0, 1.0f );
// Customize post processing effects
H3DNode matRes = h3dFindResource( H3DResTypes::Material, "pipelines/postHDR.material.xml" );
h3dSetMaterialUniform( matRes, "hdrExposure", 2.5f, 0, 0, 0 );
h3dSetMaterialUniform( matRes, "hdrBrightThres", 0.5f, 0, 0, 0 );
h3dSetMaterialUniform( matRes, "hdrBrightOffset", 0.08f, 0, 0, 0 );
}
// This is an (almost) exact copy of the original example's source
void Horde3DWindow::render()
{
_curFPS = 30; // just hardcode the fps for now
h3dSetOption( H3DOptions::DebugViewMode, _debugViewMode ? 1.0f : 0.0f );
h3dSetOption( H3DOptions::WireframeMode, _wireframeMode ? 1.0f : 0.0f );
if( !_freezeMode )
{
_animTime += 1.0f / _curFPS;
// Do animation blending
h3dSetModelAnimParams( _knight, 0, _animTime * 24.0f, _weight );
h3dSetModelAnimParams( _knight, 1, _animTime * 24.0f, 1.0f - _weight );
h3dUpdateModel( _knight, H3DModelUpdateFlags::Animation | H3DModelUpdateFlags::Geometry );
// Animate particle systems (several emitters in a group node)
unsigned int cnt = h3dFindNodes( _particleSys, "", H3DNodeTypes::Emitter );
for( unsigned int i = 0; i < cnt; ++i )
h3dUpdateEmitter( h3dGetNodeFindResult( i ), 1.0f / _curFPS );
}
// Set camera parameters
h3dSetNodeTransform( _cam, _x, _y, _z, _rx ,_ry, 0, 1, 1, 1 );
// Show stats
h3dutShowFrameStats( _fontMatRes, _panelMatRes, _statMode );
if( _statMode > 0 )
{
// Display weight
_text.str( "" );
_text << std::fixed << std::setprecision( 2 ) << "Weight: " << _weight;
h3dutShowText( _text.str().c_str(), 0.03f, 0.24f, 0.026f, 1, 1, 1, _fontMatRes );
}
// Show logo
const float ww = (float)h3dGetNodeParamI( _cam, H3DCamera::ViewportWidthI ) /
(float)h3dGetNodeParamI( _cam, H3DCamera::ViewportHeightI );
const float ovLogo[] = { ww-0.4f, 0.8f, 0, 1, ww-0.4f, 1, 0, 0, ww, 1, 1, 0, ww, 0.8f, 1, 1 };
h3dShowOverlays( ovLogo, 4, 1.f, 1.f, 1.f, 1.f, _logoMatRes, 0 );
// Render scene
h3dRender( _cam );
// Finish rendering of frame
h3dFinalizeFrame();
// Remove all overlays
h3dClearOverlays();
// Write all messages to log file
h3dutDumpMessages();
}
void Horde3DWindow::setAnimating(bool animating)
{
m_isAnimated = animating;
if (m_isAnimated) {
renderLater();
}
}
void Horde3DWindow::renderNow()
{
// return early if the Window is not visible on the screen
if (!isExposed()){
return;
}
// create a new context, if there is none yet...
if (!m_context) {
// create the Context with the same QSurfaceFormat as the Horde3DWindow
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
qDebug() << m_context->format(); // print out the context format .. I think this is were the problem is?
m_context->create();
m_context->makeCurrent(this);
initialize();
// ...otherwise just make it current
} else {
m_context->makeCurrent(this);
}
// render the Window and make its contents visible
render();
m_context->swapBuffers(this);
// if the window is animated, automatically trigger an update on the next vertical refresh
if (m_isAnimated) {
renderLater();
}
}
void Horde3DWindow::renderLater()
{
// only post one request at a time
if (!m_isUpdateRequested) {
m_isUpdateRequested = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
}
bool Horde3DWindow::event(QEvent *event)
{
// handle update requests by rendering...
if(event->type() == QEvent::UpdateRequest){
m_isUpdateRequested = false;
renderNow();
return true;
}
// ... and pass everything else up to QWindow
return QWindow::event(event);
}
void Horde3DWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);
if (isExposed()){
renderNow();
}
}