// Direct3D Immediate Mode by Bipin Patwardhan // Full Source Listing #define INITGUID #include <windows.h> #include <windowsx.h> #include <stdio.h> #include <string.h> #include <search.h> #include <ddraw.h> #include <d3drmwin.h> #include <d3d.h> #include <d3dtypes.h> #include "d3dmacs.h" #define MAX_DRIVERS 5 #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 #define BIT_DEPTH 8 #define RELEASE(x) if (x != NULL) {x->Release(); x = NULL;} #define NUM_VERTICES 3 #define NUM_TRIANGLES 1 typedef struct t_glob { HWND hWnd; HINSTANCE hInstance; // current shade mode, fill mode and lighting state D3DRMRENDERQUALITY RenderQuality; LPDIRECTDRAWSURFACE lpDDSFront, lpDDSBack; LPDIRECT3DDEVICE dev; // device LPDIRECT3DVIEWPORT view; // viewport through which we view LPDIRECT3DEXECUTEBUFFER lpD3DExBuf; GUID DriverGUID[MAX_DRIVERS]; // GUIDs of available D3D drivers char DriverName[MAX_DRIVERS][50]; // names of available D3D drivers int NumDrivers; // number of available D3D drivers int curDriver; // number of D3D driver currently being used int BPP; int binitDone; int bquit; } glob; // Global Variables glob globals; LPDIRECT3DMATERIAL lpBmat, lpMat1; LPDIRECTDRAW lpDD; D3DEXECUTEDATA d3dExData; LPDIRECT3DEXECUTEBUFFER lpD3DExBuf; D3DMATRIXHANDLE hViewPos, hView, hViewRot; D3DMATRIX viewpos = { D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0), D3DVAL(1.0) }; D3DMATRIX viewrot, view, dviewrot, dworld; D3DMATRIX identity = { D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0) }; int APIENTRY WinMain(HMODULE hInstance, HMODULE hNull, PSTR lpszCmdLine, int nCmdShow) { MSG msg; int failcount = 0; InitGlobals(); InitWindows(hInstance, hNull, lpszCmdLine, nCmdShow); InitDirectDraw(globals.hWnd); ShowWindow(globals.hWnd, SW_SHOWNORMAL); UpdateWindow(globals.hWnd); globals.bquit = FALSE; while ( !globals.bquit ) { while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { if ( msg.message == WM_QUIT ) { CleanupAndPostQuit(); break; } TranslateMessage(&msg); DispatchMessage(&msg); } if ( !globals.bquit ) RenderLoop(); } return msg.wParam; } BOOL InitWindows(HMODULE hInstance, HMODULE hnull, PSTR lpszCmd, int nCmdShow) { WNDCLASS wc; HWND hWnd; HDC hdc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "Immediate"; RegisterClass(&wc); hWnd = CreateWindow("Immediate", "Direct3D Immediate Mode", WS_OVERLAPPEDWINDOW, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL); globals.hWnd = hWnd; globals.hInstance = hInstance; hdc = GetDC(globals.hWnd); globals.BPP = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(hWnd, hdc); return TRUE; } BOOL InitDirectDraw(HWND hWnd) { DirectDrawCreate(NULL, &lpDD, NULL); lpDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); lpDD->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, BIT_DEPTH); CreateSurface(hWnd, lpDD); InitDirect3D(lpDD); return TRUE; } BOOL CreateSurface(HWND hWnd, LPDIRECTDRAW lpDD) { LPDIRECTDRAWSURFACE lpDDSFront, lpDDSZ; DDSURFACEDESC ddsd; DDSCAPS ddscaps; HRESULT ret_val; // First Obtain the primary surface memset(&ddsd, 0, sizeof(DDSURFACEDESC)); ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_3DDEVICE; ddsd.dwBackBufferCount = 1; lpDD->CreateSurface(&ddsd, &lpDDSFront, NULL); // Get the back buffer ddscaps.dwCaps = DDSCAPS_BACKBUFFER; lpDDSFront->GetAttachedSurface(&ddscaps, &(globals.lpDDSBack)); // Create the Z buffer and attach it to the back surface memset(&ddsd, 0, sizeof(DDSURFACEDESC)); ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_ZBUFFERBITDEPTH; ddsd.dwWidth = SCREEN_WIDTH; ddsd.dwHeight = SCREEN_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER; ddsd.dwZBufferBitDepth = BIT_DEPTH; lpDD->CreateSurface(&ddsd, &lpDDSZ, NULL); globals.lpDDSBack->AddAttachedSurface(lpDDSZ); globals.lpDDSFront = lpDDSFront; return TRUE; } HRESULT WINAPI enumDeviceFunc(LPGUID lpGuid, LPSTR lpDeviceDescription, LPSTR lpDeviceName, LPD3DDEVICEDESC lpHWDesc, LPD3DDEVICEDESC lpHELDesc, LPVOID lpContext) { static BOOL hardware = FALSE; static BOOL mono = FALSE; LPD3DDEVICEDESC lpDesc; int *lpStartDriver = (int *)lpContext; lpDesc = lpHWDesc->dcmColorModel ? lpHWDesc : lpHELDesc; if (!(lpDesc->dwDeviceRenderBitDepth & BPPToDDBD(globals.BPP))) return D3DENUMRET_OK; memcpy(&globals.DriverGUID[globals.NumDrivers], lpGuid, sizeof(GUID)); lstrcpy(&globals.DriverName[globals.NumDrivers][0], lpDeviceName); if (*lpStartDriver == -1) { *lpStartDriver = globals.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } else if (lpDesc == lpHWDesc && !hardware) { *lpStartDriver = globals.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } else if ((lpDesc == lpHWDesc && hardware ) || (lpDesc == lpHELDesc && !hardware)) { if (lpDesc->dcmColorModel == D3DCOLOR_MONO && !mono) { *lpStartDriver = globals.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } } globals.NumDrivers++; if (globals.NumDrivers == MAX_DRIVERS) return (D3DENUMRET_CANCEL); return (D3DENUMRET_OK); } BOOL InitDirect3D(LPDIRECTDRAW lpDD) { LPDIRECT3D lpD3D; LPGUID curDriver; LPDIRECT3DDEVICE lpD3DDev; D3DVIEWPORT viewData = { 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 1, 1, 300, 300, 300 }; viewData.dwSize = sizeof(D3DVIEWPORT); // Get Direct3D interface lpDD->QueryInterface(IID_IDirect3D, (void**)&lpD3D); // Get the current driver globals.curDriver = -1; lpD3D->EnumDevices(enumDeviceFunc, &globals.curDriver); // Now create the device globals.lpDDSBack->QueryInterface(globals.DriverGUID[0], (void**)&lpD3DDev); globals.dev = lpD3DDev; lpD3D->CreateViewport(&globals.view, NULL); globals.dev->AddViewport(globals.view); globals.view->SetViewport(&viewData); CreateLightSources(lpD3D); // CreateExecuteBuffer(); InitView(lpDD, lpD3D, globals.dev, globals.view); lpD3D->Release(); return TRUE; } void CreateLightSources(LPDIRECT3D lpD3D) { LPDIRECT3DLIGHT lpD3DLight; D3DLIGHT dlLight; lpD3D->CreateLight(&lpD3DLight, NULL); memset(&dlLight, 0, sizeof(D3DLIGHT)); dlLight.dwSize = sizeof(D3DLIGHT); dlLight.dltType = D3DLIGHT_DIRECTIONAL; dlLight.dcvColor.r = dlLight.dcvColor.g = D3DVAL(0.0); dlLight.dcvColor.b = D3DVAL(0.0); dlLight.dvPosition.x = dlLight.dvPosition.y = D3DVAL(0.0); dlLight.dvPosition.z = D3DVAL(-100.0); dlLight.dvDirection.x = dlLight.dvDirection.y = D3DVAL(0.0); dlLight.dvDirection.z = D3DVAL(1.0); lpD3DLight->SetLight(&dlLight); globals.view->AddLight(lpD3DLight); } BOOL CreateExecuteBuffer() { D3DEXECUTEBUFFERDESC debDesc; DWORD dwSize; dwSize = sizeof(D3DVERTEX) * 3; dwSize += sizeof(D3DINSTRUCTION) * 2; dwSize += sizeof(D3DTRIANGLE); memset(&debDesc, 0, sizeof(D3DEXECUTEBUFFERDESC)); debDesc.dwSize = sizeof(D3DEXECUTEBUFFERDESC); debDesc.dwFlags = D3DDEB_BUFSIZE; debDesc.dwBufferSize = dwSize; LPD3DINSTRUCTION lpIns; globals.dev->CreateExecuteBuffer(&debDesc, &lpD3DExBuf, NULL); ret_val = lpD3DExBuf->Lock(&debDesc); LPBYTE lpBuf; lpBuf = (LPBYTE)debDesc.lpData; memset(lpBuf, 0, dwSize); // Now set up all the vertices and add them LPD3DVERTEX lpV; lpV = (LPD3DVERTEX)lpBuf; // Vertex 1 lpV[0].x = lpV[0].z = D3DVAL(0.0); lpV[0].y = D3DVAL(1.0); lpV[0].nx = lpV[0].nz = D3DVAL(0.0); lpV[0].ny = D3DVAL(1.0); lpV[0].tu = lpV[0].tv = D3DVAL(0.0); // Vertex 2 lpV[1].x = lpV[1].y = D3DVAL(1.0); lpV[1].z = D3DVAL(0.0); lpV[1].nx = lpV[1].ny = D3DVAL(1.0); lpV[1].nz = D3DVAL(0.0); lpV[1].tu = lpV[1].tv = D3DVAL(0.0); // Vertex 3 lpV[2].x = D3DVAL(1.0); lpV[2].y = lpV[2].z = D3DVAL(0.0); lpV[2].nx = D3DVAL(1.0); lpV[2].ny = lpV[2].nz = D3DVAL(0.0); lpV[2].tu = lpV[2].tv = D3DVAL(0.0); LPD3DTRIANGLE lpTri; LPD3DSTATE lpSt; LPBYTE lpStart; lpStart = lpBuf + (sizeof(D3DVERTEX) * 3); lpIns = (LPD3DINSTRUCTION)lpStart; lpIns->bOpcode = D3DOP_TRIANGLE; lpIns->bSize = sizeof(D3DTRIANGLE); lpIns->wCount = 1; lpTri = (LPD3DTRIANGLE) &lpIns[1]; lpTri->v1 = 0; lpTri->v2 = 1; lpTri->v3 = 2; lpTri->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE; LPD3DINSTRUCTION lpExit; lpExit = (LPD3DINSTRUCTION)&lpTri[1]; lpExit->bOpcode = D3DOP_EXIT; lpExit->bSize = 0; lpExit->wCount = 0; lpD3DExBuf->Unlock(); memset(&d3dExData, 0, sizeof(D3DEXECUTEDATA)); d3dExData.dwSize = sizeof(D3DEXECUTEDATA); d3dExData.dwInstructionOffset = (ULONG) 0; d3dExData.dwInstructionLength = (ULONG) ((char *)(&(lpExit[1])) - (char*)lpBuf); lpD3DExBuf->SetExecuteData(&d3dExData); globals.dev->BeginScene(); globals.dev->Execute(lpD3DExBuf, globals.view, D3DEXECUTE_UNCLIPPED); globals.dev->EndScene(); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_ACTIVATE: break; case WM_DESTROY: CleanupAndPostQuit(); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0L; } void CleanupAndPostQuit() { globals.bquit = TRUE; lpDD->Release(); lpD3DExBuf->Release(); } DWORD BPPToDDBD(int bpp) { switch(bpp) { case 1: return DDBD_1; case 2: return DDBD_2; case 4: return DDBD_4; case 8: return DDBD_8; case 16: return DDBD_16; case 24: return DDBD_24; case 32: return DDBD_32; default: return 0; } } BOOL RenderLoop() { HRESULT ret_val; D3DRECT rect = {0, 0, 400, 400}; globals.view->Clear(1, &rect, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER); globals.dev->BeginScene(); globals.dev->Execute(lpD3DExBuf, globals.view, D3DEXECUTE_UNCLIPPED); globals.dev->EndScene(); lpD3DExBuf->GetExecuteData(&d3dExData); globals.lpDDSFront->Flip(globals.lpDDSBack, DDFLIP_WAIT); return TRUE; } void InitGlobals() { memset(&globals, 0, sizeof(globals)); globals.bquit = FALSE; } BOOL InitView(LPDIRECTDRAW lpDD, LPDIRECT3D lpD3D, LPDIRECT3DDEVICE lpDev, LPDIRECT3DVIEWPORT lpView) { LPVOID lpBufStart, lpInsStart, lpPointer; D3DEXECUTEBUFFERDESC debDesc; size_t size; D3DTLVERTEX src_v[NUM_VERTICES]; int t[8][3] = { 0, 1, 2, }; D3DMATERIAL bmat, mat; D3DMATERIALHANDLE hBmat, hMat1; MAKE_MATRIX(lpDev, hViewRot, identity); MAKE_MATRIX(lpDev, hView, identity); MAKE_MATRIX(lpDev, hViewPos, identity); lpD3D->CreateMaterial(&lpBmat, NULL); memset(&bmat, 0, sizeof(D3DMATERIAL)); bmat.dwSize = sizeof(D3DMATERIAL); bmat.diffuse.r = bmat.diffuse.g = (D3DVALUE)0.0; bmat.diffuse.b = (D3DVALUE)1.0; bmat.ambient.r = bmat.ambient.g = (D3DVALUE)0.0; bmat.ambient.b = (D3DVALUE)1.0; // bmat.hTexture = TextureHandle[0]; bmat.dwRampSize = 1; lpBmat->SetMaterial(&bmat); lpBmat->GetHandle(lpDev, &hBmat); lpView->SetBackground(hBmat); lpD3D->CreateMaterial(&lpMat1, NULL); memset(&mat, 0, sizeof(D3DMATERIAL)); mat.dwSize = sizeof(D3DMATERIAL); mat.diffuse.r = (D3DVALUE)1.0; mat.diffuse.g = mat.diffuse.b = (D3DVALUE)0.0; mat.ambient.r = (D3DVALUE)1.0; mat.ambient.g = mat.ambient.b = (D3DVALUE)0.0; // #define SPECULAR #ifdef SPECULAR mat.specular.r = (D3DVALUE)1.0; mat.specular.g = mat.specular.b = (D3DVALUE)0.0; mat.power = (float)40.0; #else mat.specular.r = mat.specular.g = mat.specular.b = (D3DVALUE)0.0; mat.power = (float)0.0; #endif // mat.hTexture = TextureHandle[1]; mat.dwRampSize = 1; lpMat1->SetMaterial(&mat); lpMat1->GetHandle(lpDev, &hMat1); /* Setup vertices */ memset(&src_v[0], 0, sizeof(D3DVERTEX) * NUM_VERTICES); /* V 0 */ src_v[0].sx = src_v[0].sy = D3DVAL(10.0); src_v[0].sz = D3DVAL(0.1); src_v[0].rhw = D3DVAL(1.0); src_v[0].color = RGBA_MAKE(255, 0, 0, 0); src_v[0].specular = RGB_MAKE(0, 0, 255); src_v[0].tu = src_v[0].tv = D3DVAL(0.0); /* V 1 */ src_v[1].sx = D3DVAL(300.0); src_v[1].sy = D3DVAL(50.0); src_v[1].sz = D3DVAL(0.9); src_v[1].rhw = D3DVAL(1.0); src_v[1].color = RGBA_MAKE(0, 255, 0, 0); src_v[1].specular = RGB_MAKE(0, 0, 0); src_v[1].tu = src_v[1].tv = D3DVAL(1.0); /* V 2 */ src_v[2].sx = D3DVAL(150.0); src_v[2].sy = D3DVAL(180.0); src_v[2].sz = D3DVAL(0.6); src_v[2].rhw = D3DVAL(1.0); src_v[2].color = RGBA_MAKE(0, 0, 255, 0); src_v[2].specular = RGB_MAKE(0, 0, 0); src_v[2].tu = D3DVAL(0.0); src_v[2].tv = D3DVAL(1.0); /* Create an execute buffer */ size = sizeof(D3DVERTEX) * NUM_VERTICES; size += sizeof(D3DINSTRUCTION) * 6; size += sizeof(D3DSTATE) * 2; size += sizeof(D3DPROCESSVERTICES); size += sizeof(D3DTRIANGLE) * 1; memset(&debDesc, 0, sizeof(D3DEXECUTEBUFFERDESC)); debDesc.dwSize = sizeof(D3DEXECUTEBUFFERDESC); debDesc.dwFlags = D3DDEB_BUFSIZE; debDesc.dwBufferSize = size; lpDev->CreateExecuteBuffer(&debDesc, &lpD3DExBuf, NULL); lpD3DExBuf->Lock(&debDesc); lpBufStart = debDesc.lpData; memset(lpBufStart, 0, size); lpPointer = lpBufStart; /* Copy vertices to execute buffer */ VERTEX_DATA(&src_v[0], NUM_VERTICES, lpPointer); /* Setup instructions in execute buffer */ lpInsStart = lpPointer; OP_STATE_TRANSFORM(1, lpPointer); STATE_DATA(D3DTRANSFORMSTATE_VIEW, hView, lpPointer); OP_STATE_LIGHT(1, lpPointer); STATE_DATA(D3DLIGHTSTATE_MATERIAL, hMat1, lpPointer); OP_PROCESS_VERTICES(1, lpPointer); PROCESSVERTICES_DATA(D3DPROCESSVERTICES_COPY | D3DPROCESSVERTICES_UPDATEEXTENTS, 0, NUM_VERTICES, lpPointer); if (QWORD_ALIGNED(lpPointer)) OP_NOP(lpPointer); OP_TRIANGLE_LIST(1, lpPointer); ((LPD3DTRIANGLE)lpPointer)->v1 = 0; ((LPD3DTRIANGLE)lpPointer)->v2 = 1; ((LPD3DTRIANGLE)lpPointer)->v3 = 2; ((LPD3DTRIANGLE)lpPointer)->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE; lpPointer = ((char*)lpPointer) + sizeof(D3DTRIANGLE); OP_EXIT(lpPointer); /* Setup the execute data */ lpD3DExBuf->Unlock(); memset(&d3dExData, 0, sizeof(D3DEXECUTEDATA)); d3dExData.dwSize = sizeof(D3DEXECUTEDATA); d3dExData.dwVertexCount = NUM_VERTICES; d3dExData.dwInstructionOffset = (ULONG) ((char *)lpInsStart - (char *)lpBufStart); d3dExData.dwInstructionLength = (ULONG) ((char *)lpPointer - (char *)lpInsStart); lpD3DExBuf->SetExecuteData(&d3dExData); return TRUE; } BOOL TickScene(LPDIRECT3DDEVICE lpDev, LPDIRECT3DVIEWPORT lpView) { static int dir = 1; if (viewpos._43 < D3DVAL(4.0)) dir = 0; if (viewpos._43 > D3DVAL(12.0)) dir = 1; if (dir) viewpos._43 -= D3DVAL(0.4); else viewpos._43 += D3DVAL(0.4); if (lpDev->SetMatrix(hViewPos, &viewpos) != D3D_OK) return FALSE; return TRUE; }