problem using spotlights with directx

Started by
2 comments, last by jollyjeffers 17 years, 10 months ago
I have managed to display a 3d rotating cube correctly and i can use directional lights ok but when i try to enable a spot light on my cube i cant get it to display correctly and the cube either blacks our or displays in the color thats supposed to be diffused, also my code is only in one document since im relatively new to directx, any help advice and an explanation as too why this problem is occuring would be great. sorry for the very long post :S EDIT by jollyjeffers: use [source] tags around blocks of code... [smile]
#include<d3dx9.h>

IDirect3D9 *d3d9 = 0;
IDirect3DDevice9 *device = 0;
IDirect3DVertexBuffer9 *VB = 0;
IDirect3DIndexBuffer9 *IB = 0;
const D3DXCOLOR RED(D3DCOLOR_XRGB(255, 0, 0));
const D3DXCOLOR WHITE(D3DCOLOR_XRGB(255, 255, 255));
const D3DXCOLOR BLACK(D3DCOLOR_XRGB(0, 0, 0));

const int width = 640;
const int height = 480;

struct Vertex
{
	Vertex(){}
	Vertex(float x, float y, float z, float nx, float ny, float nz)
	{
		_x = x; _y = y; _z = z;
		_nx = nx; _ny = ny; _nz = nz;
	}
	float _x, _y, _z;
	float _nx, _ny, _nz;
	D3DCOLOR _color;

	static const DWORD FVF;
};

#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

	switch(msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE)
		{
			DestroyWindow(hWnd);
		}
		break;
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASS wc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = NULL;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hIcon = LoadIcon(0, IDI_APPLICATION);
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = L"d3dwindow";
	wc.lpszMenuName = 0;
	wc.style = CS_HREDRAW | CS_VREDRAW;

	RegisterClass(&wc);

	HWND hWnd = CreateWindow(L"d3dwindow", L"Direct3D9App", 
							WS_EX_TOPMOST,
							0, 0, width, height,
							0, 0, hInstance, 0); 

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	//init d3d

	d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

	//check caps

	D3DCAPS9 caps;
	d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);


	int vp = 0;
	if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
	{
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	}
	else
	{
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.BackBufferWidth = width;
	d3dpp.BackBufferHeight = height;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
	d3dpp.BackBufferCount = 1;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.Windowed = TRUE;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	//create device

	d3d9->CreateDevice(D3DADAPTER_DEFAULT, // primary adapter
					D3DDEVTYPE_HAL,         // device type
					hWnd,               // window associated with device
					vp,                 // vertex processing
					&d3dpp,             // present parameters
					&device);

	device->CreateVertexBuffer(8*sizeof(Vertex), D3DUSAGE_WRITEONLY, D3DFVF_VERTEX, D3DPOOL_MANAGED, &VB, 0);

	device->CreateIndexBuffer(36*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &IB, 0);

	Vertex *vertices;
	VB->Lock(0, 0, (void**)&vertices, 0);

	// vertices of a unit cube
	vertices[0] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f);
	vertices[1] = Vertex(-1.0f,  1.0f, -1.0f, -1.0f, 1.0f, -1.0f);
	vertices[2] = Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 1.0f, -1.0f);
	vertices[3] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f);
	vertices[4] = Vertex(-1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f);
	vertices[5] = Vertex(-1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f);
	vertices[6] = Vertex( 1.0f,  1.0f,  1.0f, 1.0f,  1.0f,  1.0f);
	vertices[7] = Vertex( 1.0f, -1.0f,  1.0f, 1.0f, -1.0f,  1.0f);

	VB->Unlock();

	// define the triangles of the cube:
	WORD* indices = 0;
	IB->Lock(0, 0, (void**)&indices, 0);

	// front side
	indices[0]  = 0; indices[1]  = 1; indices[2]  = 2;
	indices[3]  = 0; indices[4]  = 2; indices[5]  = 3;

	// back side
	indices[6]  = 4; indices[7]  = 6; indices[8]  = 5;
	indices[9]  = 4; indices[10] = 7; indices[11] = 6;

	// left side
	indices[12] = 4; indices[13] = 5; indices[14] = 1;
	indices[15] = 4; indices[16] = 1; indices[17] = 0;

	// right side
	indices[18] = 3; indices[19] = 2; indices[20] = 6;
	indices[21] = 3; indices[22] = 6; indices[23] = 7;

	// top
	indices[24] = 1; indices[25] = 5; indices[26] = 6;
	indices[27] = 1; indices[28] = 6; indices[29] = 2;

	// bottom
	indices[30] = 4; indices[31] = 0; indices[32] = 3;
	indices[33] = 4; indices[34] = 3; indices[35] = 7;

	IB->Unlock();

	D3DLIGHT9 dir;
	::ZeroMemory(&dir, sizeof(dir));
	dir.Type      = D3DLIGHT_SPOT;
	dir.Ambient   = WHITE * 0.0f;
	dir.Diffuse   = WHITE;
	dir.Specular  = WHITE * 0.6f;
	dir.Position  = D3DXVECTOR3 (0.0f, 0.0f, -5.0f);
	dir.Direction = D3DXVECTOR3 (0.0f, 0.0f,  1.0f);
	dir.Range        = 1000.0f;
	dir.Falloff      = 1.0f;
	dir.Attenuation0 = 1.0f;
	dir.Attenuation1 = 0.0f;
	dir.Attenuation2 = 0.0f;
	dir.Theta        = 0.4f;
	dir.Phi          = 0.9f;
	
	device->SetLight(0, &dir);
	device->LightEnable(0, true);
		
	D3DMATERIAL9 mtrl;
	mtrl.Ambient = RED;
	mtrl.Diffuse = RED;
	mtrl.Specular = RED;
	mtrl.Emissive = BLACK;
	mtrl.Power = 1.0f;

	device->SetMaterial(&mtrl);

	// position and aim the camera
	D3DXVECTOR3 position(0.0f, 0.0f, -5.0f);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMATRIX V;
	D3DXMatrixLookAtLH(&V, &position, &target, &up);
	device->SetTransform(D3DTS_VIEW, &V);

	// set projection matrix
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, (float)width / (float)height, 1.0f, 1000.0f);
	device->SetTransform(D3DTS_PROJECTION, &proj);

	// set the render states
	device->SetRenderState(D3DRS_LIGHTING, true);
	device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
	device->SetRenderState(D3DRS_SPECULARENABLE, true);

	MSG msg; 

	ZeroMemory(&msg, sizeof(MSG));

		while( msg.message!=WM_QUIT )
        {
			if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}
			else
			{
				if(device)
				{
					device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);

					D3DXMATRIX Rx, Ry;

					UINT  iTime  = timeGetTime() % 50000000;
					FLOAT Rotatey = iTime * (2.0f * D3DX_PI) / 1000.0f;

					D3DXMatrixRotationY(&Ry, timeGetTime()/Rotatey);
					D3DXMatrixRotationX(&Rx, 6.5f);

					D3DXMATRIX p = Rx * Ry;

					device->BeginScene();
					device->SetTransform(D3DTS_WORLD, &p);
					device->SetStreamSource(0, VB, 0, sizeof(Vertex));
					device->SetIndices(IB);
					device->SetFVF(D3DFVF_VERTEX);
					device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
					if(Rotatey <= D3DX_PI/2)
					{
						Rotatey = 0.0f;
					}

					device->EndScene();            
					ValidateRect( hWnd, NULL );
					device->Present(0, 0, 0, 0);// present backbuffer
				}
			}
		}

	d3d9->Release();
	device->Release();

	DestroyWindow( hWnd );
	UnregisterClass( L"d3dwindow", GetModuleHandle(0) ); 

	return true;
}
Advertisement
For starters, your vertex structure does not match you FVF. You have position, normal, and color in the struct, but your FVF only has position and color. I don't know why it didn't bomb with that. However, since normals are not defined in the FVF, it won't be able to use the lights anyway. It will just use the vertex colors when rendering.

You also do not set the color components of the struct when you create the vertex buffer.
--------------------------Most of what I know came from Frank D. Luna's DirectX books
the color part of the struct was left over from a previous exercise, i wasnt too sure about the FVF stuff as the book im using just kind of says do this, and didnt really explain it, if you have any info on this that you could write over a board please share :P
Quote:Original post by LordFallout
the color part of the struct was left over from a previous exercise, i wasnt too sure about the FVF stuff as the book im using just kind of says do this, and didnt really explain it, if you have any info on this that you could write over a board please share :P
An FVF (and their bigger and better counterparts 'vertex declarations') simply describes the incoming data to the API/driver/GPU.

When you send it geometry in a vertex buffer all it sees is a huge block of binary data - it has no knowledge of the struct/types you used to construct that data (note that you use a VOID** when Lock()'ing them). If it's to perform the correct computations it needs to know which 1's and 0's correspond to what type of data (int, float...) and for what semantic ('position', 'color', 'normal')...

An FVF despite having "flexible" in its name has a fixed ordering and meaning. It's for the fixed function pipeline so that doesn't really matter - its only when you hit vertex shaders that this gets inconvenient.

D3DFVF_XYZ | D3DFVF_DIFFUSE tells the API that your vertex is composed of a 3-component XYZ vector (defined as 3 float's) and a D3DCOLOR component. The coordinate comes before the diffuse, so it knows that OFFSET + 0 = Position(X,Y,Z) and OFFSET + sizeof(Position) = Diffuse. Where 'OFFSET' is the "stride" you set for SetStreamSource().

Now, the problem comes when the binary and the FVF dont match up - either mismatched elements (e.g. as DXNut pointed out - you declare "Position + Color" but provide "Position + Normal") or if the binary data is NOT in the order dictated by the FVF (e.g. diffuse comes before position).

Because the GPU/driver gets a description and a block of data it'll just start chomping away and interpret the wrong bits for the wrong data types for the wrong semantics. I'm sure you can understand that this isn't good [wink]

Having the correct vertex stride (as you have) helps, but the fact that your FVF declares a binary format that is shorter than your actual binary data means that its effectively going to cast the Vertex::_nx field (32bit float) to your diffuse (32bit integer). It'll ignore the '_ny' and '_nz' components as you've not told it to do anything with them. All-in-all, its unlikely to yield any sensible values [smile]

Moral of the story: Make sure your declaration/FVF matches the real binary format

That'll solve at least part of your problem - but probably not all of it. But I'll leave you in the capable hands of other forum regulars as it's past my bed-time now [grin]

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement