Advanced 3D Game Programming with DirectX - phần 7 ppsx

71 354 0
Advanced 3D Game Programming with DirectX - phần 7 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

427 struct mtVertex { point3 m_loc; // Position point3 m_norm; // Normal ulong m_diff; // Color (Diffuse) ulong m_spec; // Color (Specular) texCoord2 m_tex1; texCoord3 m_tex2; static ulong m_fvfFlags; }; ulong mtVertex::m_fvfFlags = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEXCOORDSIZE2(0) | // set 0 is 2-dimensional D3DFVF_TEXCOORDSIZE3(1); // set 1 is 3-dimensional Primitive Types When drawing primitives using the D3D device, you need to inform the device what type of primitive you would like it to draw. Currently, Direct3D can draw three types of primitives: points, lines, and triangles. D3DPT_POINTLIST The data being handed to the driver is a list of points. The Direct3D device draws one pixel for each vertex handed to it. D3DPT_LINELIST The data being handed to the driver is a list of lines. The number of vertices provided to the device must be even. If n vertices are passed in, n/2 lines are drawn. For example, the third line D3D draws is from the fourth to the fifth vertex. 428 D3DPT_LINESTRIP Direct3D draws a continuous strip of lines. Each vertex besides the first becomes the endpoint of a line, with a beginning of the vertex before it. D3DPT_TRIANGLELIST Direct3D draws a list of distinct triangles. Each three vertices are rendered as a triangle. Of course, the number of vertices supplied to the DrawPrim functions must be a multiple of three. D3DPT_TRIANGLESTRIP Direct3D draws a triangle strip, each vertex after the first two defining the third point of a triangle. See Chapter 5 for a discussion of triangle strips. D3DPT_TRIANGLEFAN Direct3D draws a triangle fan, each vertex after the first two defining the third point of a triangle. See Chapter 5 for a discussion of triangle fans. The DrawPrimitive Functions There are four total functions to draw primitives for us. They are all very similar and once you've mastered one, you've pretty much mastered them all. Let's take a look at each of them. DrawPrimitive DrawPrimitive is the most basic primitive drawing function. It simply takes the current vertex buffer that is attached to a rendering stream and renders it. It doesn't use any indexed information, and therefore isn't as efficient for drawing triangle meshes as DrawIndexedPrimitive for most applications. The one exception is drawing triangle strips and fans. On some cards (such as the GeForce), the cache coherency goes way up and using DrawPrimitive is actually faster than DrawIndexedPrimitive. HRESULT DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount ); PrimitiveType The type of primitive you would like the device to draw for you. StartVertex Index of the first vertex you want to load; usually set this to 0. PrimitiveCount The number of primitives to render. 429 DrawPrimitiveUP DrawPrimitiveUP is very similar to the regular DrawPrimitive except that it does not require you to package your vertices in buffers. Instead it takes a pointer to vertex data that exists somewhere in system memory and uses that as the rendering stream. UP, by the way, stands for user pointer. The function has this definition: HRESULT DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride ); PrimitiveType The type of primitive you would like the device to draw for you. PrimitiveCount The number of primitives you want to render. pVertexStreamZeroData A pointer to the vertex data that the device will use as rendering stream 0. VertexStreamZeroStride The stride between each vertex, in bytes. Usually this will be 0. DrawIndexedPrimitive DrawIndexedPrimitive accepts two buffers: an array of vertices and an array of indices. The entire list of vertices is transformed, and then the primitives are drawn using the list of indices. Warning Each time DrawIndexedPrimitive is called, the entire list of vertices is transformed, regardless of whether or not they actually end up being used in the list of indices. Thus, for efficiency reasons, DrawIndexedPrimitive shouldn't be called multiple times for the same buffer. If this type of behavior is required, consider putting the vertices in a vertex buffer and transforming them just once using the ProcessVertices method on the vertex buffer interface. HRESULT DrawIndexedPrimitive( D3DPRIMITIVETYPE Type, INT BaseVertexIndex, // Note this new parameter UINT MinIndex, UINT NumVertices, 430 UINT StartIndex, UINT PrimitiveCount ); Type The type of primitive you would like the device to draw for you. BaseVertexIndex Offset from the start of the index buffer to the first vertex index. MinIndex The lowest vertex that will be used for this call. NumVertices The number of vertices that will be used for this call. StartIndex The location in the array to start reading vertices PrimitiveCount The number of primitives that will be rendered. DrawIndexedPrimitiveUP DrawIndexedPrimitiveUP is to DrawIndexedPrimitive what DrawPrimitiveUP was to DrawPrimitive. Basically it operates in exactly the same way as DrawIndexedPrimitive, except that it uses vertex data at a particular memory location instead of requiring it to be packaged into a vertex buffer and attached to a rendering stream. It has this definition: HRESULT DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertexIndices, UINT PrimitiveCount, CONST void* pIndexData, D3DFORMAT IndexDataFormat, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride ); PrimitiveType The type of primitive you would like the device to draw for you. 431 MinVertexIndex The minimum vertex index that will be used for a vertex in this call. NumVertexIndices The number of vertex indices to be used for this call. PrimitiveCount The number of primitives that you want to render. pIndexData A pointer to the index data. IndexDataFormat This can be set to either D3DFMT_INDEX16 or D3DFMT_INDEX32, depending on whether you are using 16- or 32-bit indices. You will usually use 16-bit indices. pVertexStreamZeroData A pointer to the vertex data. VertexStreamZeroStride The stride (distance between each vertex, in bytes) for the vertices; this will almost always be 0. Adding Direct3D to the Graphics Layer Now that you know enough Direct3D to get up and running, let's add Direct3D support to the graphics layer in the game library. I'll be adding more than initialization code this time around, as there are some convenience functions to help and also new native matrix types. Direct3D Initialization Getting Direct3D initialized used to be a tricky process, but these days it is much more straightforward, conceptually. In fact, in Chapter 2 , I showed you almost everything you need to know, although I'll admit I glossed over the more complex 3D topics a little. Don't worry; I'll cover them here. For the updates there will be some changes to the class system in Chapter 2 . There are a few new steps to perform, such as initializing view and projection matrices, and so on. The particular feature set an application would like may not necessarily be the same for all applications. For example, some apps may choose not to use a z-buffer to avoid the added memory overhead on low-memory cards. To facilitate the various options a user application might like, the graphics layer's Direct3D initialization call accepts a set of flags that modify the path the initialization steps go through. The flags are: 432 Table 8.7: The set of graphics layer flags GLF_ZBUFFER The application is requesting that a z-buffer is created. GLF_HIRESZBUFFER The application is requesting that a high-resolution (24- or 32-bit) z- buffer is created. GLF_STENCIL The application is requesting stencil bits in addition to depth information in the z-buffer. GLF_FORCEREFERENCE The application is demanding a reference device. If one of these cannot be created, the initialization phase fails. GLF_FORCEHARDWARE The application is demanding a hardware (HAL) device. If one of these cannot be created, the initialization phase fails. GLF_FORCESOFTWARE The application is demanding a software device. If one of these cannot be created, the initialization phase fails. GLF_FORCE16BIT The application is forcing 16-bit rendering. Let's take a step-by-step look at how Direct3D is initialized within the graphics layer. Some of this you have already seen in Chapter 2 , but for consistency I'll show you it again since it is pretty relevant. Acquire an IDirect3D9 Interface Getting an IDirect3D9 interface pointer is the simplest task to do. All you need to do is ask the Direct 3D interface pointer. This is done using Direct3DCreate9. For a discussion on how COM works, see Chapter 1 . Listing 8.10: Acquiring a Direct3D9 interface pointer // Create the Direct3D interface m_pD3D = Direct3DCreate9( D3D_SDK_VERSION ); if( !m_pD3D ) 433 { throw cGameError( "Could not create IDirect3D9" ); } Fill In the Presentation Parameters I'm going to run through this quickly because you have seen a lot of it before. However, it has changed somewhat, so pay attention to the updates. If you need a refresher, refer back to Chapter 2 . The first part of the D3DPRESENT_PARAMETERS structure deals with the format of the back buffer. Check out the following code: // Structure to hold the creation parameters for the device D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof( d3dpp ) ); // The width and height for the initial back buffer d3dpp.BackBufferWidth = width; d3dpp.BackBufferHeight = height; // Set the flags for the bit depth - only supports 16-, 24-, and 32-bit formats if( bpp == 16 ) d3dpp.BackBufferFormat = D3DFMT_R5G6B5; else if( bpp == 24 ) d3dpp.BackBufferFormat = D3DFMT_R8G8B8; else if( bpp == 32 ) d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; else { OutputDebugString( "Invalid surface format - defaulting to 32bit" ); d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; } 434 // Only have one back buffer associated with the primary surface d3dpp.BackBufferCount = 1; // No multisampling d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; // Copy the back buffer to the primary surface normally d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; // The handle to the window to render in to d3dpp.hDeviceWindow = m_hWnd; // Fullscreen operation d3dpp.Windowed = FALSE; Notice how the bit depth format is set with flags by comparing the bit depth passed as an integer. That code is quite straightforward. Now check out the following code, which implements some of the flags that I was talking about previously to set up the depth and stencil buffer. // If a depth buffer was requested if( flags & (GLF_ZBUFFER|GLF_HIRESZBUFFER) ) { // Tell Direct3D we want a depth buffer d3dpp.EnableAutoDepthStencil = TRUE; if( flags & (GLF_HIRESZBUFFER) ) { if( flags & (GLF_STENCIL) ) // 24-bit depth buffer and 8-bit stencil d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; else // 32-bit depth buffer and no stencil d3dpp.AutoDepthStencilFormat = D3DFMT_D32; } else { if( flags & (GLF_STENCIL) ) 435 // 15-bit depth buffer and 1-bit stencil d3dpp.AutoDepthStencilFormat = D3DFMT_D15S1; else // 16-bit depth buffer and no stencil d3dpp.AutoDepthStencilFormat = D3DFMT_D16; } } else { // No depth buffer or stencil d3dpp.EnableAutoDepthStencil = FALSE; } That is also pretty straightforward, so I'll let the code speak for itself. Finally, just before I actually create the device there is another snippet of code that I want to show that has changed from Chapter 2 : // Use the default refresh rate d3dpp.FullScreen_RefreshRateInHz= D3DPRESENT_RATE_DEFAULT; // Update the screen as soon as possible (don't wait for vsync) d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Hardware device by default D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL; if( flags & (GLF_FORCEHARDWARE) ) DeviceType = D3DDEVTYPE_HAL; else if( flags & (GLF_FORCEREFERENCE) ) DeviceType = D3DDEVTYPE_REF; Notice how you now have the option of forcing a certain type of device to be created by passing a flag to the InitD3DFullScreen. After all of that structure filling it is simple to create the device with a call to, you guessed it, CreateDevice. The function call looks like this: // Create the device using hardware acceleration if available r = m_pD3D->CreateDevice( Ordinal, DeviceType, m_hWnd, 436 D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_pDevice ); if( FAILED(r)) { throw cGameError( "Could not create IDirect3DDevice9" ); } And that's it—you now have a fully 3D capable device set up and ready to render. If you have had previous experience with DirectX, particularly prior to version 5.0, you will be trying to pick your jaw off the floor out of surprise at how easy it is to create. In this last section (about two pages) is everything that used to take over a thousand lines of code to set up. Just smile and nod. Create a Viewport and Projection Matrix Creating the viewport is one of the more monotonous tasks in Direct3D initialization. The graphics layer is assuming that all applications will want the entire viewport as visible. If this is not the case, user applications will have to create a viewport themselves. The code that the graphics layer uses to set up the viewport is straightforward. The z-range from 0.0 to 1.0 is used, and the bounds of the screen are used as the viewport boundaries. Listing 8.11: Viewport creation code void cGraphicsLayer::MakeViewport() { HRESULT hr; if( !m_pDevice ) { DP("[cGraphicsLayer::MakeViewport]: no device\n"); return; } DWORD dwRenderWidth = m_rcScreenRect.right; DWORD dwRenderHeight = m_rcScreenRect.bottom; D3DVIEWPORT9 vp={0,0, dwRenderWidth, dwRenderHeight, 0.0f, 1.0f }; [...]... pDevice = Graphics( )-> GetDevice(); pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); pDevice->SetRenderState(D3DRS_LIGHTING, TRUE); pDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE); pDevice->SetRenderState(D3DRS_SPECULARENABLE, TRUE); pDevice->SetRenderState(D3DRS_AMBIENT, 0x404040); /** * initialize the camera */ Graphics( )-> SetViewMatrix( matrix4::Identity ); /** * Create a model with the given filename,... /** * Here is where we actually draw our object */ m_pModel->Draw( mat ); Graphics( )-> EndScene(); /** * flip the buffer */ Graphics( )-> Flip(); } } 454 Chapter 9: Advanced 3D Programming This is my favorite chapter in the book Nothing but sweet, pure, uncut 3D graphics We're going to take a whirlwind tour of some more advanced topics in 3D programming Among other things we'll cover inverse kinematics,... files, so you can mess around with it if you want to see what other models look like I highly recommend the rabbit Listing 8.15: D3DSample.cpp /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Title: D3DSample.cpp * Desc: An extremely simple D3D app, using the framework... normal or texture information Listing 8.13 has the o3d file for a simple tetrahedron model Listing 8.13: Tetrahedron model Tetrahedron 3 1 4 4 -1 .0 -1 .0 -1 .0 1.0 1.0 -1 .0 -1 .0 1.0 1.0 1.0 -1 .0 1.0 2 3 4 1 4 3 1 3 2 1 2 4 The first line of the file is the header It has five fields, separated by spaces They are, in order: The name for the object (spaces within the name are not allowed) The number of fields... ); m_pModel->Scale( 1.f / m_pModel->GenRadius() ); InitLights(); } void cD3DSampleApp::InitLights() { LPDIRECT3DDEVICE9 pDevice = Graphics( )-> GetDevice(); sLight light; // Light 0 light = sLight::Directional( point3(0 ,-4 ,2).Normalized(), 0.5f * color3::White + 0.2f * color3::Red, 0.7f * color3::White + 0.2f * color3::Red, 0.2f * color3::White + 0.2f * color3::Red); // Set the light pDevice->SetLight(... point3 (-3 ,3,5).Normalized(), 0.5f * color3::White + 0.2f * color3::Blue, 0.7f * color3::White + 0.2f * color3::Blue, 0.2f * color3::White + 0.2f * color3::Blue); // Set the light pDevice->SetLight( 2, &light ); pDevice->LightEnable(2, TRUE); sMaterial mat( 16.f, color3(0.5f,0.5f,0.5f), color3(0.7f,0.7f,0.7f), color3(0.1f,0.1f,0.1f) ); pDevice->SetMaterial(&mat); } void cD3DSampleApp::DoFrame( float timeDelta ) { /** * update... cos(m_fov/2)/sin(m_fov/2) ); float Q = m_far / ( m_far - m_near ); ZeroMemory( &mat, sizeof(D3DMATRIX) ); mat._11 = w; 4 37 mat._22 = h; mat._33 = Q; mat._34 = 1.0f; mat._43 = -Q*m_near; m_pDevice->SetTransform( D3DTS_PROJECTION, &mat ); return resAllGood; } Further Additions to the GameLib To handle the addition of Direct3D to the GameLib, some changes needed to be made The cGraphicsLayer class got a host of new... GetWorldMatrix( Gets the currently set world matrix from the D3D device matrix4* pMat ); void SetWorldMatrix( Sets the current world matrix to the supplied matrix const matrix4& mat ); LPDIRECT3DDEVICE9 Gets the Direct3D device interface GetDevice(); LPDIRECT3D9 GetD3D(); void Clear( bool bClearFrame, Gets the Direct3D interface Clears the back buffer, and the z-buffer if needed, to the provided color and value... complaints about D3D's complexity is the Direct3DX library (D3DX for short) It attempts to take care of most of the grunt work by providing things like macros, mathematical functions, COM objects, and many other useful bits and pieces that makes DirectX a nicer place to live I'm not going to give you an exhaustive look at the D3DX library, since it so large, but I really suggest you take a look at DirectX 9.0... cD3DSampleApp : public cApplication { 449 public: string m_filename; cModel* m_pModel; void InitLights(); //========== cApplication virtual void DoFrame( float timeDelta ); virtual void SceneInit(); virtual void SceneEnd() { delete m_pModel; } cD3DSampleApp() : cApplication() { m_title = string( "D3DSample - Objects Spinning in D3D" ); m_pModel = NULL; m_filename = " \\BIN\\Media\\Cow.o3d"; . mtVertex::m_fvfFlags = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEXCOORDSIZE2(0) | // set 0 is 2-dimensional D3DFVF_TEXCOORDSIZE3(1); // set 1 is 3-dimensional . Direct3D9 interface pointer // Create the Direct3D interface m_pD3D = Direct3DCreate9( D3D_SDK_VERSION ); if( !m_pD3D ) 433 { throw cGameError( "Could not create IDirect3D9". for the bit depth - only supports 1 6-, 2 4-, and 32-bit formats if( bpp == 16 ) d3dpp.BackBufferFormat = D3DFMT_R5G6B5; else if( bpp == 24 ) d3dpp.BackBufferFormat = D3DFMT_R8G8B8; else

Ngày đăng: 08/08/2014, 23:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan