Reputation: 115
At the moment, I'm doing a few test cases with DirectX before I begin working on a program. And right now, I've been messing with the code below for a day or so. I revised code while looking around on the internet at similar problems. But in the end, it's not working the way I want it, too. Can someone tell me what exactly I'm doing wrong? Maybe someone else could fix this.
// TetrisClone.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "TetrisClone.h"
#include <d3d9.h>
#include <d3dx9.h>
#define MAX_LOADSTRING 100
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
//for testing purposes
struct CUSTOMVERTEX
{
FLOAT x, y, z; //position
DWORD color; //Color
};
// global declarations for Direct3d
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
// 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
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_TETRISCLONE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRISCLONE));
// Main message loop:
while(TRUE)
{
// Check to see if any messages are waiting in the queue
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Translate the message and dispatch it to WindowProc()
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// If the message is WM_QUIT, exit the while loop
if(msg.message == WM_QUIT)
break;
render_frame();
}
cleanD3D();
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRISCLONE));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_GRAYTEXT);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TETRISCLONE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
// set up and initialize Direct3D
initD3D(hWnd);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
/***
Direct3d functions
***/
// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION); // create the Direct3D interface
D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information
ZeroMemory(&d3dpp, sizeof(d3dpp)); // clear out the struct for use
d3dpp.Windowed = TRUE; // program windowed, not fullscreen
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // discard old frames
d3dpp.hDeviceWindow = hWnd; // set the window to be used by Direct3D
// create a device class using this information and information from the d3dpp stuct
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
init_graphics();
}
// this is the function used to render a single frame
void render_frame(void)
{
// clear the window to a deep blue
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
d3ddev->BeginScene(); // begins the 3D scene
//set FVF
d3ddev->SetFVF(D3DFVF_CUSTOMVERTEX);
//setting stream source
d3ddev->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
// do 3D rendering on the back buffer here
//drawing triangle
d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
d3ddev->EndScene(); // ends the 3D scene
d3ddev->Present(NULL, NULL, NULL, NULL); // displays the created frame
}
// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
// create the vertices using the CUSTOMVERTEX struct
CUSTOMVERTEX vertices[] =
{
{ 400.0f, 62.5f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
{ 650.0f, 500.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), },
{ 150.0f, 500.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
};
// create a vertex buffer interface called v_buffer
d3ddev->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),
0,
D3DFVF_CUSTOMVERTEX,
D3DPOOL_MANAGED,
&g_pVB,
NULL);
VOID* pVoid; // a void pointer
// lock v_buffer and load the vertices into it
g_pVB->Lock(0, 0, (void**)&pVoid, 0);
memcpy(pVoid, vertices, sizeof(vertices));
g_pVB->Unlock();
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
g_pVB->Release();
d3ddev->Release(); // close and release the 3D device
d3d->Release(); // close and release Direct3D
}
Upvotes: 3
Views: 2541
Reputation: 1155
I was able to render your scene with a few tweaks!
The first thing I notice is that your FVF is not defined in a way I am used to that would render without a camera. (Not 100% on this) In your case if you wanted just position and color without camera you would use
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
instead of what you had.
This also means you need to change your custom vertex structure to add the w component and this link might help you understand what the w is for. In basic terms XYZRHW means that the points are translated to screen space already, not in world space. ( quick google search: http://www.gamedev.net/topic/200077-xyz-rhw/ )
struct CUSTOMVERTEX
{
FLOAT x, y, z, w; //position
DWORD color; //Color
};
With this change you also need to change your vertices in init_graphics, try something like this, (what you had but with the w component added ) I also removed some extra commas that were bothering me.
CUSTOMVERTEX vertices[] =
{
{ 400.0f, 62.5f, 0.0f, 1, D3DCOLOR_XRGB(0,0,255) },
{ 650.0f, 500.0f, 0.0f, 1, D3DCOLOR_XRGB(0,255,0) },
{ 150.0f, 500.0f, 0.0f, 1, D3DCOLOR_XRGB(255,0,0) }
};
Second, in your init_graphics function your vertex buffer lock is locking a size of 0! Change it to lock the number of vertices you have and in this case it's 3.
g_pVB->Lock(0, 3*sizeof(CUSTOMVERTEX), (void**)&pVoid, 0);
Be careful with the way you are using memcpy, if you were passing the vertex data between functions your pointer would degrade causing sizeof to only retrieve the size of one element in your array. It is not necessary AT ALL in your current code to change this but it's something that could matter in the future and you should know about. Using something like this would make this more future proof in my eyes.
memcpy(pVoid, vertices, 3*sizeof(CUSTOMVERTEX));
If there isn't anything I forgot, that should be enough to get you up and running. Good luck!
Upvotes: 1
Reputation: 8757
Since you didn't set a camera and didn't do any transformation of your vertex, so, you should set your vertex in the range of the cuboid(after perspective projection transform), which is
-1 << 1 << 1
-1 << y << 1
0 << z << 1
The following change will make your triangle visible.
CUSTOMVERTEX vertices[] =
{
{ 0.0f, 0.0, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
{ 0.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), },
{ 1.0f, 0.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
};
There are many reasons can cause a geometry/model invisible in Direct3D programs, I list some as below.
Normally, we need to enable lighting during rendering, but if you use the LT(Lit and Transformed) vertex format, that means you define the color for the vertex and use the screen coordinates for it, this tell Direct3D engine: "Hey, do not lighting and transform my vertex, I will do that!", in your code you didn't use the LT format, that means you should transform and light your vertex, but you didn't have any light settings, so you will got an black triangle, in fact, you didn't get anything, since the vertex coordinates was out of view.
We usually specify vertex coordinates in object space, and then transform them to world space->view space->projection space->screen space, in your code above, you didn't do any transfer, but your vertex coordinates is too large, it's out of the range of the cuboid after perspective transformation, the range is -1 << x << 1, -1 << y << 1, 0 << z << 1.
This is also an very important factor to consider, by default, Direct3D use will culling all faces define in counter-clockwise, see this thread Odd behaviour when trying to create triangle using TriangleStrip Topologu for details.
Frist, make sure your camera faces what you are drawing, then set a correct distance. you use a big coordinates for your vertex, so, you should set the camera far from the geometry you are rendering, otherwise you will see nothing.
It seems a litter funny for this item, but it does happened, When we want to test whether the rendering works, we always comment out some code in the render function and draw something else, we may also test whether the program works without any drawing code, then we forgot the uncomment the drawing code back which caused nothing present on the screen. I have encountered this problem when I learn D3D initially.
Upvotes: 3