1

So I was trying to learn how to make a sprite in directX and downloaded an example file, but there's a bug in the example that I can't get over and must know how to fix, whenever the sprite attached to window is moved out of the screen and back in the part of the sprite that was moved off the screen is not re-drawn until I release the left mouse button. I read about everything in the RenderFrame() function hoping to find some setting to alter with no success.

Here is an example animation of the problem (I couldn't find any file sharing services that didn't 502 error on my video for some odd reason.)

Here is the source code:

// main.cpp : Defines the entry point for the application.
//
#include <windows.h>
#include "C:\Program Files\Microsoft DirectX SDK (August 2008)\Include\D3dx9core.h"
#include "C:\Documents and Settings\Death\My Documents\Downloads\DXSprite\DXSprite\resource.h"

//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
HWND g_hWnd = NULL;
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pD3DDevice = NULL;
ID3DXSprite * g_pD3DXSprite = NULL;
LPDIRECT3DTEXTURE9 g_pTexture = NULL;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;

//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT InitializeD3D       ( );
void RenderFrame            ( );

//-----------------------------------------------------------------------------
//    Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance,
                HINSTANCE hPrevInstance,
                LPSTR     lpCmdLine,
                int       nCmdShow )
{
WNDCLASSEX  winClass;
MSG         uMsg;
HRESULT     hr;

memset(&uMsg,0,sizeof(uMsg));

winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize        = sizeof(WNDCLASSEX);
winClass.style         = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc   = WindowProc;
winClass.hInstance     = hInstance;
winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName  = NULL;
winClass.cbClsExtra    = 0;
winClass.cbWndExtra    = 0;

if( !RegisterClassEx(&winClass) )
    return E_FAIL;

g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS",
                         "Direct3D 9 - ID3DXSprite Example",
                         WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                         0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );

if( g_hWnd == NULL )
    return E_FAIL;

ShowWindow( g_hWnd, nCmdShow );
UpdateWindow( g_hWnd );

//----------------------------------------------------------------
// Create the DirectX device
//----------------------------------------------------------------
if (FAILED( InitializeD3D()))
    return 0;


//----------------------------------------------------------------
// CREATE THE ID3DXSprite
//----------------------------------------------------------------

// Create the ID3DXSprite interface object
hr = D3DXCreateSprite(g_pD3DDevice, &g_pD3DXSprite );
if( FAILED(hr) )
    return hr;


//----------------------------------------------------------------
// LOAD THE TEXTURE FOR THE SPRITE
//----------------------------------------------------------------

// --------------------------------------------------------
// Load the texture.  I decided to use the extended
// version of the texture loading function just to show what
// it would look like.
// The texture was created with Photoshop with a transparent
// background to start with.  Then line cross hairs were added.
//
// Note - If you don't use a texture image that has a power of
// 2 size for the width or height then the image may not load
// properly.  This image is 256x256.
//
D3DXCreateTextureFromFileEx(
    g_pD3DDevice,
    "C:\\Documents and Settings\\Death\\My Documents\\Downloads\\DXSprite\\DXSprite\\sarah.jpg",                // Our texture image!
    D3DX_DEFAULT,               // width
    D3DX_DEFAULT,               // height
    D3DX_DEFAULT,               // MIP levels
    0,                          // usage
    D3DFMT_DXT1,                // texture format
    D3DPOOL_MANAGED,            // mem pool
    D3DX_DEFAULT,               // filter
    D3DX_DEFAULT,               // MIP filter
    0,                          // transparent color key
    NULL,                       // image info struct
    NULL,                       // palette
    &g_pTexture);               // the returned texture, if success

if ( FAILED(hr) )
    return hr;





// ---------
// Main Loop
// ---------
while( uMsg.message != WM_QUIT )
{
RenderFrame();
    if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
    {
        TranslateMessage( &uMsg );
        DispatchMessage( &uMsg );
    }
}

// -------------------------
// Release directx resources
// -------------------------
if (g_pD3DXSprite)
{
    g_pD3DXSprite->Release();
    g_pD3DXSprite = NULL;
}

if (g_pTexture)
{
    g_pTexture->Release();
    g_pTexture = NULL;
}

if (g_pD3DDevice)
{
    g_pD3DDevice->Release();
    g_pD3DDevice = NULL;
}




UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );

return (int)uMsg.wParam;
}

//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND   hWnd,
                         UINT   msg,
                         WPARAM wParam,
                         LPARAM lParam )
{

switch( msg )
{
    case WM_KEYDOWN:
    {
        switch( wParam )
        {
            case VK_ESCAPE:
                PostQuitMessage(0);
                break;

        }
    }
    break;

    case WM_CLOSE:
    {
        PostQuitMessage(0);
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);
    }
    break;

    default:
    {
        return DefWindowProc( hWnd, msg, wParam, lParam );
    }
    break;
}

return 0;
}

//-----------------------------------------------------------------------------
// Name: InitializeD3D()
// Desc: Create DirectX interface objects 
//       Initialize the view matrix.
//       Setup render states that will not need changing throughout
//       the life of the application.
//-----------------------------------------------------------------------------
HRESULT InitializeD3D( ) 
{
HRESULT hr;

// Create a direct 3D interface object
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

if( g_pD3D == NULL )
{
    // TO DO: Respond to failure of Direct3DCreate9
    return E_FAIL;
}

D3DDISPLAYMODE d3ddm;

if( FAILED( hr = g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
{
    // TO DO: Respond to failure of GetAdapterDisplayMode
    return hr;
}


//
if( FAILED( hr = g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
                                            d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
                                            D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
{
    if( hr == D3DERR_NOTAVAILABLE )
        // POTENTIAL PROBLEM: We need at least a 16-bit z-buffer!
        return hr;
}

//
// Do we support hardware vertex processing? If so, use it.
// If not, downgrade to software.
//

D3DCAPS9 d3dCaps;

if( FAILED( hr = g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT,
                                   D3DDEVTYPE_HAL, &d3dCaps ) ) )
{
    // TO DO: Respond to failure of GetDeviceCaps
    return hr;
}

DWORD dwBehaviorFlags = 0;

if( d3dCaps.VertexProcessingCaps != 0 )
    dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
    dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

//
// Everything checks out - create a simple, windowed device.
//

D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));

d3dpp.BackBufferFormat       = d3ddm.Format;
d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed               = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;

// Attempt to create a HAL device, end app on failure just to keep things
// simple.  In other words we are not trying to create a REF device if the
// HAL fails.
if( FAILED( hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                  dwBehaviorFlags, &d3dpp, &g_pD3DDevice ) ) )
{
    // Should respond to failure of creating the hardware device.
    return hr;
}


// If we get here everything worked!
return S_OK;
}


//-----------------------------------------------------------------------------
// Name: RenderFrame()
// Desc: Draw the image to the framebuffer.
//-----------------------------------------------------------------------------
void RenderFrame( )
{
if (!g_pD3DDevice && !g_pD3DXSprite && !g_pTexture)
    return;


// Clear the frame & depth buffer ready for drawing (Black color)
g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,  0x00000000, 1.0f, 0 );

g_pD3DDevice->BeginScene();
{
    //-------------------------
    // Render the sprite
    //

    D3DXVECTOR3 vecPos = D3DXVECTOR3(0,0,0);

    if (g_pD3DXSprite && g_pTexture)
    {
        g_pD3DXSprite->Begin( D3DXSPRITE_ALPHABLEND );
        g_pD3DXSprite->Draw(g_pTexture, NULL, NULL, &vecPos, 0xffffffff);
        g_pD3DXSprite->End();
    }


}
g_pD3DDevice->EndScene();


// Frame buffer to Front buffer
g_pD3DDevice->Present( NULL, NULL, NULL, NULL );

}
user2462027
  • 323
  • 3
  • 16

2 Answers2

1

This answer is the answer to your problem as well. You can verify that the message loop isn't running while you are dragging the window by outputting the current time in the loop with this code (#include <ctime>):

// ---------
// Main Loop
// ---------
while( uMsg.message != WM_QUIT )
{
    std::time_t time = std::time(0);
    OutputDebugStringA(ctime(&time));
    // ... 

Here is the answer:

There are a number of modal operations that happen on windows. Win32 Modal operations refer to functions that put an application into a "mode" by starting their own event processing loop until the mode finishes. Common application modes include drag and drop operations, move/size operations, anytime a dialog pops up that needs input before the application can continue.

So what is happening is: Your message loop is NOT being run. Your window recieved a WM_LBUTTONDOWN message that you passed to DefWindowProc. DefWindowProc determined that the user was trying to size or move the window interactively and entered a sizing/moving modal function. This function is in a message processing loop watching for mouse messages so that It can intercept them to provide the interactive sizing experience, and will only exit when the sizing operation completes - typically by the user releasing the held button, or by pressing escape.

You get notified of this - DefWindowProc sends a WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages as it enters and exits the modal event processing loop.

To continue to generate "idle" messages, typically create a timer (SetTimer) before calling a modal function - or when getting a message that DefWindowProc is entering a modal function - the modal loop will continue to dispatch WM_TIMER messages... and call the idle proc from the timer message handler. Destroy the timer when the modal function returns.

Original answer:

I see some DX applications that show this behavior and some that do not. Not just for sprites, but the entire rectangle dragged from off-screen is black. That implies that it's a window repaint issue.

Try intercepting WM_MOVING in your WindowProc and calling RedrawWindow

Community
  • 1
  • 1
Turch
  • 1,546
  • 2
  • 15
  • 31
  • @user2462027 Nope, that was off the top of my head and I don't have a DX dev environment where I can try stuff :-\ – Turch Jul 05 '13 at 13:54
  • @user2462027 If you haven't solved this yet, I finally got around to trying the code out at home and figured it out. See my updated answer – Turch Jul 09 '13 at 01:29
0

When you are overriding a Win32 Window with DirectX / OpenGL / GDI+, you are ignoring the GDI painting. Dragging a window off the screen sends a WM_PAINT and WM_ERASEBKGND message to the region of the window that went off the screen. The WM_ERASEBKGND overrides the ID3DXSprite, clearing the area to the window class's hbrBackground member.

The fix: Set hbrBackground in the WNDCLASS(EX) to GetStockObject(NULL_BRUSH). A null brush is an 'empty' brush, so to speak. It causes the WM_ERASEBKGND message to be ignored, preventing the sprite from being erased.

Code:

winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize        = sizeof(WNDCLASSEX);
winClass.style         = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc   = WindowProc;
winClass.hInstance     = hInstance;
winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
winClass.lpszMenuName  = NULL;
winClass.cbClsExtra    = 0;
winClass.cbWndExtra    = 0;

Everything else stays the same. :)