Thursday 17 February 2011

Использование Direct3D совместно с Qt

Однажды, у меня появилась необходимость использования Direct3D совместно с Qt. После нескольких дней поисков в интернете, я нашел всего лишь какие-то обрывки информации. Полноценного описания механизма использования Direct3D не было нигде. В результате, после долгих изысканий я добился того, чего хотел:)

Под катом находится инструкция «сближения“ Direct3D и Qt, а так же код виджета, который можно использовать as is



При написании данной статьи использовались следующие средства:

  1. Qt 4.5.0
  2. VS2008 SP1
  3. DirectX SDK August 2008




Если Вы используете другую. IDE, то момент сборки будет несколько отличаться. Я привожу пример сборки, только для VS2008. Если вы уверены, что в вашей сборке присутствует поддержка direct3d engine, тогда пропустите шаг конфигурирования и сборки Qt.



Конфигурирование и сборка Qt



  • Первым шагом необходимо настроить переменные окружения для DirectX, для этого в консоли выполняем следующую команду:

    > "%DXSDK_DIR%\Utilities\Bin\dx_setenv.cmd“


  • Переходим в каталог Qt и конфигурируем его. В моем случае это выглядело так > configure -direct3d -platform win32-msvc2008

    -direct3d можно опустить, этот параметр должен быть вычислен конфигуратором.

    Если все прошло нормально, то в появившемся выводе конфигуратора будет: Direct3D support…yes



  • Делаем nmake, и если не будет проблем то дожидаемся конца сборки. Я столкнулся с проблемой, при сборке была ошибка unresolved external связанная с Direct3D. Чтобы ее исправить, идем в %QTDIR%\src\gui\ и в обоих Makefile’ах(release и debug) меняем dxguid.lib на "%DXSDK_DIR%\Lib\x86\dxguid.lib»(или x64 в зависимости от того, под какую платформу вы собираете). После этого проблема должна исчезнуть так что дожидаемся окончания сборки.


Код виджета



Direct3DWidget.h



#ifndef DIRECT3DWIDGET_H
#define DIRECT3DWIDGET_H
 
#include 
#include 
#include 
 
class Direct3DWidget : public QWidget
{
        Q_OBJECT
 
        CComPtr m_pD3D;
        CComPtr 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();
}




Пример использования:

#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();
}



Казалось бы теперь есть все, чтобы спокойно использовать direct3d с отличным фреймворком Qt, но, к сожалению, мною были обнаружены некоторые подводные камни.

После того, как я стал использовать Direct3d, на некоторых компьютерах, вместо картинки отрисованной Direct3d был экран, зарисованный цветом фона. Я перебрал все комбинации аттрибутов, но убрать данный дефект так и не смог. Так же не удалось выявить причину неправильной работы direct3d engine на некоторых машинах. Т.к документация по Qt утверждает, что «This functionality is experimental.», то можно предположить, что дело в сыроватости поддержки direct3d.



Но, данный недостаток не может быть причиной не использования Direct3D:). Пока trolltech допиливает direct3d до stable state можно воспользоваться следующим workaround’ом:

Закомментируем вызов Rendering () в paintEvent:

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

и в место использования добавим следующий код:

...
//It is important to initialize the Direct3d after the widget was embedded to the window 
        xScene->InitDirect3D();
        xMainWindow.show();
 
        QTimer xTimer;
        QWidget::connect(&xTimer, SIGNAL(timeout()), xScene, SLOT(Rendering()));
        xTimer.start(100);      
        
        return xApplication.exec();
...

я добавил этот код в код примера, приведенного мною выше.

Так же не забудьте включить заголовок QTimer

Приятного Вам использования Direct3d API в Qt, и не забывайте, что приложение, должно быть запущено с опцией -direct3d, я включил эту опцию в код моего примера использования.

via habrahabr

7 comments:

  1. Очень подробно описал!Но для чего может пригодится использование Direct3D совместно с Qt?

    ReplyDelete
  2. Для простоты разработки, например.

    ReplyDelete
  3. Эта статья может пригодится

    ReplyDelete
  4. Хорошо написал, буду изучать.

    ReplyDelete
  5. престижно, модно, молодежно! Но непонятно для чего

    ReplyDelete
  6. Годно, давно искал что-то подобное, спасибо.

    ReplyDelete
  7. Полезная инфа, как раз сейчас вникаю в QT.

    ReplyDelete