I'm sure there is a simple answer to this one somewhere. I currently have a program which correctly renders a .png image as a texture over a rectangle of vertices. This all works fine and displays correctly.
My question is how do I go about rendering a second texture onscreen using a separate image? The easiest/optimal way (I know they are probably different!) The second image I want to add will serve as the background image. I have tried duplicating the CUSTOMVERTEX structure, vertex buffer, separate vertex data, translation matrix, texture stage setting etc.. for the second texture I want to use, but I can only successfully render one on the screen, something is getting overwritten. Unfortunately I am still learning texturing and am missing the right approach. Is a completely separate vertex setup even required?
The code below reflects my working code so far, displaying an image as a texture overlayed on a rectangle of vertices. What should I add to setup & render a separate texture for my background?
//////////////////////////////////////////////////////
// I N V A D E R S
//////////////////////////////////////////////////////
#include <Windows.h> // Windows library (for window functions, menus, dialog boxes, etc)
#include <d3dx9.h> // Direct 3D library (for all Direct 3D functions).
//-----START-----DEFINE GLOBAL ELEMENTS----------//
LPDIRECT3D9 D3D_Object = NULL; // Name of the Direct3D Obeject. Used to create the D3DDevice
LPDIRECT3DDEVICE9 D3D_Device = NULL; // Name of the rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; // Buffer to hold vertices for the rectangle
LPDIRECT3DTEXTURE9 D3D_Tex_Background = NULL; // The texture for the background
LPDIRECT3DTEXTURE9 D3D_Tex_Invader = NULL; // The texture for the invader
float g_InvaderX = -50, g_InvaderY = 0, g_InvaderZ = 0; // starting cooordinates of object
// A structure for our custom vertex type, containing the vertex & texture coordinates
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // Vertex coordinates
FLOAT u, v; // Texture coordinates
};
// The structure of a vertex in our vertex buffer...
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1) // it will contain xyz coordinates & a texture
//////////////////////////////////////////////////////
// Setup Direct3D
//////////////////////////////////////////////////////
HRESULT SetupD3D(HWND hWnd)
{
// Create the D3D object.
if (NULL == (D3D_Object = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL;
// Set up the structure used to create the D3D Device
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE; // application will be windowed
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // back to front buffer behavior - DISCARD = random data used for error checking
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // back buffer format - UNKNOWN = use current display resolution to retain consistency
d3dpp.EnableAutoDepthStencil = TRUE; // D3D device to create and manage depth and stencil buffer automatically
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // format of the surfaces which hold the depth & stencil buffers - D16 = 16Bit colours
// Create the D3D Device - on successful completion return a pointer to the created device D3D_Device
if (FAILED(D3D_Object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &D3D_Device)))
{
return E_FAIL; // return error if D3D Device creation has failed
}
return S_OK;
}
//////////////////////////////////////////////////////
// DIRECT3D CLEANUP
//
// Release (delete) all the resources used by this program.
// Only release things if they are valid (i.e. have a valid pointer).
// If not, the program will crash at this point.
//////////////////////////////////////////////////////
void CleanUp()
{
// Delete the textures
if (D3D_Tex_Background != NULL) D3D_Tex_Background -> Release();
if (D3D_Tex_Invader != NULL) D3D_Tex_Invader -> Release();
// Release other D3D Device resources
if (g_pVertexBuffer != NULL) g_pVertexBuffer -> Release();
if (D3D_Device != NULL) D3D_Device -> Release();
if (D3D_Object != NULL) D3D_Object -> Release();
}
//////////////////////////////////////////////////////
// Setup the camera
//////////////////////////////////////////////////////
void SetupMatrices()
{
// Setup View Matrix
D3DXVECTOR3 vCamera(5.0f, 5.0f, -100.0f);
D3DXVECTOR3 vLookat(5.0f, 5.0f, 0.0f);
D3DXVECTOR3 vUpVector(0.0f, 1.0f, 0.0f);
D3DXMATRIX matrixView;
D3DXMatrixLookAtLH(&matrixView, &vCamera, &vLookat, &vUpVector);
D3D_Device -> SetTransform(D3DTS_VIEW, &matrixView);
// Setup Projection Matrix
// This transforms 2D geometry into a 3D space
D3DXMATRIX matrixProjection;
D3DXMatrixPerspectiveFovLH(&matrixProjection, D3DX_PI/4, 1.0f, 1.0f, 800.0f);
D3D_Device -> SetTransform(D3DTS_PROJECTION, &matrixProjection);
}
//////////////////////////////////////////////////////
// SETUP GEOMETRY -
// Define a square using the required verticies
//////////////////////////////////////////////////////
HRESULT SetupGeometry()
{
// Calculate the number of vertices required for the desired graphic
int Vertices = 2 * 3; // Six vertices required for the 2 triangles which make up the square.
// Calculate the size in bytes of the buffer which will hold the vertex information
// based on the number of verticies required and the elements contained within the CUSTOMVERTEX structure
int BufferSize = Vertices * sizeof(CUSTOMVERTEX);
// Create the vertex buffer which will store the vertex data to render
// (buffer size, special instructions, vertex buffer format, target memory location for buffer,pointer to vertex buffer, future use only)
if (FAILED(D3D_Device -> CreateVertexBuffer(BufferSize, 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL)))
{
return E_FAIL; // if the vertex buffer could not be created.
}
//////////////////////////////////////////////////////
// Fill vertex buffer with required vertex & texture info
//////////////////////////////////////////////////////
CUSTOMVERTEX *pVertices; // Create a pointer to the first vertex in the buffer.
if (FAILED(g_pVertexBuffer -> Lock(0, 0, (void**)&pVertices, 0))) // lock the buffer to prevent interference, allow access using the
{ // &pVerticies pointer
return E_FAIL; // if the pointer to the vertex buffer could not be established.
}
// Fill the vertex buffers with data concerning both vertex and texture locations
// Triangle 1
// Vertex 0
pVertices[0].position.x = 0; // Vertex coordinates
pVertices[0].position.y = 0;
pVertices[0].position.z = 0;
pVertices[0].u = 0; // texture coordinates
pVertices[0].v = 1;
// Vertex 1
pVertices[1].position.x = 0;
pVertices[1].position.y = 10;
pVertices[1].position.z = 0;
pVertices[1].u = 0;
pVertices[1].v = 0;
// Vertex 2
pVertices[2].position.x = 10;
pVertices[2].position.y = 0;
pVertices[2].position.z = 0;
pVertices[2].u = 1;
pVertices[2].v = 1;
// Triangle 2
// Vertex 3
pVertices[3].position.x = 10;
pVertices[3].position.y = 0;
pVertices[3].position.z = 0;
pVertices[3].u = 1;
pVertices[3].v = 1;
// Vertex 4
pVertices[4].position.x = 0;
pVertices[4].position.y = 10;
pVertices[4].position.z = 0;
pVertices[4].u = 0;
pVertices[4].v = 0;
// Vertex 5
pVertices[5].position.x = 10;
pVertices[5].position.y = 10;
pVertices[5].position.z = 0;
pVertices[5].u = 1;
pVertices[5].v = 0;
// Unlock the vertex buffer...
g_pVertexBuffer -> Unlock();
return S_OK;
}
//////////////////////////////////////////////////////
// Load the textures for the invaders and background
//////////////////////////////////////////////////////
void LoadTextures()
{
//D3DXCreateTextureFromFile(D3D_Device, // Direct3D Device name
// "background.jpg", // Source file in program folder
// &D3D_Tex_Background); // address of target texture object
D3DXCreateTextureFromFile(D3D_Device,
"invader300x300.png",
&D3D_Tex_Invader);
}
//////////////////////////////////////////////////////
// Render
//////////////////////////////////////////////////////
void Render()
{
// Clearing the current frame & Z buffers
// (rectangles to clear, rectangle coords, clear current buffer & Z buffer, RGB background colour of screen, initial z buffer value, initial
// stencil buffer value)
D3D_Device -> Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
// Once current frame has been cleared, begin drawing new frame
// lock is created at this point to prevent anything else drawing on the frame
if (SUCCEEDED(D3D_Device -> BeginScene()))
{
// Construct a translation matrix to move the Invader
D3DXMATRIX TranslateMatrix;
D3DXMatrixTranslation(&TranslateMatrix, g_InvaderX, g_InvaderY, g_InvaderZ);
D3D_Device -> SetTransform(D3DTS_WORLD, &TranslateMatrix);
// Render the contents of the vertex buffer.
D3D_Device -> SetStreamSource(0, g_pVertexBuffer, 0, sizeof(CUSTOMVERTEX));
D3D_Device -> SetFVF(D3DFVF_CUSTOMVERTEX);
D3D_Device -> DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// Select the invader texture, and initialise the texture stage state...
D3D_Device -> SetTexture(0, // the texture stage being set
D3D_Tex_Invader); // the object associated with the texture image
D3D_Device -> SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
D3D_Device -> SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
// Update the invader's x co-ordinate.
//if (g_RectX <=14.5)
g_InvaderX += 0.5f;
// Drawing of the scene has finished
D3D_Device -> EndScene();
}
// Present the back frame buffer contents to the screen (front frame buffer swaped with back fram buffer)
D3D_Device -> Present(NULL, NULL, NULL, NULL);
}
//////////////////////////////////////////////////////
// Windows Message Handling
//////////////////////////////////////////////////////
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
//////////////////////////////////////////////////////
// Create the application window
//////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int)
{
// Register the window class
WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"Invaders", NULL};
RegisterClassEx(&wc);
// Create the application window
HWND hWnd = CreateWindow( "Invaders", // Window class's name
"I N V A D E R S", // Title bar text
WS_OVERLAPPEDWINDOW, // The window style
100, // window horizontal position
500, // window vertical position
800, // window width
800, // window height
GetDesktopWindow(), // the parent window's module
NULL, // the window's menu handle
wc.hInstance, // the instance handle
NULL);
// Initialize Direct3D
if (SUCCEEDED(SetupD3D(hWnd)))
{
// Create the scene geometry
if (SUCCEEDED(SetupGeometry()))
{
// Load the required texture(s) from files into memeory
LoadTextures();
// Display the window onscreen
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Initialse the Viewpoint / Camera based on defined settings
SetupMatrices();
// Enter message loop to handle any messages MS Windows sends to the application
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
Render();
}
}
}
CleanUp(); // release resources used ( delete all dynamically allocated objects)
UnregisterClass("Server", wc.hInstance);
return 0;
} // end WinMain