wxWidgets and Direct3D9 integration

Do you like to promote your wxWidgets based application or component!? Post it here and let's see what the critics have to say. Also, if you found that ONE wx component the world needs to know about, put it here for future reference.
Post Reply
Contrust
In need of some credit
In need of some credit
Posts: 2
Joined: Fri May 14, 2010 12:24 pm

wxWidgets and Direct3D9 integration

Post by Contrust »

Hi guys, I am quite experienced programmer that is new to wxWidgets and want to share some code with you.

I'm working on a university project: 3d editor which allows edition of heightmaps. One of the tough decisions I had to face was choosing GUI tookit. After some considerations I decided to try wxWidgets :] and immediately started looking on the internet for ways of embedding Direct3D 9 context inside wxWidgets application, but to my disbelief I've found no ready-to-go samples!

Geez, one would think that such a popular and mature library like wxWidgets would have tons of samples on mixing it with currently most popular graphics API, unfortunately it's not true and I had to combine various pieces of code together to make it all work. It was painfull but eventually I made it: simple windowed application that can be resized, which embeds D3D context on which color triangle is rendered; there's also a button which changes color of the background behind triangle. In order to simplify the life of other programmers, I release it as public domain and you can see it here:

Code: Select all

/*
        Author:   Koshmaar (www.koshmaar.pl)       
        License:  public domain
       
        Description:    shows how to integrate Direct3D9 with wxWidgets in one application - I was looking for a sample
        all over the internet but I've found none, so I decided to make my own and share it with the world.
                                       
        It's based on http://forums.wxwidgets.org/viewtopic.php?t=20948 and http://wiki.wxwidgets.org/Making_a_render_loop

*/


#include "wx/sizer.h"
#include "wx/wx.h"

#define D3D_DEBUG_INFO

#include <d3d9.h>
#include <d3dx9.h>

IDirect3DDevice9 * Device;

#pragma comment (lib, "dxguid.lib")


// *************************** DirectX ************************

struct MY_TRANSFORMED_VERTEX
{
        FLOAT x, y, z, rhw;
        DWORD color;
};

#define D3DFVF_MY_TRANSFORMED_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

MY_TRANSFORMED_VERTEX triangle2d[] =
{
        { 0.0f, 0.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
        { 570.0f, 395.0f, 0.5f, 1.0f, 0xff00ff00, },
        { 100.0f,295.0f, 0.5f, 1.0f, 0xff00ffff, },
};

//--------------------------------------------------------------------

class MyFrame;

class DirectXPanel : public wxPanel
{   
 public:

         DirectXPanel(wxFrame* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(640, 480) /*DX context size*/, wxWANTS_CHARS /* pressing arrows key events will be propagated, see: http://wxforum.shadonet.com/viewtopic.php?t=21669 */ ) 
         {
                 // initialize direct3d
                 IDirect3D9 *D3d = Direct3DCreate9(D3D_SDK_VERSION);   

                 D3DPRESENT_PARAMETERS present_params;
                 ZeroMemory( &present_params, sizeof(present_params) );
                 present_params.Windowed = true;
                 present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
                 present_params.BackBufferFormat = D3DFMT_UNKNOWN;
                 present_params.BackBufferWidth = GetSize().x;
                 present_params.BackBufferHeight = GetSize().y;
                 //present_params.hDeviceWindow = (HWND)GetHandle(); // not really needed

                 //set the params, best to make the backbuffer the size of the users screen resolution, so account for resizeing
                 if ( FAILED( D3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, (HWND)GetHandle()/*get handle from wxWidgets, vitally important*/, D3DCREATE_HARDWARE_VERTEXPROCESSING, &present_params, &Device) ))
                 {
                         wxMessageBox( "Failed to CreateDevice for Direct3D", "Error");
                         throw "aaaaaaaaaaaaaa!!!"; // I agree that this error handling scheme is not very professional
                 }     

         }


        void paintEvent(wxPaintEvent& evt)
        {
                wxPaintDC dc(this);
                render(dc);     
        }
       
        void paintNow()
        {
                wxClientDC dc(this);
                render(dc);     
        }
       
        void render( wxDC& dc ); // deffered to make the definition of MyFrame available in its body

        MyFrame * frame; // the same as parent in constructor, used to call its render method

        DECLARE_EVENT_TABLE()
};


BEGIN_EVENT_TABLE(DirectXPanel, wxPanel)
        EVT_PAINT(DirectXPanel::paintEvent)
END_EVENT_TABLE()
 

// ************************************************************
 
enum
{
        wxMyButton = 10
};
 
class MyFrame : public wxFrame
{
        IDirect3DVertexBuffer9 * vertex_buffer;
        int color;

 public:
 
    MyFrame() : wxFrame((wxFrame *)NULL, -1,  wxT("DX + WX integration"), wxDefaultPosition, wxSize(800, 600)), color(0) { }
   
    void onClose(wxCloseEvent& evt);
       
        void MyButton( wxCommandEvent & evt)
        {
                color += 10;   
        }
       
        void CreateResources()
        {       
                if (FAILED( Device->CreateVertexBuffer( 3*sizeof(MY_TRANSFORMED_VERTEX), 0,     D3DFVF_MY_TRANSFORMED_VERTEX, D3DPOOL_MANAGED, &vertex_buffer, 0) ) )
                {
                        wxMessageBox( "Failed to create vertex buffer", "Error");
                        return;
                }       

                void * buffer;
                if (FAILED( vertex_buffer->Lock(0, sizeof(triangle2d), (void**)&buffer, 0) ) )
                {
                        wxMessageBox( "Failed to lock vertex buffer", "Error");
                        return;
                }

                memcpy( buffer, triangle2d, sizeof(triangle2d) );

                vertex_buffer->Unlock();               
               
                SetBackgroundColour(wxColour(230, 230, 230));
                ClearBackground();
        }

        void Render() // renders on DirectXPanel context, but logic moved here to make DirectXPanel just an utility class
        {
                       
                Device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(color, 0, 255), 1, 0);
                Device->BeginScene();

                Device->SetStreamSource( 0, vertex_buffer, 0, sizeof(MY_TRANSFORMED_VERTEX) );
                Device->SetFVF( D3DFVF_MY_TRANSFORMED_VERTEX );
                Device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1);

                Device->EndScene();
                Device->Present(0, 0, 0, 0); 
       
        }
                               
   
    DECLARE_EVENT_TABLE()
};

 
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
        EVT_CLOSE(MyFrame::onClose)
        EVT_BUTTON(wxMyButton, MyButton)
END_EVENT_TABLE()


void DirectXPanel::render( wxDC& dc )
{     
        if (frame)
                frame->Render();
}
 
// ************************************************************

class MyApp: public wxApp
{
    bool render_loop_on;
    bool OnInit();
    void onIdle(wxIdleEvent& evt);
   
    MyFrame* frame;
    DirectXPanel* drawPane;
public:
    void activateRenderLoop(bool on);
           
};
 
IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    render_loop_on = false;
   
    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    frame = new MyFrame();     
    drawPane = new DirectXPanel( frame );
   
    drawPane->frame = frame;
   
    frame->CreateResources();
   
    sizer->Add(drawPane, 0, wxSHAPED | wxALL | wxALIGN_CENTER, 15); // wxLEFT | wxRIGHT | wxCENTER | wxEXPAND | wxSHAPED
    sizer->Add( new wxButton(frame, wxMyButton, "Button"), 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 10 );
       
    frame->SetSizer(sizer);
    frame->Show();
   
    activateRenderLoop(true);
    return true;
}
 
void MyApp::activateRenderLoop(bool on)
{
    if(on && !render_loop_on)
    {
        Connect( wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle) );
        render_loop_on = true;
    }
    else if(!on && render_loop_on)
    {
        Disconnect( wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle) );
        render_loop_on = false;
    }
}
void MyApp::onIdle(wxIdleEvent& evt)
{
    if(render_loop_on)
    {
        drawPane->paintNow();
        evt.RequestMore(); // render continuously, not only once on idle
    }
}
 
void MyFrame::onClose( wxCloseEvent& evt ) // MyFrame, needs to know MyApp definition
{
        wxGetApp().activateRenderLoop(false);
        evt.Skip(); // don't stop event, we still want window to close
}
HTH!
If debugging is the process of removing bugs, then programming is the process of putting them in...
Scorcher24
Earned some good credits
Earned some good credits
Posts: 128
Joined: Sat Sep 25, 2004 9:11 pm
Location: Nuremberg, Germany
Contact:

Post by Scorcher24 »

Awesome! Thanks for contributing.
regards
OS: Windows 7 Ultimate 64bit
IDE: VC++ 2008 Professional
WX: 2.9.2
My Projects
Post Reply