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
![Shocked :shock:](./images/smilies/icon_eek.gif)
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 Display![Image](https://photos-2.dropbox.com/t/2/AAA6dRdV6rzzo0Ykgzl56hPHm1wINSw-0mjRx3SuhJKfXg/12/26238640/jpeg/1024x768/3/1425571200/0/2/horde3d_knight_qwindow.jpg/CLC9wQwgASACIAMoAQ/3czLbuVTJjdGPTfPWrsvZwi74GDma-19jNAjl1O1R-M)
There 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();
}
}