PDA

View Full Version : Direct3D widget



ixSci
31st March 2009, 11:11
Hello!

I'm trying to create a widget which encapsulate the Direct3D engine(something like OpenGL widget). I have the following code:
.h

class Direct3DWidget : public QWidget
{
Q_OBJECT
public:
Direct3DWidget(QWidget *parent = 0);
~Direct3DWidget();
bool InitDirect3D();
public slots:
bool Rendering();
private:
void paintEvent(QPaintEvent *p)
{
Rendering();
}
QPaintEngine* paintEngine()
{
return NULL;
}
LPDIRECT3D9 m_pD3D;
LPDIRECT3DDEVICE9 m_pd3dDevice;
};
.cpp

Direct3DWidget::Direct3DWidget(QWidget *parent /* = 0 */)
{
setFixedSize(200, 200);
setAutoFillBackground(false);
setAttribute(Qt::WA_PaintOnScreen, true);
}
Direct3DWidget::~Direct3DWidget()
{
if( m_pd3dDevice != NULL)
m_pd3dDevice->Release();

if( m_pD3D != NULL)
m_pD3D->Release();
}
bool Direct3DWidget::InitDirect3D()
{
m_pD3D = Direct3DCreate9( D3D_SDK_VERSION);
if( !m_pD3D)
return false;

D3DPRESENT_PARAMETERS d3dpp = {0};
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT hr = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winId(),
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp, &m_pd3dDevice );
if( FAILED(hr) || !m_pd3dDevice)
return false;
}
bool Direct3DWidget::Rendering()
{
if(m_pd3dDevice == NULL)
return false;
m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 0), 1.0f, 0);
if(SUCCEEDED(m_pd3dDevice->BeginScene()))
{
m_pd3dDevice->EndScene();
}
m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
return true;
}
main.cpp

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QMainWindow window;
Direct3DWidget* widget = new Direct3DWidget(&window);

window.show();
widget->InitDirect3D();
return a.exec();
}

But this code does nothing :(. What should I change to gain the appropriate behavior?
I'll be glad for any advices regarding the Direct3D.

minimoog
31st March 2009, 21:54
Try with:


setAttribute(Qt::WA_NoSystemBackground, true);

ComaWhite
1st April 2009, 04:04
I would say put the init function call inside of the constructor. Use the pimpl idiom. Replace all the NULL with 0. Since NULL is just a 0 in C++. And replace the LP calls with CComPtr<>. And it will manage all of your pointers by calling release itself. And what is the exact problem?

ixSci
1st April 2009, 08:24
I made some slight changes. The last version of the code is:
.h


class Direct3DWidget : public QWidget
{
Q_OBJECT
public:
Direct3DWidget(QWidget *parent = 0);
~Direct3DWidget();
public slots:
bool Rendering();
private:

void InitDirect3D();
void paintEvent(QPaintEvent *p)
{
Rendering();
}
QPaintEngine* paintEngine()
{
return 0;
}
private:
CComPtr<IDirect3D9> m_pD3D;
CComPtr<IDirect3DDevice9> m_pd3dDevice;
};
.cpp

Direct3DWidget::Direct3DWidget(QWidget *parent /* = 0 */)
{
setFixedSize(200, 200);
setAutoFillBackground(false);
setAttribute(Qt::WA_PaintOnScreen, true);
setAttribute(Qt::WA_MSWindowsUseDirect3D, true);
setAttribute(Qt::WA_NoSystemBackground, true);
InitDirect3D();
}
Direct3DWidget::~Direct3DWidget()
{
}
void Direct3DWidget::InitDirect3D()
{
m_pD3D = Direct3DCreate9( D3D_SDK_VERSION);

D3DPRESENT_PARAMETERS d3dpp = {0};
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winId(),
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp, &m_pd3dDevice );
}
bool Direct3DWidget::Rendering()
{
if(m_pd3dDevice == 0)
return false;
m_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 0), 1.0f, 0);
if(SUCCEEDED(m_pd3dDevice->BeginScene()))
{
m_pd3dDevice->EndScene();
}
m_pd3dDevice->Present( 0, 0, 0, 0 );
return true;
}
main

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow window;
Direct3DWidget* widget = new Direct3DWidget(&window);
widget->setMinimumSize(20, 20);
window.show();
return a.exec();
}
minimoog, It didn't help :(

And what is the exact problem?
When I execute the code from above I see the only standard QT background instead of the yellow background which should be drew by the Direct3D engine

spirit
1st April 2009, 08:38
maybe this link (http://www.gamedev.net/community/forums/topic.asp?topic_id=477580) wiil help you.
also take a look at this archive. it was downloaded from http://www.gamedev.ru/download/?id=8148. unfortunately this is link on Russian site. :)

spirit
1st April 2009, 08:46
maybe this Qt-interest Archive (http://lists.trolltech.com/qt-interest/2003-03/thread01272-0.html) will help you too. (pay attention to last feedback).

ixSci
1st April 2009, 09:11
spirit, thank you for the links, but I saw it before. Some of them conflicted to each other.
For instance there: http://lists.trolltech.com/qt-interest/2003-03/thread01272-0.html
advise to initialize Direct3d in a constructor, but there(Russian) http://www.gamedev.ru/code/forum/?id=90538 say that initialization should be perfomed after the widget will be set as central widget in a Main Window.
If I clear the Direct3DWidget constructor:

Direct3DWidget::Direct3DWidget(QWidget *parent /* = 0 */)
{
}
and rewrite the main as following:

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow window;
Direct3DWidget* widget = new Direct3DWidget(&window);
window.setCentralWidget(widget);
widget->InitDirect3D();
window.show();
QTimer *timer = new QTimer();
QObject::connect(timer, SIGNAL( timeout() ), widget, SLOT( Rendering() ) );
timer->start();
return a.exec();
}
I'll gain the yellow screen! But, if I remove the timer I'll get the standard background again :(
Based on the text above I have two questions:
1. Why the code of the Rendering() does nothing inside the paintEvent?
2. Is it obligatory that Direct3DWidget should be the Central widget? I prefer to combine it with other widgets rather then have one direct3dwidget :(

Thank you for your attention

ComaWhite
1st April 2009, 10:22
Because I think that if you resize the form and what not, then it will cause a repainting to be called. When you hook it up to a timer, its being called even if the painting event isn't.

ixSci
1st April 2009, 10:28
paintEvent is called, I see it in the debugger.

ixSci
1st April 2009, 11:07
I replaced the QMainWindow to Qwidget also I placed the widget into a layout. As result I have a blink widget inside the window while resizing and when I stop resizing I have the blank window

ComaWhite
1st April 2009, 15:12
You need to handle LostDevice in DirectX on resizing

ixSci
1st April 2009, 16:10
The main problem is an absence of painting in the paintEvent. Actually after paintEvent is called something fills the background and as a result I have the blank screen. This is a problem. Can anybody tell me why it happens?
I've mentioned resizing as an example of a case when paintEvent is emitted

pdolbey
1st April 2009, 18:13
Have you tried to see if setting the Qt::WA_NativeWindow atrribute changes the way it uses the backing store - had this problem with OpenCASCADE viewers when 4.4 came out

Pete

ixSci
1st April 2009, 18:25
pdolbey, it doesn't help :(

minimoog
1st April 2009, 21:01
ComaWhite is right. You need to recreate buffers when doing resizing.

ixSci
1st April 2009, 21:37
Ok, forget about resize. I have the paintEvent which is called but a result surface hasn't any changes, i.e the screen is as blank as it was before. It isn't problem in the direct3d engine this problem relate to the QT. I need to know why my drawing inside the paintEvent don't remain on screen but are cleaned from it.

ixSci
2nd April 2009, 19:47
Problem is solved. I rebuilt the Qt with -direct3d key, and removed
QPaintEngine* paintEngine()
{
return 0;
}
from the widget's code. Also I needed to use -direct3d key when I've executed the application.
Thank all for prompts.

Freakish
15th October 2009, 07:15
Hi, I am having some similar issues on handling a Direct3D 10 application. The DX10SDK has a simple example (tutorial4) that i have compiled and is working fine.

I have adapted the code to Qt4.53 and it works fine... Except that it won't animate! The Cube appears just as the SDK example, however i can only make the Cube spin if i resize the QWidget window continuously. So as long as i resize my window the cube spins, but not otherwise. The Windows example obviously spins without user intervention.

Any ideas on what could be happening and or how to fix it?

I guess that Qt is only drawing once and not repainting unless i resize, Any ideas on how to fix, I'm a C++ and Qt and DirectX complete Noob! I'm not using any Qtimer like the above example.

I'm using Qt 4.53 on Windows 7 x64, with Latest DirectX SDK and MSVC-2008.

ixSci
15th October 2009, 17:15
Here is a full working example:
Direct3DWidget.h

#ifndef DIRECT3DWIDGET_H
#define DIRECT3DWIDGET_H

#include <QtGui/QWidget>
#include <d3d9.h>
#include <atlbase.h>

class Direct3DWidget : public QWidget
{
Q_OBJECT

CComPtr<IDirect3D9> m_pD3D;
CComPtr<IDirect3DDevice9> m_pd3dDevice;
public:
Direct3DWidget(QWidget *parent = );
~Direct3DWidget();
bool InitDirect3D();
public slots:
bool Rendering();
private:
void paintEvent(QPaintEvent *pEvent);
};

#endif

Direct3DWidget.cpp

#include "Direct3DWidget.h"

Direct3DWidget::Direct3DWidget(QWidget *parent /* = 0 */): QWidget(parent)
{
setAttribute(Qt::WA_PaintOnScreen);
}
Direct3DWidget::~Direct3DWidget()
{
}
bool Direct3DWidget::InitDirect3D()
{
m_pD3D = Direct3DCreate9( D3D_SDK_VERSION);
if(!m_pD3D)
return false;
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT hrResult = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winId(),
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp, &m_pd3dDevice );
if(FAILED(hrResult))
return false;
return true;
}
bool Direct3DWidget::Rendering()
{
if(m_pd3dDevice == )
return false;
m_pd3dDevice->Clear(, , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, ), 1.0f, );
if(SUCCEEDED(m_pd3dDevice->BeginScene()))
{
m_pd3dDevice->EndScene();
}
m_pd3dDevice->Present( , , , );
return true;
}

void Direct3DWidget::paintEvent(QPaintEvent *pEvent)
{
Q_UNUSED(pEvent);
Rendering();
}

Example:

#include <QtGui/QWidget>
#include <QtGui/QApplication>
#include <QtGui/QHBoxLayout>
#include "Direct3DWidget.h"

int main(int argc, char *argv[])
{
//------------------------------Start initialization
//Additional parameters, just add a parameter separated by comma
std::string aAdditionalParameters[] = {"-direct3d"};
int iRealArgumentAmount = argc + sizeof(aAdditionalParameters)/sizeof(std::string);
//This double pointer will contain parameters from argv and
//statical parameters which necessary for this application
char** pArguments = new char*[iRealArgumentAmount];
//Copy all source(from the command line) parameters
for(int i = ; i < argc; ++i)
{
*(pArguments + i) = new char[ strlen(argv[i]) + 1 ];
strcpy( *(pArguments + i), argv[i] );
}
//Append parameters we want to add
for(int i = argc, j = ; i < iRealArgumentAmount; ++i, ++j)
{
*(pArguments + i) = new char[ aAdditionalParameters[j].size() + 1 ];
strcpy( *(pArguments + i), aAdditionalParameters[j].c_str() );
}
QApplication xApplication(iRealArgumentAmount, pArguments);
for(int i = ; i < iRealArgumentAmount; ++i)
delete []*(pArguments + i);
delete []pArguments;
//--------------------------------Initialization complete
QWidget xMainWindow;
Direct3DWidget* xScene = new Direct3DWidget(&xMainWindow);
QHBoxLayout* xMainLayout = new QHBoxLayout;
xMainLayout->addWidget(xScene);
xMainWindow.setLayout(xMainLayout);
//It is important to initialize the Direct3d after the widget was embedded to the window
xScene->InitDirect3D();
xMainWindow.show();

return xApplication.exec();
}
But there is a problem.. I don't know why but on some computers this draw nothing. Perhaps the problem lies in the experimental support of the directx in Qt. The workaround I've found is to use timer for redrawing scene. But I didn't test new Qt releases maybe they fixed this bug? :)

Freakish
15th October 2009, 22:40
Thankyou for your post, It was only after searching via this thread your post help me get a working compiling D3D10 going.

But when i got to an Animated test Qt fails.


// Update our time
static float t = 0.0f;
t += ( float )D3DX_PI * 0.0125f;

I'm really trying to work out why the Windows SDK version would spin, but Qt needs to be forcibly repainted or updated.

Google also gives another link with similar issues...
http://www.gamedev.net/community/forums/topic.asp?topic_id=477580&forum_id=10&gforum_id=0

I did use update(); which does fix the issue, but it updates continuously (way too fast). But at least this proves that it's not updating correctly.

I will try and look into a Qtimer, Thankyou for your suggestion.

Unfortunately, I'm using Qt4.53.....To my disappointment, Nokia/Trolltech have REMOVED Direct3D as an option in Qt 4.6 and beyond. So the experimental support has now been removed and simply won't work in any later versions.. (I don't know why i bother sometimes) $@#^$@#^*@#^* Nokia!