// ================================================================
// Filename: Texturemap.cpp
// Description: Drawing a quad in world space and texturemapping it
//
// This source corresponds to 32Bits.co.uk DirectX
// Basics Series 3 part 4.
// ================================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d8.h>
#include <d3dx8.h>
#include <dxerr8.h>
#include "D3DFuncs.h"
// Function declarations
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
HRESULT GameInit();
HRESULT GameLoop();
HRESULT GameShutDown();
HRESULT Render();
// Globals
static char strAppname[]="Direct3D Texturemapping";
LPDIRECT3D8 g_pD3D;
LPDIRECT3DDEVICE8 g_pDevice;
LPDIRECT3DSURFACE8 g_pBackSurface;
HWND g_hWnd;
D3DCURRENTSETTINGS g_D3DSettings;
// Globals specifically for this source
LPDIRECT3DVERTEXBUFFER8 g_pVertexBuffer;
LPDIRECT3DTEXTURE8 g_pTexture;
// Change our normal vertex struct to include members for texturemapping...
typedef struct _tagSimpleTexMapVertex
{
float x,y,z;
D3DCOLOR dwDiffuse;
float tu,tv;
} SIMPLETEXMAPVERTEX;
// ...and update our FVF define to match.
#define FVF_SIMPLETEXMAPVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(1))
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize=sizeof(WNDCLASSEX); // size of the window struct in bytes
wc.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // window styles to use
wc.lpfnWndProc=MsgProc; // function name of event handler
wc.hInstance=hInstance; // handle to this apps instance
wc.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);// background colour of window
wc.hIcon= LoadIcon(NULL, IDI_APPLICATION); // icon for the app window
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION); // icon when minimized to taskbar
wc.hCursor=LoadCursor(NULL, IDC_ARROW); // cursor to use for this window
wc.lpszClassName=strAppname; // name for this class
// Register the window class
RegisterClassEx( &wc );
g_D3DSettings.m_nDeviceWidth=800;
g_D3DSettings.m_nDeviceHeight=600;
g_D3DSettings.m_fScreenAspect=(float)g_D3DSettings.m_nDeviceWidth / (float)g_D3DSettings.m_nDeviceHeight;
// Create the application's window
g_hWnd = CreateWindow(strAppname, strAppname, WS_OVERLAPPEDWINDOW, 10, 10,
g_D3DSettings.m_nDeviceWidth, g_D3DSettings.m_nDeviceHeight,
NULL, NULL, wc.hInstance, NULL );
// Show the window
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
if(FAILED(GameInit()))
{
UnregisterClass( strAppname, wc.hInstance );
return -1;
}
// Enter the message loop
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
int count=0;
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
GameLoop();
}
}
GameShutDown();
UnregisterClass( strAppname, wc.hInstance );
return 0;
}
//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
HDC hDC;
PAINTSTRUCT PaintStruct;
switch( msg )
{
case WM_PAINT:
{
hDC=BeginPaint(hWnd, &PaintStruct); // Tell windows we want to update the window
// Do GDI drawing here
EndPaint(hWnd, &PaintStruct);
return 0;
}
case WM_KEYDOWN:
{
switch(wParam)
{
case VK_SPACE:
{
PostQuitMessage( 0 );
return 0;
}
}
}
case WM_DESTROY:
{
PostQuitMessage( 0 );
return 0;
}
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
}
// =====================================================================================
// High level functions for initialization, loop and shutdown
// =====================================================================================
HRESULT GameInit()
{
HRESULT rslt=0;
g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);
if(g_pD3D==NULL)
{
return D3DError(E_FAIL, __LINE__, __FILE__, "Failed to create a D3D8 object.");
}
// Populate our struct with how we want to set up D3D...
g_D3DSettings.m_bWindowed=TRUE;
g_D3DSettings.m_bMultiSampling=FALSE;
g_D3DSettings.m_D3DFormat=D3DFMT_X8R8G8B8;
// ...and pass it to our function to create the device!
rslt=InitDirect3DDevice(g_hWnd, g_D3DSettings, g_pD3D, &g_pDevice);
if(FAILED(rslt))
{
return E_FAIL;
}
// ===================================================================================
// Set up our Projection, View and World transformations
// ===================================================================================
// Create a matrix to store our Projection transform. Null all the fields.
D3DXMATRIX matProjection;
ZeroMemory(&matProjection, sizeof(matProjection));
// Use D3DX to create a left handed cartesian Field Of View transform
D3DXMatrixPerspectiveFovLH(&matProjection, D3DX_PI/4, g_D3DSettings.m_fScreenAspect,
1.0f, 100.0f);
// Tell D3D to use our Projection matrix for the projection transformation stage
rslt=g_pDevice->SetTransform(D3DTS_PROJECTION, &matProjection);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set Projection Transform."); }
// Create a matrix to store our View transform. Null all the fields.
D3DXMATRIX matView;
ZeroMemory(&matView, sizeof(matView));
// Use D3DX to create a Look At matrix from eye, lookat and up vectors.
D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0.0f, 0.0f, -10.0f),
&D3DXVECTOR3(0.0f, 0.0f, 0.0f),
&D3DXVECTOR3(0.0f, 1.0f, 0.0f));
// Tell D3D to use our View matrix for the view transformation stage
g_pDevice->SetTransform(D3DTS_VIEW, &matView);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set View Transform."); }
// Create a matrix to store our World transform
D3DXMATRIX matWorld;
// Set the matrix to an identity matrix (one that makes no change)
D3DXMatrixIdentity(&matWorld);
// Tell D3D to use our World matrix for the world transformation stage
g_pDevice->SetTransform(D3DTS_WORLD, &matWorld);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set World Transform."); }
// ===================================================================================
// Set up our primitive and vertex buffer
// ===================================================================================
// Set our culling & lighting renderstates
g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
g_pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
// Create a quad for texturemapping, and assign each vertex a texcoord:
SIMPLETEXMAPVERTEX Quad[4];
Quad[0].x=-3.0f; Quad[0].y=-3.7f; Quad[0].z=0.0f;
Quad[0].dwDiffuse=D3DCOLOR_XRGB(255,255,255);
Quad[1].x=-3.0f; Quad[1].y=3.0f; Quad[1].z=0.0f;
Quad[1].dwDiffuse=D3DCOLOR_XRGB(255,255,255);
Quad[2].x=3.0f; Quad[2].y=-3.7f; Quad[2].z=0.0f;
Quad[2].dwDiffuse=D3DCOLOR_XRGB(255,255,255);
Quad[3].x=3.0f; Quad[3].y=3.0f; Quad[3].z=0.0f;
Quad[3].dwDiffuse=D3DCOLOR_XRGB(255,255,255);
// Uncomment the section of code (and comment out the rest) to see the effect.
// These texcoords demonstrate wrapping mode. They repeat the texture 3 times in the x & y axis
Quad[0].tu=0.0f; Quad[0].tv=3.0f;
Quad[1].tu=0.0f; Quad[1].tv=0.0f;
Quad[2].tu=3.0f; Quad[2].tv=3.0f;
Quad[3].tu=3.0f; Quad[3].tv=0.0f;
/*
// These texcoords demonstrate mirroring mode. They mirror the texture 3 times in both the x & y axis
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_MIRROR);
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_MIRROR);
Quad[0].tu=0.0f; Quad[0].tv=3.0f;
Quad[1].tu=0.0f; Quad[1].tv=0.0f;
Quad[2].tu=3.0f; Quad[2].tv=3.0f;
Quad[3].tu=3.0f; Quad[3].tv=0.0f;
*/
/*
// These texcoords demonstrate mirror once mode. They produce a hybrid of mirror and clamp modes.
// Note that this mode is not supported by the Geforce card range.
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_MIRRORONCE);
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_MIRRORONCE);
Quad[0].tu=0.0f; Quad[0].tv=3.0f;
Quad[1].tu=0.0f; Quad[1].tv=0.0f;
Quad[2].tu=3.0f; Quad[2].tv=3.0f;
Quad[3].tu=3.0f; Quad[3].tv=0.0f;
*/
/*
// These texcoords demonstrate border colour mode. They show the texture with the scale you
// specify using texcoords, then fill the rest of the primitive with the colour you choose
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_BORDER);
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_BORDER);
g_pDevice->SetTextureStageState(0, D3DTSS_BORDERCOLOR, D3DCOLOR_XRGB(100,255,100));
Quad[0].tu=0.0f; Quad[0].tv=2.0f;
Quad[1].tu=0.0f; Quad[1].tv=0.0f;
Quad[2].tu=2.0f; Quad[2].tv=2.0f;
Quad[3].tu=2.0f; Quad[3].tv=0.0f;
*/
/*
// These texcoords demonstrate clamp mode. They show the texture with the scale you
// specify using texcoords, then extrude the last pixel to the borders of the primitive.
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
g_pDevice->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
Quad[0].tu=0.0f; Quad[0].tv=3.0f;
Quad[1].tu=0.0f; Quad[1].tv=0.0f;
Quad[2].tu=3.0f; Quad[2].tv=3.0f;
Quad[3].tu=3.0f; Quad[3].tv=0.0f;
*/
// Next, create our vertex buffer using the properties of our custom struct and FVF
rslt=g_pDevice->CreateVertexBuffer(sizeof(Quad), 0, FVF_SIMPLETEXMAPVERTEX, D3DPOOL_DEFAULT,
&g_pVertexBuffer);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "CreateVertexBuffer() failed."); }
// Now lock the vertex buffer...
BYTE* pVerticeLock=0;
rslt=g_pVertexBuffer->Lock(0, sizeof(Quad), &pVerticeLock, 0);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Vertex Buffer."); }
// ...and copy our array of vertices straight into it
CopyMemory(pVerticeLock, &Quad, sizeof(Quad));
// Remember to unlock the vertex buffer once we're done.
g_pVertexBuffer->Unlock();
// ===================================================================================
// Create our texture surface for texturemapping
// ===================================================================================
// Create the texture surface from the file
rslt=D3DXCreateTextureFromFileEx(g_pDevice, "jessica.jpg", D3DX_DEFAULT, D3DX_DEFAULT,
D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &g_pTexture);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create texture."); }
return S_OK;
}
HRESULT GameLoop()
{
return Render();
}
HRESULT GameShutDown()
{
// Don't forget to release the vertex buffer - it's a COM interface!
if(g_pVertexBuffer)
g_pVertexBuffer->Release();
if(g_pTexture)
g_pTexture->Release();
if(g_pBackSurface)
g_pBackSurface->Release();
if(g_pDevice)
g_pDevice->Release();
if(g_pD3D)
g_pD3D->Release();
return S_OK;
}
// =====================================================================================
// Main render function to perform D3D drawing
// =====================================================================================
HRESULT Render()
{
HRESULT rslt=NULL;
// ====================================================================================
// - Do all the usual checks to make sure we have the right pointers, etc...
// ====================================================================================
// Make sure we have a valid D3D Device
if(!g_pDevice) { return E_FAIL; }
// Return if the device is not ready
rslt=ValidateDevice(g_pDevice, g_pBackSurface, g_D3DSettings);
if(FAILED(rslt)) { return rslt; }
// Clear the back buffer
g_pDevice->Clear(0,0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,55), 1.0f, 0);
// Get a pointer to the back buffer (remember, page flipping has taken place)
rslt=g_pDevice->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &g_pBackSurface);
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to get the back buffer."); }
rslt=g_pDevice->BeginScene();
if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "BeginScene() failed."); }
// ====================================================================================
// - Do our drawing operations
// ====================================================================================
// Tell D3D we want to use our vertex buffer as the "source stream"
g_pDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(SIMPLETEXMAPVERTEX));
// Tell D3D we want to use our custom FVF define
g_pDevice->SetVertexShader(FVF_SIMPLETEXMAPVERTEX);
// Tell D3D to use the following texture for texturemapping the next set of primitives
g_pDevice->SetTexture(0, g_pTexture);
// And finally tell D3D to draw our triangle!
g_pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
// ====================================================================================
// - Clean up and present the back buffer to be page flipped
// ====================================================================================
g_pDevice->EndScene();
g_pBackSurface->Release();
// Present the back buffer to the display adapter to be drawn
g_pDevice->Present(NULL, NULL, NULL, NULL);
return S_OK;
}