//----------------------------------------------------------------------------- // File: Textures.cpp // // Desc: Better than just lights and materials, 3D objects look much more // convincing when texture-mapped. Textures can be thought of as a sort // of wallpaper, that is shrinkwrapped to fit a texture. Textures are // typically loaded from image files, and D3DX provides a utility to // function to do this for us. Like a vertex buffer, textures have // Lock() and Unlock() functions to access (read or write) the image // data. Textures have a width, height, miplevel, and pixel format. The // miplevel is for "mipmapped" textures, an advanced performance- // enhancing feature which uses lower resolutions of the texture for // objects in the distance where detail is less noticeable. The pixel // format determines how the colors are stored in a texel. The most // common formats are the 16-bit R5G6B5 format (5 bits of red, 6-bits of // green and 5 bits of blue) and the 32-bit A8R8G8B8 format (8 bits each // of alpha, red, green, and blue). // // Textures are associated with geometry through texture coordinates. // Each vertex has one or more sets of texture coordinates, which are // named tu and tv and range from 0.0 to 1.0. Texture coordinates can be // supplied by the geometry, or can be automatically generated using // Direct3D texture coordinate generation (which is an advanced feature). // // Copyright (c) 2000 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #include //#include #include "gtarcball.h" //----------------------------------------------------------------------------- // Global variables //----------------------------------------------------------------------------- LPDIRECT3D8 g_pD3D = NULL; // Used to create the D3DDevice LPDIRECT3DDEVICE8 g_pd3dDevice = NULL; // Our rendering device LPDIRECT3DVERTEXBUFFER8 g_pVB = NULL; // Buffer to hold vertices LPDIRECT3DTEXTURE8 g_pTexture = NULL; // Our texture GTArcBall *g_pArcBall = NULL; // Our Arc Ball // A structure for our custom vertex type. We added texture coordinates struct CUSTOMVERTEX { D3DXVECTOR3 position; // The position D3DXVECTOR3 normal; // The normal D3DCOLOR color; // The color FLOAT tu, tv; // The texture coordinates }; // Our custom FVF, which describes our custom vertex structure #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1) VOID SetupLights() { // Set up a material. The material here just has the diffuse and ambient // colors set to yellow. Note that only one material can be used at a time. D3DMATERIAL8 mtrl; ZeroMemory( &mtrl, sizeof(D3DMATERIAL8) ); mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f; mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f; mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f; mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f; g_pd3dDevice->SetMaterial( &mtrl ); // Set up a white, directional light, with an oscillating direction. // Note that many lights may be active at a time (but each one slows down // the rendering of our scene). However, here we are just using one. Also, // we need to set the D3DRS_LIGHTING renderstate to enable lighting D3DXVECTOR3 vecDir; D3DLIGHT8 light; ZeroMemory( &light, sizeof(D3DLIGHT8) ); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse.r = 1.0f; light.Diffuse.g = 1.0f; light.Diffuse.b = 1.0f; vecDir = D3DXVECTOR3(0.f, 0.f, 1.f); D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir ); light.Range = 1000.0f; g_pd3dDevice->SetLight( 0, &light ); g_pd3dDevice->LightEnable( 0, TRUE ); g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); // Finally, turn on some ambient light. g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00202020 ); } //----------------------------------------------------------------------------- // Name: InitD3D() // Desc: Initializes Direct3D //----------------------------------------------------------------------------- HRESULT InitD3D( HWND hWnd ) { // Create the D3D object. if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) ) return E_FAIL; // Get the current desktop display mode, so we can set up a back // buffer of the same format D3DDISPLAYMODE d3ddm; if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) ) return E_FAIL; // Set up the structure used to create the D3DDevice. Since we are now // using more complex geometry, we will create a device with a zbuffer. D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) ); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = d3ddm.Format; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // Create the D3DDevice if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) ) { return E_FAIL; } // Turn off culling g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); // Turn on the zbuffer g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); //Sets up the scene lighting SetupLights(); return S_OK; } //----------------------------------------------------------------------------- // Name: InitGeometry() // Desc: Create the textures and vertex buffers //----------------------------------------------------------------------------- HRESULT InitGeometry() { // Use D3DX to create a texture from a file based image if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "FaFiSketch.bmp", &g_pTexture ) ) ) return E_FAIL; // Create the vertex buffer. if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB ) ) ) { return E_FAIL; } // Fill the vertex buffer. We are setting the tu and tv texture // coordinates, which range from 0.0 to 1.0 CUSTOMVERTEX* pVertices; if( FAILED( g_pVB->Lock( 0, 0, (BYTE**)&pVertices, 0 ) ) ) return E_FAIL; pVertices[0].position = D3DXVECTOR3(-1.f, -1.f, 0.f); pVertices[0].normal = D3DXVECTOR3(0.f, 0.f, -1.f); pVertices[0].color = 0xffffffff; pVertices[0].tu = 0.f; pVertices[0].tv = 1.f; pVertices[1].position = D3DXVECTOR3(-1.f, 1.f, 0.f); pVertices[1].normal = D3DXVECTOR3(0.f, 0.f, -1.f); pVertices[1].color = D3DXCOLOR(0.5f, 0.8f, 0.5f, 1.f); pVertices[1].tu = 0.f; pVertices[1].tv = 0.f; pVertices[2].position = D3DXVECTOR3(1.f, -1.f, 0.f); pVertices[2].normal = D3DXVECTOR3(0.f, 0.f, -1.f); pVertices[2].color = 0xffffffff; pVertices[2].tu = 1.f; pVertices[2].tv = 1.f; pVertices[3].position = D3DXVECTOR3(1.f, 1.f, 0.f); pVertices[3].normal = D3DXVECTOR3(0.f, 0.f, -1.f); pVertices[3].color = D3DXCOLOR(0.8f, 0.5f, 0.5f, 1.f); pVertices[3].tu = 1.f; pVertices[3].tv = 0.f; g_pVB->Unlock(); return S_OK; } //----------------------------------------------------------------------------- // Name: Cleanup() // Desc: Releases all previously initialized objects //----------------------------------------------------------------------------- VOID Cleanup() { if( g_pTexture != NULL ) g_pTexture->Release(); if( g_pVB != NULL ) g_pVB->Release(); if( g_pd3dDevice != NULL ) g_pd3dDevice->Release(); if( g_pD3D != NULL ) g_pD3D->Release(); } //----------------------------------------------------------------------------- // Name: SetupMatrices() // Desc: Sets up the world, view, and projection transform matrices. //----------------------------------------------------------------------------- VOID SetupMatrices() { // For our world matrix, we will just leave it as the identity D3DXMATRIX matWorld; D3DXMatrixIdentity( &matWorld ); g_pArcBall->GetMatrix(&matWorld); g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); // Set up our view matrix. A view matrix can be defined given an eye point, // a point to lookat, and a direction for which way is up. Here, we set the // eye five units back along the z-axis and up three units, look at the // origin, and define "up" to be in the y-direction. D3DXMATRIX matView; D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-5.0f ), &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); // For the projection matrix, we set up a perspective transform (which // transforms geometry from 3D view space to 2D viewport space, with // a perspective divide making objects smaller in the distance). To build // a perpsective transform, we need the field of view (1/4 pi is common), // the aspect ratio, and the near and far clipping planes (which define at // what distances geometry should be no longer be rendered). D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); } //----------------------------------------------------------------------------- // Name: Render() // Desc: Draws the scene //----------------------------------------------------------------------------- VOID Render() { // Clear the backbuffer and the zbuffer g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 ); // Begin the scene g_pd3dDevice->BeginScene(); // Setup the world, view, and projection matrices SetupMatrices(); // Setup our texture. Using textures introduces the texture stage states, // which govern how textures get blended together (in the case of multiple // textures) and lighting information. In this case, we are modulating // (blending) our texture with the diffuse color of the vertices. g_pd3dDevice->SetTexture( 0, g_pTexture ); // Render the vertex buffer contents g_pd3dDevice->SetStreamSource( 0, g_pVB, sizeof(CUSTOMVERTEX) ); g_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); // End the scene g_pd3dDevice->EndScene(); // Present the backbuffer contents to the display g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); } //----------------------------------------------------------------------------- // Name: MsgProc() // Desc: The window's message handler //----------------------------------------------------------------------------- LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DESTROY: PostQuitMessage( 0 ); return 0; case WM_PAINT: Render(); ValidateRect( hWnd, NULL ); return 0; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: if (g_pArcBall) { D3DXMATRIX worldTransform; g_pArcBall->BeginDrag(LOWORD(lParam), HIWORD(lParam)); Render(); } break; case WM_MOUSEMOVE: if (g_pArcBall) { D3DXMATRIX worldTransform; g_pArcBall->Mouse(LOWORD(lParam), HIWORD(lParam)); Render(); } break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: if (g_pArcBall) { D3DXMATRIX worldTransform; g_pArcBall->EndDrag(); Render(); } break; } return DefWindowProc( hWnd, msg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: WinMain() // Desc: The application's entry point //----------------------------------------------------------------------------- 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, "D3D Tutorial", NULL }; RegisterClassEx( &wc ); // Create the application's window HWND hWnd = CreateWindow( "D3D Tutorial", "iMAGIS D3D8.0", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, GetDesktopWindow(), NULL, wc.hInstance, NULL ); // Create GTArcBall g_pArcBall = new GTArcBall; g_pArcBall->SetWindow(500, 500); // Initialize Direct3D if( SUCCEEDED( InitD3D( hWnd ) ) ) { // Create the scene geometry if( SUCCEEDED( InitGeometry() ) ) { // Show the window ShowWindow( hWnd, SW_SHOWDEFAULT ); UpdateWindow( hWnd ); // Enter the message loop MSG msg; ZeroMemory( &msg, sizeof(msg) ); while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } } // Clean up everything and exit the app Cleanup(); UnregisterClass( "D3D Tutorial", wc.hInstance ); return 0; }