Thread: Creating a simple square in DirectX

  1. #1
    Registered User
    Join Date
    Sep 2011
    Posts
    25

    Creating a simple square in DirectX

    Hey guys, I don't know why this won't work. I'm trying to draw a simple square made of four vertices. I'm using vertex buffers and DrawPrimitive, if that matters. For some reason, the following only draws a single triangle defined by the first three vertices, totally skipping the fourth it seems. Can anyone help?

    winmain.cpp (Based on a sample from Beginning DirectX 9 by Wendy Jones):

    Code:
    #include <windows.h>
    #include <mmsystem.h>
    #include <d3d9.h>
    #include <d3dx9tex.h>
    #include <string>
    using namespace std;
    
    HINSTANCE hInst;				// holds the instance for this app
    HWND wndHandle;					// global window handle
    
    LPDIRECT3DVERTEXBUFFER9 g_pVB        = NULL; // Buffer to hold vertices
    
    // A structure for our custom vertex type
    struct CUSTOMVERTEX
    {
        FLOAT x, y, z, rhw;      // The untransformed, 3D position for the vertex
        DWORD color;        // The vertex color
    };
    
    // Our custom FVF, which describes our custom vertex structure
    #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
    
    LPDIRECT3D9             pD3D;
    LPDIRECT3DDEVICE9       pd3dDevice;
    
    ////////////////////////////////////////////// forward declarations
    bool    initWindow(HINSTANCE hInstance);
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    // direct3D functions
    HRESULT SetupVB();
    bool initDirect3D(HWND hwnd);
    void shutdown(void);
    LPDIRECT3DVERTEXBUFFER9 createVertexBuffer(int size, DWORD usage);
    void drawVB(LPDIRECT3DVERTEXBUFFER9 buffer);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    {
    	// call our function to init and create our window
    	if (!initWindow(hInstance))
    	{
    		MessageBox(NULL, "Unable to create window", "ERROR", MB_OK);
    		return false;
    	}
    
    	// init direct3d
    	initDirect3D(wndHandle);
    
    	// setup the vertex buffer and add the triangle to it.
    	SetupVB();
    
    	// Main message loop:
    	// Enter the message loop
        MSG msg; 
        ZeroMemory( &msg, sizeof(msg) );
        while( msg.message!=WM_QUIT )
        {
    		// check for messages
    		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
    			TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
    		// this is called when no messages are pending
    		else
    		{
    			// call our render function
    			pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
    
    			pd3dDevice->BeginScene();
    
    			// draw the contents of the vertex buffer
    			drawVB(g_pVB);
    			
    			pd3dDevice->EndScene();
    
    			pd3dDevice->Present( NULL, NULL, NULL, NULL );	
    		}
        }
    
    	// shutdown the directx manager
    	shutdown();
    
    	return (int) msg.wParam;
    }
    
    HRESULT SetupVB()
    {
    	HRESULT hr;
    
        // Initialize three vertices for rendering a triangle
        CUSTOMVERTEX g_Vertices[] =
        {
            { 100,  100, 0.5f, 1.0f, D3DCOLOR_ARGB(0,255,0,0), }, // x, y, z, rhw, color
            { 300, 100, 0.5f, 1.0f, D3DCOLOR_ARGB(0,0,255,0), },
            {  300, 300, 0.5f, 1.0f, D3DCOLOR_ARGB(0,0,0,255), },
    		{  100, 300, 0.5f, 1.0f, D3DCOLOR_ARGB(0,0,0,255), }
        };
    
    	// Create the vertex buffer
    	g_pVB = createVertexBuffer(4*sizeof(CUSTOMVERTEX), D3DFVF_CUSTOMVERTEX);
    
        // Fill the vertex buffer.
        VOID* pVertices;
    	
    	hr = g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0 );
    	if FAILED (hr)
            return E_FAIL;
    
    	// copy the vertices into the buffer
        memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
    
    	// unlock the vertex buffer
        g_pVB->Unlock();
    
        return S_OK;
    }
    
    bool initDirect3D(HWND hwnd)
    {
    	HRESULT hr;
    
    	if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
    	{
    		return false;
    	}
    
    	D3DPRESENT_PARAMETERS d3dpp; 
        ZeroMemory( &d3dpp, sizeof(d3dpp) );
        d3dpp.Windowed = TRUE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    	d3dpp.BackBufferCount  = 1;
    	d3dpp.BackBufferHeight = 480;
    	d3dpp.BackBufferWidth  = 640;
    	d3dpp.hDeviceWindow    = hwnd;
    
        hr = pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                 D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                 &d3dpp, &pd3dDevice );
    	if FAILED (hr)
        {
            return false;
        }
    
    	return true;
    }
    
    LPDIRECT3DVERTEXBUFFER9 createVertexBuffer(int size, DWORD usage)
    {
    	HRESULT hr;
    	LPDIRECT3DVERTEXBUFFER9 buffer;
    
        // Create the vertex buffer.
        hr = pd3dDevice->CreateVertexBuffer( size,
                                             0, 
    										 usage,
                                             D3DPOOL_DEFAULT, 
    										 &buffer, 
    										 NULL );
    	if FAILED ( hr)
            return NULL;
        
    	return buffer;
    }
    
    void drawVB(LPDIRECT3DVERTEXBUFFER9 buffer)
    {
    	pd3dDevice->SetStreamSource( 0, buffer, 0, sizeof(CUSTOMVERTEX) );
        pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
    }
    ...
    I cut some of the standard Win32 stuff out to keep it short. Sorry if I cut too much/not enough. Let me know if I should just post the entire file.

  2. #2
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Can we move this to the Game Forum where it actually has a shot at getting a reply?
    -Thx
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by AndrewHunter
    Can we move this to the Game Forum where it actually has a shot at getting a reply?
    Yes, we can!
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Quote Originally Posted by laserlight View Post
    Yes, we can!
    Thanx! (Following American Politics?)
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  5. #5
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    Sorry, I thought since this was in C++ it was meant to go to the C++ forum.

    I figured out a solution today. By swapping the last two vertices, the square is drawn just fine. This doesn't make sense, though, since it's in violation of DirectX's winding order (forward faces must be represented by vertices in a clockwise order by default). Does anyone have any light to shine on this?

    Edit: I think I've got it all figured out now. The winding order rule applies to the tris being defined, not the entire polygon or quad in this specific instance. So, there were two triangles being defined here: The first one having a clockwise order and the second having a seemingly clockwise order, but when drawn by DirectX has the opposite order. Probably a noob mistake on my part, but I thought I would share.
    Last edited by blakeo_x; 09-30-2011 at 02:20 AM.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    If the winding order is clockwise then all vertices of all triangles (on a per triangle basis) must be ordered clockwise when looking at them from a top-down perspective and vice versa for counter-clockwise. Triangle strips are handled automatically by the hardware and the culling mode is auto-flipped from CCW to CW and back for each triangle in the strip so the strip is visible.

    EDIT:
    The simplest way to draw a quad is using a trianglestrip.

    Code:
    struct SimpleVertex
    {
       D3DXVECTOR3 vecPos;
       float rhw;
       D3DCOLOR color;
       float u;
       float v;
       
       static const DWORD FVF;
    
       SimpleVertex(float x,float y,float z,D3DCOLOR color,float u,float v) : vecPos(x,y,z), color(color) ,u(u), v(v), rhw(1.0f)
       {
       }
       
        SimpleVertex() : vecPos(0.0f,0.0f,0.0f),color(0),u(0.0f),v(0.0f),rhw(1.0f)
        {
        }
    };
    Code:
    DWORD SimpleVertex::FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1; 
    
    // Create two user vertex buffers
    SimpleVertex workingVerts[4];
    SimpleVertex verts[4];
    
    // Setup working vertex buffer
    D3DXCOLOR color(1.0f,1.0f,1.0f,1.0f);
    workingVerts[0] = SimpleVertex(-0.5f,-0.5f,1.0f,color,0.0f,0.0f);
    workingVerts[1] = SimpleVertex(0.5f,-0.5f,1.0f,color,1.0f,0.0f);
    workingVerts[2] = SimpleVertex(-0.5f,0.5f,1.0f,color,0.0f,1.0f);
    workingVerts[3] = SimpleVertex(0.5f,0.5f,1.0f,color,1.0f,1.0f);
    
    // Setup transformation = Scale * Rotate * Translate
    D3DXMATRIXA16 matScale;
    D3DXMATRIXA16 matTrans;
    D3DXMATRIXA16 matRot;
    
    // Setup scale, rotation, and translation matrices
    D3DXMatrixScaling(&matScale,100.0f,100.0f,1.0f);
    D3DXMatrxTranslation(&matTrans,100.0f,100.0f,1.0f);
    D3DXMatrixRotationYawPitchRoll(&matRot,0.0f,0.0f,0.0f);
    
    // Compute world matrix
    D3DXMATRIXA16 matWorld = matScale * matRot * matTrans;
    
    // Setup view and projection matrices
    D3DXMATRIXA16 matProj;
    D3DXMATRIXA16 matView;
    D3DXMatrixIdentity(&matView);
    D3DXMatrixPerspectiveFovLH(&matProj,D3DXToRadian(90),screenWidth / screenHeight,1.0f,1000.0f);
    
    // Compute WorldViewProjection matrix
    D3DXMATRIXA16 matWVP = matWorld * matView * matProj;
    
    // Transform vertices in workingVerts by WorldViewProjection matrix and store result in verts
    for (int i = 0;i < 4; ++i)
    {
        D3DXVec3TransformCoord(&verts[i].vecPos,&m_workingVerts[i].vecPos,&matWVP);
    }
    
    // Setup render states
    m_pDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
    m_pDevice->SetRenderState(D3DRS_ZENABLE,FALSE);
    m_pDevice->SetRenderState(D3DRS_COLORVERTEX,TRUE);
    
    // Draw the quad
    m_pDevice->SetTexture(0,0);
    m_pDevice->SetFVF(SimpleVertex::FVF);
    m_pDevice->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,0,1.0f,0);
    m_pDevice->BeginScene();
    m_pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,&verts,4 * sizeof(SimpleVertex));
    m_pDevice->EndScene();
    m_pDevice->Present(0,0,0,0);
    Notice the triangle winding order.
    0........1
    ...........
    ...........
    ...........
    2........3

    The triangle strip will draw the 0,1,2 tri first and then create the second triangle using vertex 3.

    Also note that I'm using D3DFVF_XYZRHW which is not optimal since it requires a user vertex buffer and software transformation since XYZRHW effectively will disable the pipeline and SetTransform() calls will do nothing to vertices with this flag enabled. You can use D3DFVF_XYZ but then you will need to use an orthogonal projection matrix to display the quads. The ortho matrix will put the object in screen coordinates just like the D3DFVF_XYZRHW flag does but it is a more elegant solution b/c it does not have to transformed in software and can be used in shaders as well. You also will not want to set your view and projection matrices every frame since this is slow and both calls cause Direct3D to do some internal housekeeping. You can set your world matrix to the world / view / projection matrix to avoid constantly setting the view and projection matrices. I'm also using D3DXMATRIXA16 which has performance benefits but only on NVidia based cards.
    Last edited by VirtualAce; 10-04-2011 at 09:50 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. A simple square root calculator
    By main() in forum C Programming
    Replies: 7
    Last Post: 05-19-2010, 05:50 AM
  2. DirectX | Simple Drawing
    By gavra in forum Game Programming
    Replies: 8
    Last Post: 05-31-2009, 05:17 PM
  3. Attempting to make a square creating function
    By Shamino in forum Game Programming
    Replies: 9
    Last Post: 05-23-2005, 10:56 AM
  4. Replies: 15
    Last Post: 01-14-2003, 11:30 PM
  5. DirectX 8: Creating the DirectX Object
    By Toraton in forum Game Programming
    Replies: 3
    Last Post: 11-28-2002, 03:45 AM