Recently I have been following the tutorials at directxtutorial.com and so far they have been great. Unfortunately, I have hit a major snag and over the last few days I have been crawling the internet trying to find a solution which I havn't yet.
This issue lies with the D3DXCreateTextureFromFile() function in init_graphics(). Here is the whole code as it is right now:
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 1440
#define SCREEN_HEIGHT 900
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// include the Direct3D Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
// global declarations
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
// texture declarations
LPDIRECT3DTEXTURE9 texture_1; // our first texture
// mesh declatations
LPD3DXMESH meshSpaceship;
D3DMATERIAL9* material; // define the material object
LPDIRECT3DTEXTURE9* texture; // a pointer to a texture
DWORD numMaterials;
// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory
void init_graphics(void); // 3D declarations
void init_light(void); // sets up the light and the material
struct CUSTOMVERTEX {FLOAT X, Y, Z; D3DVECTOR NORMAL; FLOAT U, V;};
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",
WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
// set up and initialize Direct3D
initD3D(hWnd);
// enter the main loop:
MSG msg;
while(TRUE)
{
DWORD starting_point = GetTickCount();
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render_frame();
// check the 'escape' key
if(KEY_DOWN(VK_ESCAPE))
PostMessage(hWnd, WM_DESTROY, 0, 0);
while ((GetTickCount() - starting_point) < 25);
}
// clean up DirectX and COM
cleanD3D();
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// create a device class using this information and the info from the d3dpp stuct
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
init_graphics(); // call the function to initialize the cube
init_light(); // call the function to initialize the light and material
d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE); // turn on the 3D lighting
d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE); // turn on the z-buffer
d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50)); // ambient light
return;
}
// this is the function used to render a single frame
void render_frame(void)
{
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->BeginScene();
// set the view transform
D3DXMATRIX matView; // the view transform matrix
D3DXMatrixLookAtLH(&matView,
&D3DXVECTOR3 (0.0f, 8.0f, 25.0f), // the camera position
&D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at position
&D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction
d3ddev->SetTransform(D3DTS_VIEW, &matView);
// set the projection transform
D3DXMATRIX matProjection; // the projection transform matrix
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45), // the horizontal field of view
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
1.0f, // the near view-plane
100.0f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);
// set the world transform
static float index = 0.0f; index+=0.03f; // an ever-increasing float value
D3DXMATRIX matRotateY; // a matrix to store the rotation for each triangle
D3DXMatrixRotationY(&matRotateY, index); // the rotation matrix
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY)); // set the world transform
// draw the spaceship
for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
d3ddev->SetMaterial(&material); // set the material for the subset
if(texture != NULL) // if the subset has a texture (if texture is not NULL)
d3ddev->SetTexture(0, texture); // ...then set the texture
meshSpaceship->DrawSubset(i); // draw the subset
}
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
return;
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
meshSpaceship->Release(); // close and release the spaceship mesh
d3ddev->Release(); // close and release the 3D device
d3d->Release(); // close and release Direct3D
return;
}
// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
LPD3DXBUFFER bufShipMaterial;
D3DXLoadMeshFromX(L"airplane 2.x", // load this file
D3DXMESH_SYSTEMMEM, // load the mesh into system memory
d3ddev, // the Direct3D Device
NULL, // we aren't using adjacency
&bufShipMaterial, // put the materials here
NULL, // we aren't using effect instances
&numMaterials, // the number of materials in this model
&meshSpaceship); // put the mesh here
// retrieve the pointer to the buffer containing the material information
D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufShipMaterial->GetBufferPointer();
// create a new material buffer for each material and texture in the mesh
material = new D3DMATERIAL9[numMaterials];
texture = new LPDIRECT3DTEXTURE9[numMaterials];
for(DWORD i = 0; i < numMaterials; i++) // for each material...
{
material = tempMaterials.MatD3D; // get the material info
material.Ambient = material.Diffuse; // make ambient the same as diffuse
// if there is a texture to load, load it
if(FAILED(D3DXCreateTextureFromFile(d3ddev,
tempMaterials.pTextureFilename,
&texture)))
texture = NULL; // if there is no texture, set the texture to NULL
}
return;
}
// this is the function that sets up the lights and materials
void init_light(void)
{
D3DLIGHT9 light; // create the light struct
D3DMATERIAL9 material; // create the material struct
ZeroMemory(&light, sizeof(light)); // clear out the struct for use
light.Type = D3DLIGHT_DIRECTIONAL; // make the light type 'directional light'
light.Diffuse.r = 0.5f; // .5 red
light.Diffuse.g = 0.5f; // .5 green
light.Diffuse.b = 0.5f; // .5 blue
light.Diffuse.a = 1.0f; // full alpha (we'll get to that soon)
D3DVECTOR vecDirection = {-1.0f, -0.3f, -1.0f}; // the direction of the light
light.Direction = vecDirection; // set the direction
d3ddev->SetLight(0, &light); // send the light struct properties to light #0
d3ddev->LightEnable(0, TRUE); // turn on light #0
ZeroMemory(&material, sizeof(D3DMATERIAL9)); // clear out the struct for use
material.Diffuse.r = material.Ambient.r = 1.0f; // set the material to full red
material.Diffuse.g = material.Ambient.g = 1.0f; // set the material to full green
material.Diffuse.b = material.Ambient.b = 1.0f; // set the material to full blue
material.Diffuse.a = material.Ambient.a = 1.0f; // set the material to full alpha
d3ddev->SetMaterial(&material); // set the globably-used material to &material
return;
}
The origenal source used the ATL functions USES_CONVERSION and CA2W to get arround this. But as I don't have ATL (express edition) I removed these. If I run the code above I get:
(246) : error C2664: 'D3DXCreateTextureFromFileW' : cannot convert parameter 2 from 'LPSTR' to 'LPCWSTR'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
So I tried a traditional style cast and a reinterpret cast. Both produced the same result, the airplane 2.x model displaying correctly but with no textures.
This is the bit that I changed:
if(FAILED(D3DXCreateTextureFromFile(d3ddev,
(LPCWSTR)tempMaterials.pTextureFilename,
&texture)))
texture = NULL; // if there is no texture, set the texture to NULL
I don't know what a function cast is though presumably, that is what CA2W is. If I plug in the name of one of the textures it works correctly although only using the texture specified:
if(FAILED(D3DXCreateTextureFromFile(d3ddev,
L"wings.bmp",
&texture)))
texture = NULL; // if there is no texture, set the texture to NULL
Does anyone know of a work around for this? Is there another way to convert tempMaterials
.pTextureFilename from a LPSTR to a LPCWSTR? Or is there a better way to do it somwhere else? Any help is apreciated on this matter.
(BTW, the airplane 2.x comes with the sdk I believe)