用DirectX绘制使用纹理的立方体

下面将学习DirectX的3D渲染基础部分,但不会对3D数学或图形理论有太详细的介绍。 首先要了解DirectX中的坐标系,要记住的是DirectX采用左手坐标系。

在Direct3D中渲染一个场景,涉及到的三维变换有:世界变换、视图变换、投影变换。

世界变换:三维变换的第一步就是将模型的顶点的局部坐标变换到所有对象都共享的一个坐标系中,也就是从模型空间向世界空间转换。变换到的新的坐标系称为世界坐标系。世界坐标系中的每一个顶点的坐标都以世界坐标系来表示。根据需要,应该将物体进行缩放、旋转、平移后放置到我们想要的位置。

视图变换:又称为观察变换,即如照相机一般,表示用户在屏幕上看到的内容。我们在世界坐标系中选择一个观察点,世界坐标系将被重新定位到这个观察点的视野中,也就是从世界坐标空间向观察空间的转换。需要注意的是,观察变换实际上是坐标系的变换。但是,Direct3D在处理时是对物体顶点进行矩阵变换,所以观察矩阵需要执行与视点变换相反的变换。例如,我们希望视点沿着z轴负方向移动5个单位,那么相对的就是顶点向z轴正方向移动5个单位。

投影变换:这是最后一步,在这里将视图变换的结果投影到屏幕上形成2D图像。视景体(field of view, FOV)用于定义在某个特定的位置能够看到的物体的范围,又称为视锥体或观察体。视景体在三维空间由6个面组成,以你的眼睛为视点向前看,依次是投影平面、近平面、远平面。典型的投影变换包括缩放和透视投影。投影变换将视景体变换为一个立方体。因为视景体的近端比远端小,所以会在投影平面上产生了离摄像机近的物体就被放大的效果。

除此之外,还要理解一些常见的术语,比如顶点缓存、索引缓存、深度缓存、模板缓存、交换链、多重采样、着色器、HLSL,下面将会简要介绍。

DirectX是一种基于COM(Component Object Model,组件对象模型)的系统,它既不属于驱动程序层,也不属于应用层。DirectX的主要设计目标是提供某种设备的独立性的同时获取很高的速度。COM使Direct3D独立于编程语言和具有向下兼容性,通常把COM对象作为一个接口,可以把它当做达到某种目的的C++类来使用,COM接口都具有前缀大写字母I表示,比如接口ID3DTexture2D。在DirectX的体系结构中,处于DirectX下层的是HAL(Hardware Abstract Layer,硬件抽象层)与HEL(Hardware Emulation Layer,硬件模拟层)。HAL负责检测本机的硬件功能,并以一种独立于设备的方式提供这些功能。HEL负责提供DirectX功能中本机硬件不支持部分的软件模拟实现。从DirectX 8.0起,DirectX就不再提供硬件模拟层HEL模块了,最新的DirectX 11要求硬件必须实现全部DirectX 11的特性,因此现在的DirectX是严重依赖于硬件的。Direct3D是一种低层的图形API,它能让我们利用三维硬件加速来渲染三维世界,可以被看成应用程序和图形设备之间的中介。

这里必须提到的是,DirectX 9中采用固定图像渲染管线,而DirectX 10与DirectX 11都放弃了固定的渲染管线,可以通过着色器代码改变渲染管线的过程。 在DirectX 9中,提供了两个独立的着色器:顶点着色器与像素着色器,在硬件中有各自的实现,不能通用。而从DirectX 10起,Shader Model 4.0提出了通用着色器的概念,即硬件上仅仅实现一种着色器,每一个着色阶段都利用通用着色器实现核心功能,每个特定的着色阶段都提供各自的额外功能。在通用着色器中,无论是常数、纹理或是缓存都被视为同样的资源直接或者通过采样器传递给着色器代码进行处理,HLSL便是DirectX使用的着色语言,类似C语言。

顶点着色器:顶点着色阶段将顶点作为输入集,负责处理“逐顶点”的操作,如坐标变换、蒙皮、动画以及顶点光照。顶点着色器的工作是处理一个独立的输入顶点并产生一个独立的输出顶点。顶点着色阶段在渲染管线中必须激活,即便没有对顶点做任何修改,也必须提供一个“不做任何修改”的顶点着色器才能保证渲染管线的正常工作。

几何着色器:这时由Direct3D 10引入的新的着色阶段,用来在已知的输入顶点的基础上生成新的输入顶点。和顶点着色器不同的是,顶点着色器仅仅处理独立的顶点,而几何着色器的输入则是一个图元(两个顶点的线段、三个顶点的三角形、一个顶点的独立点等),也可以将顶点的邻接点作为输入。

像素着色器:可以实现丰富的着色技术如逐像素光照和后期处理等。像素着色器可以通过常量、贴图、插值以及其他数据来产生逐像素的输出值。光栅处理器对每一个图元中的每一个像素都会执行一次像素着色器,但是也可以通过程序设置跳过像素着色器的执行。

计算着色器:是Direct3D 11引入的新着色器,独立于渲染管线之外,和其他着色器一样使用着色器语言实现。计算着色器提供了利用GPU中大量并行处理器进行高速通用计算的能力。

Direct3D管线中用到的所有资源可分为缓存(buffer)资源和纹理(texture)资源。缓存资源是指一组指定类型的数据的集合,分为顶点缓存、索引缓存、常量缓存。顶点缓存用于存储顶点数据,包括顶点的位置、法线、纹理坐标。纹理资源是一种结构,用于存储纹理。Direct3D有三种纹理类型:一维、二维和三维纹理。一维纹理指只在某一方向上变化,相当于高度为1的二维纹理;二维纹理是指纹理在相互垂直的两个方向上变化,用u表示横向纹理,v表示纵向纹理;三维纹理,其纹理单元为三维纹理体,每个纹理单元用u、v、w向量表示。多重纹理是指事先制定一系列分辨率逐渐减小的纹理图像,纹理采用mipmap方式自动生成,即每个层次的纹理细节(LOD)为小于上一级纹理平方根大小,Direct3D会根据所映射的物体尺寸自动选择使用哪个分辨率的纹理图。

Direct3D维护着两个纹理缓存,前台缓存(Front Buffer)和后台缓存(Back Buffer)。前台缓存存储着当前显示在屏幕上的图像数据,下一帧的动画则被绘制在后台缓存上。为了避免动画的闪动,当帧的整个场景在后台缓存中绘制好后,后台缓存和前台缓存就互相交换,这样后台缓存中的内容才被完整地呈现在屏幕上。前台缓存与后台缓存组成了交换链

深度缓存是一个只包含特定像素深度信息而不包含图像数据的纹理。深度值的范围为[0,1],0表示能被观察者看到的最近的位置,1表示能被观察者看到的最远的位置。为了理解这个概念,设想这样一个场景,一个圆圈环绕着一个立方体,显然圆圈会遮挡一部分的立方体,该如何绘制场景了?如果采用传统的画家算法,先画什么,再画什么就不行了。Direct3D在像素级别上提供了一个很好的解决方案,它使用一种叫深度缓存,又叫z-buffer的技术。深度缓存保存着每个像素的深度值,也叫z值,在绘制场景时按深度值绘制就可以了。

用像素矩阵表示的图像通常会出现“阶梯效应”,比如一条线段的边缘产生了锯齿,发生走样。这里要采用反走样技术,通过采集并使用线段中的点周围的一些像素,减少图像的阶梯效应。Direct3D支持一种称为多重采样的反走样技术,它使用目标像素周围的一些点来生成该像素点的最终颜色。由于这个过程使用了多个像素采样,所以叫多重采样。

实际的模型可以分为很多个三角形,这些三角形有大量的重复顶点和重复边。如果在顶点缓存中重复保存这些顶点信息,那么显卡将耗费比实际模型体积大得多的空间来保存模型信息。这里必须引入索引缓存的概念,它不能脱离顶点缓存而独立存在。我们需要建立一个顶点缓存,比如一个立方体,顶点缓存中只需要保存8个顶点的信息。除此之外,还需要建立一个缓存来确定每一个顶点对应顶点缓存中的哪一个顶点,这就是索引缓存

模板是一块特殊的缓存区,称为模板缓存(Stencil Buffer)。模板缓存的作用是限定哪些像素将被绘制到屏幕上而哪些不会。因为模板缓存的容量很小,因此在实际的硬件实现中,并没有一个独立的模板缓存,而是将深度缓存和模板缓存合并成一个深度模板缓存(Depth Stencil Buffer),其中每一个像素的前24位用作深度缓存,而后8位用于模板缓存。

Direct3D使用由用户自定义的顶点格式,下面是我们将要使用的结构VERTEX。前三个成员变量是顶点的位置,而tu和tv用于表达使用的纹理的坐标,可以使用tu=0.0、tv=0.0指定纹理左上角,tu=1.0、tv=1.0指定纹理右下角。

struct VERTEX
{
    float x, y, z;
    float tu, tv;
};

 

我们要创建一个四边形,所以需要这样一个结构QUAD,显然vertices保存四个顶点,buffer为顶点缓存,texture为用于着色的纹理贴图。

struct QUAD
{
    VERTEX vertices[4];
    LPDIRECT3DVERTEXBUFFER9 buffer;
    LPDIRECT3DTEXTURE9 texture;
};

 

基础部分暂时讲到这里,下面看一个例子,还是基于前面的例子,只有game.cpp文件改变了,也是唯一重要的文件。

DirectSound.h

View Code
  1 //-----------------------------------------------------------------------------
  2 // File: DSUtil.h
  3 //
  4 // Desc: 
  5 //
  6 // Copyright (c) Microsoft Corp. All rights reserved.
  7 //-----------------------------------------------------------------------------
  8 //
  9 // Note: This file has been edited for use in Beginning Game Programming, Third Edition,
 10 // originally distributed with a 2004 release of DirectX 9.0c SDK.
 11 //
 12 
 13 #ifndef DSUTIL_H
 14 #define DSUTIL_H
 15 
 16 #include <windows.h>
 17 #include <mmsystem.h>
 18 #include <mmreg.h>
 19 #include <dsound.h>
 20 #include <dxerr.h>
 21 
 22 #pragma comment(lib, "dxerr.lib")
 23 #pragma comment(lib, "dsound.lib")
 24 
 25 
 26 //-----------------------------------------------------------------------------
 27 // Classes used by this header
 28 //-----------------------------------------------------------------------------
 29 class CSoundManager;
 30 class CSound;
 31 class CStreamingSound;
 32 class CWaveFile;
 33 
 34 
 35 
 36 
 37 //-----------------------------------------------------------------------------
 38 // Typing macros 
 39 //-----------------------------------------------------------------------------
 40 #define WAVEFILE_READ   1
 41 #define WAVEFILE_WRITE  2
 42 
 43 #define DSUtil_StopSound(s)         { if(s) s->Stop(); }
 44 #define DSUtil_PlaySound(s)         { if(s) s->Play( 0, 0 ); }
 45 #define DSUtil_PlaySoundLooping(s)  { if(s) s->Play( 0, DSBPLAY_LOOPING ); }
 46 
 47 
 48 
 49 
 50 //-----------------------------------------------------------------------------
 51 // Name: class CSoundManager
 52 // Desc: 
 53 //-----------------------------------------------------------------------------
 54 class CSoundManager
 55 {
 56 protected:
 57     LPDIRECTSOUND8 m_pDS;
 58 
 59 public:
 60     CSoundManager();
 61     ~CSoundManager();
 62 
 63     HRESULT Initialize( HWND hWnd, DWORD dwCoopLevel );
 64 
 65     inline  LPDIRECTSOUND8 GetDirectSound() { return m_pDS; }
 66 
 67     HRESULT SetPrimaryBufferFormat( DWORD dwPrimaryChannels, DWORD dwPrimaryFreq, 
 68         DWORD dwPrimaryBitRate );
 69 
 70     HRESULT Create( CSound** ppSound, LPTSTR strWaveFileName, DWORD dwCreationFlags = 0,
 71         GUID guid3DAlgorithm = GUID_NULL, DWORD dwNumBuffers = 1 );
 72 };
 73 
 74 
 75 
 76 
 77 //-----------------------------------------------------------------------------
 78 // Name: class CSound
 79 // Desc: Encapsulates functionality of a DirectSound buffer.
 80 //-----------------------------------------------------------------------------
 81 class CSound
 82 {
 83 protected:
 84     LPDIRECTSOUNDBUFFER* m_apDSBuffer;
 85     DWORD                m_dwDSBufferSize;
 86     CWaveFile*           m_pWaveFile;
 87     DWORD                m_dwNumBuffers;
 88     DWORD                m_dwCreationFlags;
 89 
 90     HRESULT RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored );
 91 
 92 public:
 93     CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, DWORD dwNumBuffers,
 94         CWaveFile* pWaveFile, DWORD dwCreationFlags );
 95     virtual ~CSound();
 96 
 97     HRESULT FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger );
 98     LPDIRECTSOUNDBUFFER GetFreeBuffer();
 99 
100     HRESULT Play( DWORD dwPriority = 0, DWORD dwFlags = 0, LONG lVolume = 0, 
101         LONG lFrequency = -1, LONG lPan = 0 );
102     HRESULT Stop();
103     HRESULT Reset();
104     BOOL    IsSoundPlaying();
105 };
106 
107 
108 //-----------------------------------------------------------------------------
109 // Name: class CWaveFile
110 // Desc: Encapsulates reading or writing sound data to or from a wave file
111 //-----------------------------------------------------------------------------
112 class CWaveFile
113 {
114 public:
115     WAVEFORMATEX* m_pwfx;        // Pointer to WAVEFORMATEX structure
116     HMMIO         m_hmmio;       // MM I/O handle for the WAVE
117     MMCKINFO      m_ck;          // Multimedia RIFF chunk
118     MMCKINFO      m_ckRiff;      // Use in opening a WAVE file
119     DWORD         m_dwSize;      // The size of the wave file
120     MMIOINFO      m_mmioinfoOut;
121     DWORD         m_dwFlags;
122     BOOL          m_bIsReadingFromMemory;
123     BYTE*         m_pbData;
124     BYTE*         m_pbDataCur;
125     ULONG         m_ulDataSize;
126     CHAR*         m_pResourceBuffer;
127 
128 protected:
129     HRESULT ReadMMIO();
130     HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest );
131 
132 public:
133     CWaveFile();
134     ~CWaveFile();
135 
136     HRESULT Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags );
137     HRESULT Close();
138 
139     HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead );
140     HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote );
141 
142     DWORD   GetSize();
143     HRESULT ResetFile();
144     WAVEFORMATEX* GetFormat() { return m_pwfx; };
145 };
146 
147 
148 
149 
150 #endif // DSUTIL_H

 

DirectSound.cpp

View Code
   1 //-----------------------------------------------------------------------------
   2 // File: DSUtil.cpp
   3 //
   4 // Desc: DirectSound framework classes for reading and writing wav files and
   5 //       playing them in DirectSound buffers. Feel free to use this class 
   6 //       as a starting point for adding extra functionality.
   7 //
   8 // Copyright (c) Microsoft Corp. All rights reserved.
   9 //-----------------------------------------------------------------------------
  10 //
  11 // Note: This file has been edited for use in Beginning Game Programming, Third Edition,
  12 // originally distributed with a 2004 release of DirectX 9.0c SDK.
  13 //
  14 
  15 #define STRICT
  16 #include <windows.h>
  17 #include <mmsystem.h>
  18 #include "DirectSound.h"
  19 
  20 
  21 //-----------------------------------------------------------------------------
  22 // Miscellaneous helper functions
  23 //-----------------------------------------------------------------------------
  24 #define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
  25 #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
  26 #define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
  27 
  28 
  29 //-----------------------------------------------------------------------------
  30 // Name: CSoundManager::CSoundManager()
  31 // Desc: Constructs the class
  32 //-----------------------------------------------------------------------------
  33 CSoundManager::CSoundManager()
  34 {
  35     m_pDS = NULL;
  36 }
  37 
  38 //-----------------------------------------------------------------------------
  39 // Name: CSoundManager::~CSoundManager()
  40 // Desc: Destroys the class
  41 //-----------------------------------------------------------------------------
  42 CSoundManager::~CSoundManager()
  43 {
  44     SAFE_RELEASE( m_pDS ); 
  45 }
  46 
  47 //-----------------------------------------------------------------------------
  48 // Name: CSoundManager::Initialize()
  49 // Desc: Initializes the IDirectSound object and also sets the primary buffer
  50 //       format.  This function must be called before any others.
  51 //-----------------------------------------------------------------------------
  52 HRESULT CSoundManager::Initialize( HWND  hWnd, 
  53                                    DWORD dwCoopLevel )
  54 {
  55     HRESULT             hr;
  56 
  57     SAFE_RELEASE( m_pDS );
  58 
  59     // Create IDirectSound using the primary sound device
  60     if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) )
  61         return DXTRACE_ERR( TEXT("DirectSoundCreate8"), hr );
  62 
  63     // Set DirectSound coop level 
  64     if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) )
  65         return DXTRACE_ERR( TEXT("SetCooperativeLevel"), hr );   
  66 
  67     return S_OK;
  68 }
  69 
  70 
  71 
  72 
  73 //-----------------------------------------------------------------------------
  74 // Name: CSoundManager::SetPrimaryBufferFormat()
  75 // Desc: Set primary buffer to a specified format 
  76 //       !WARNING! - Setting the primary buffer format and then using this
  77 //                   same dsound object for DirectMusic messes up DirectMusic! 
  78 //       For example, to set the primary buffer format to 22kHz stereo, 16-bit
  79 //       then:   dwPrimaryChannels = 2
  80 //               dwPrimaryFreq     = 22050, 
  81 //               dwPrimaryBitRate  = 16
  82 //-----------------------------------------------------------------------------
  83 HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, 
  84                                                DWORD dwPrimaryFreq, 
  85                                                DWORD dwPrimaryBitRate )
  86 {
  87     HRESULT             hr;
  88     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
  89 
  90     if( m_pDS == NULL )
  91         return CO_E_NOTINITIALIZED;
  92 
  93     // Get the primary buffer 
  94     DSBUFFERDESC dsbd;
  95     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
  96     dsbd.dwSize        = sizeof(DSBUFFERDESC);
  97     dsbd.dwFlags       = DSBCAPS_PRIMARYBUFFER;
  98     dsbd.dwBufferBytes = 0;
  99     dsbd.lpwfxFormat   = NULL;
 100        
 101     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
 102         return DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr );
 103 
 104     WAVEFORMATEX wfx;
 105     ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); 
 106     wfx.wFormatTag      = (WORD) WAVE_FORMAT_PCM; 
 107     wfx.nChannels       = (WORD) dwPrimaryChannels; 
 108     wfx.nSamplesPerSec  = (DWORD) dwPrimaryFreq; 
 109     wfx.wBitsPerSample  = (WORD) dwPrimaryBitRate; 
 110     wfx.nBlockAlign     = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
 111     wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign);
 112 
 113     if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )
 114         return DXTRACE_ERR( TEXT("SetFormat"), hr );
 115 
 116     SAFE_RELEASE( pDSBPrimary );
 117 
 118     return S_OK;
 119 }
 120 
 121 
 122 
 123 //-----------------------------------------------------------------------------
 124 // Name: CSoundManager::Create()
 125 // Desc: 
 126 //-----------------------------------------------------------------------------
 127 HRESULT CSoundManager::Create( CSound** ppSound, 
 128                                LPTSTR strWaveFileName, 
 129                                DWORD dwCreationFlags, 
 130                                GUID guid3DAlgorithm,
 131                                DWORD dwNumBuffers )
 132 {
 133     HRESULT hr;
 134     HRESULT hrRet = S_OK;
 135     DWORD   i;
 136     LPDIRECTSOUNDBUFFER* apDSBuffer     = NULL;
 137     DWORD                dwDSBufferSize = NULL;
 138     CWaveFile*           pWaveFile      = NULL;
 139 
 140     if( m_pDS == NULL )
 141         return CO_E_NOTINITIALIZED;
 142     if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 )
 143         return E_INVALIDARG;
 144 
 145     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
 146     if( apDSBuffer == NULL )
 147     {
 148         hr = E_OUTOFMEMORY;
 149         goto LFail;
 150     }
 151 
 152     pWaveFile = new CWaveFile();
 153     if( pWaveFile == NULL )
 154     {
 155         hr = E_OUTOFMEMORY;
 156         goto LFail;
 157     }
 158 
 159     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
 160 
 161     if( pWaveFile->GetSize() == 0 )
 162     {
 163         // Wave is blank, so don't create it.
 164         hr = E_FAIL;
 165         goto LFail;
 166     }
 167 
 168     // Make the DirectSound buffer the same size as the wav file
 169     dwDSBufferSize = pWaveFile->GetSize();
 170 
 171     // Create the direct sound buffer, and only request the flags needed
 172     // since each requires some overhead and limits if the buffer can 
 173     // be hardware accelerated
 174     DSBUFFERDESC dsbd;
 175     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
 176     dsbd.dwSize          = sizeof(DSBUFFERDESC);
 177     dsbd.dwFlags         = dwCreationFlags;
 178     dsbd.dwBufferBytes   = dwDSBufferSize;
 179     dsbd.guid3DAlgorithm = guid3DAlgorithm;
 180     dsbd.lpwfxFormat     = pWaveFile->m_pwfx;
 181     
 182     // DirectSound is only guarenteed to play PCM data.  Other
 183     // formats may or may not work depending the sound card driver.
 184     hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL );
 185 
 186     // Be sure to return this error code if it occurs so the
 187     // callers knows this happened.
 188     if( hr == DS_NO_VIRTUALIZATION )
 189         hrRet = DS_NO_VIRTUALIZATION;
 190             
 191     if( FAILED(hr) )
 192     {
 193         // DSERR_BUFFERTOOSMALL will be returned if the buffer is
 194         // less than DSBSIZE_FX_MIN and the buffer is created
 195         // with DSBCAPS_CTRLFX.
 196         
 197         // It might also fail if hardware buffer mixing was requested
 198         // on a device that doesn't support it.
 199         DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr );
 200                     
 201         goto LFail;
 202     }
 203 
 204     // Default to use DuplicateSoundBuffer() when created extra buffers since always 
 205     // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if 
 206     // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
 207     if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 )
 208     {
 209         for( i=1; i<dwNumBuffers; i++ )
 210         {
 211             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
 212             {
 213                 DXTRACE_ERR( TEXT("DuplicateSoundBuffer"), hr );
 214                 goto LFail;
 215             }
 216         }
 217     }
 218     else
 219     {
 220         for( i=1; i<dwNumBuffers; i++ )
 221         {
 222             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
 223             if( FAILED(hr) )
 224             {
 225                 DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr );
 226                 goto LFail;
 227             }
 228         }
 229    }
 230     
 231     // Create the sound
 232     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
 233     
 234     SAFE_DELETE_ARRAY( apDSBuffer );
 235     return hrRet;
 236 
 237 LFail:
 238     // Cleanup
 239     SAFE_DELETE( pWaveFile );
 240     SAFE_DELETE_ARRAY( apDSBuffer );
 241     return hr;
 242 }
 243 
 244 
 245 
 246 //-----------------------------------------------------------------------------
 247 // Name: CSound::CSound()
 248 // Desc: Constructs the class
 249 //-----------------------------------------------------------------------------
 250 CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, 
 251                 DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags )
 252 {
 253     DWORD i;
 254 
 255     m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
 256     if( NULL != m_apDSBuffer )
 257     {
 258         for( i=0; i<dwNumBuffers; i++ )
 259             m_apDSBuffer[i] = apDSBuffer[i];
 260     
 261         m_dwDSBufferSize = dwDSBufferSize;
 262         m_dwNumBuffers   = dwNumBuffers;
 263         m_pWaveFile      = pWaveFile;
 264         m_dwCreationFlags = dwCreationFlags;
 265         
 266         FillBufferWithSound( m_apDSBuffer[0], FALSE );
 267     }
 268 }
 269 
 270 
 271 
 272 
 273 //-----------------------------------------------------------------------------
 274 // Name: CSound::~CSound()
 275 // Desc: Destroys the class
 276 //-----------------------------------------------------------------------------
 277 CSound::~CSound()
 278 {
 279     for( DWORD i=0; i<m_dwNumBuffers; i++ )
 280     {
 281         SAFE_RELEASE( m_apDSBuffer[i] ); 
 282     }
 283 
 284     SAFE_DELETE_ARRAY( m_apDSBuffer ); 
 285     SAFE_DELETE( m_pWaveFile );
 286 }
 287 
 288 
 289 
 290 
 291 //-----------------------------------------------------------------------------
 292 // Name: CSound::FillBufferWithSound()
 293 // Desc: Fills a DirectSound buffer with a sound file 
 294 //-----------------------------------------------------------------------------
 295 HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger )
 296 {
 297     HRESULT hr; 
 298     VOID*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
 299     DWORD   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer
 300     DWORD   dwWavDataRead        = 0;    // Amount of data read from the wav file 
 301 
 302     if( pDSB == NULL )
 303         return CO_E_NOTINITIALIZED;
 304 
 305     // Make sure we have focus, and we didn't just switch in from
 306     // an app which had a DirectSound device
 307     if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) 
 308         return DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
 309 
 310     // Lock the buffer down
 311     if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, 
 312                                  &pDSLockedBuffer, &dwDSLockedBufferSize, 
 313                                  NULL, NULL, 0L ) ) )
 314         return DXTRACE_ERR( TEXT("Lock"), hr );
 315 
 316     // Reset the wave file to the beginning 
 317     m_pWaveFile->ResetFile();
 318 
 319     if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer,
 320                                         dwDSLockedBufferSize, 
 321                                         &dwWavDataRead ) ) )           
 322         return DXTRACE_ERR( TEXT("Read"), hr );
 323 
 324     if( dwWavDataRead == 0 )
 325     {
 326         // Wav is blank, so just fill with silence
 327         FillMemory( (BYTE*) pDSLockedBuffer, 
 328                     dwDSLockedBufferSize, 
 329                     (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
 330     }
 331     else if( dwWavDataRead < dwDSLockedBufferSize )
 332     {
 333         // If the wav file was smaller than the DirectSound buffer, 
 334         // we need to fill the remainder of the buffer with data 
 335         if( bRepeatWavIfBufferLarger )
 336         {       
 337             // Reset the file and fill the buffer with wav data
 338             DWORD dwReadSoFar = dwWavDataRead;    // From previous call above.
 339             while( dwReadSoFar < dwDSLockedBufferSize )
 340             {  
 341                 // This will keep reading in until the buffer is full 
 342                 // for very short files
 343                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
 344                     return DXTRACE_ERR( TEXT("ResetFile"), hr );
 345 
 346                 hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
 347                                         dwDSLockedBufferSize - dwReadSoFar,
 348                                         &dwWavDataRead );
 349                 if( FAILED(hr) )
 350                     return DXTRACE_ERR( TEXT("Read"), hr );
 351 
 352                 dwReadSoFar += dwWavDataRead;
 353             } 
 354         }
 355         else
 356         {
 357             // Don't repeat the wav file, just fill in silence 
 358             FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead, 
 359                         dwDSLockedBufferSize - dwWavDataRead, 
 360                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
 361         }
 362     }
 363 
 364     // Unlock the buffer, we don't need it anymore.
 365     pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
 366 
 367     return S_OK;
 368 }
 369 
 370 
 371 
 372 
 373 //-----------------------------------------------------------------------------
 374 // Name: CSound::RestoreBuffer()
 375 // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was 
 376 //       restored.  It can also NULL if the information is not needed.
 377 //-----------------------------------------------------------------------------
 378 HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored )
 379 {
 380     HRESULT hr;
 381 
 382     if( pDSB == NULL )
 383         return CO_E_NOTINITIALIZED;
 384     if( pbWasRestored )
 385         *pbWasRestored = FALSE;
 386 
 387     DWORD dwStatus;
 388     if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) )
 389         return DXTRACE_ERR( TEXT("GetStatus"), hr );
 390 
 391     if( dwStatus & DSBSTATUS_BUFFERLOST )
 392     {
 393         // Since the app could have just been activated, then
 394         // DirectSound may not be giving us control yet, so 
 395         // the restoring the buffer may fail.  
 396         // If it does, sleep until DirectSound gives us control.
 397         do 
 398         {
 399             hr = pDSB->Restore();
 400             if( hr == DSERR_BUFFERLOST )
 401                 Sleep( 10 );
 402         }
 403         while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST );
 404 
 405         if( pbWasRestored != NULL )
 406             *pbWasRestored = TRUE;
 407 
 408         return S_OK;
 409     }
 410     else
 411     {
 412         return S_FALSE;
 413     }
 414 }
 415 
 416 
 417 
 418 
 419 //-----------------------------------------------------------------------------
 420 // Name: CSound::GetFreeBuffer()
 421 // Desc: Finding the first buffer that is not playing and return a pointer to 
 422 //       it, or if all are playing return a pointer to a randomly selected buffer.
 423 //-----------------------------------------------------------------------------
 424 LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer()
 425 {
 426     if( m_apDSBuffer == NULL )
 427         return FALSE; 
 428     DWORD i;
 429     for( i=0; i<m_dwNumBuffers; i++ )
 430     {
 431         if( m_apDSBuffer[i] )
 432         {  
 433             DWORD dwStatus = 0;
 434             m_apDSBuffer[i]->GetStatus( &dwStatus );
 435             if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
 436                 break;
 437         }
 438     }
 439 
 440     if( i != m_dwNumBuffers )
 441         return m_apDSBuffer[ i ];
 442     else
 443         return m_apDSBuffer[ rand() % m_dwNumBuffers ];
 444 }
 445 
 446 
 447 
 448 //-----------------------------------------------------------------------------
 449 // Name: CSound::Play()
 450 // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
 451 //       in the dwFlags to loop the sound
 452 //-----------------------------------------------------------------------------
 453 HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan )
 454 {
 455     HRESULT hr;
 456     BOOL    bRestored;
 457 
 458     if( m_apDSBuffer == NULL )
 459         return CO_E_NOTINITIALIZED;
 460 
 461     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
 462 
 463     if( pDSB == NULL )
 464         return DXTRACE_ERR( TEXT("GetFreeBuffer"), E_FAIL );
 465 
 466     // Restore the buffer if it was lost
 467     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
 468         return DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
 469 
 470     if( bRestored )
 471     {
 472         // The buffer was restored, so we need to fill it with new data
 473         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
 474             return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr );
 475     }
 476 
 477     if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME )
 478     {
 479         pDSB->SetVolume( lVolume );
 480     }
 481 
 482     if( lFrequency != -1 && 
 483         (m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY) )
 484     {
 485         pDSB->SetFrequency( lFrequency );
 486     }
 487     
 488     if( m_dwCreationFlags & DSBCAPS_CTRLPAN )
 489     {
 490         pDSB->SetPan( lPan );
 491     }
 492     
 493     return pDSB->Play( 0, dwPriority, dwFlags );
 494 }
 495 
 496 
 497 
 498 //-----------------------------------------------------------------------------
 499 // Name: CSound::Stop()
 500 // Desc: Stops the sound from playing
 501 //-----------------------------------------------------------------------------
 502 HRESULT CSound::Stop()
 503 {
 504     if( m_apDSBuffer == NULL )
 505         return CO_E_NOTINITIALIZED;
 506 
 507     HRESULT hr = 0;
 508 
 509     for( DWORD i=0; i<m_dwNumBuffers; i++ )
 510         hr |= m_apDSBuffer[i]->Stop();
 511 
 512     return hr;
 513 }
 514 
 515 
 516 
 517 
 518 //-----------------------------------------------------------------------------
 519 // Name: CSound::Reset()
 520 // Desc: Reset all of the sound buffers
 521 //-----------------------------------------------------------------------------
 522 HRESULT CSound::Reset()
 523 {
 524     if( m_apDSBuffer == NULL )
 525         return CO_E_NOTINITIALIZED;
 526 
 527     HRESULT hr = 0;
 528 
 529     for( DWORD i=0; i<m_dwNumBuffers; i++ )
 530         hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );
 531 
 532     return hr;
 533 }
 534 
 535 
 536 
 537 
 538 //-----------------------------------------------------------------------------
 539 // Name: CSound::IsSoundPlaying()
 540 // Desc: Checks to see if a buffer is playing and returns TRUE if it is.
 541 //-----------------------------------------------------------------------------
 542 BOOL CSound::IsSoundPlaying()
 543 {
 544     BOOL bIsPlaying = FALSE;
 545 
 546     if( m_apDSBuffer == NULL )
 547         return FALSE; 
 548 
 549     for( DWORD i=0; i<m_dwNumBuffers; i++ )
 550     {
 551         if( m_apDSBuffer[i] )
 552         {  
 553             DWORD dwStatus = 0;
 554             m_apDSBuffer[i]->GetStatus( &dwStatus );
 555             bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
 556         }
 557     }
 558 
 559     return bIsPlaying;
 560 }
 561 
 562 
 563 
 564 //-----------------------------------------------------------------------------
 565 // Name: CWaveFile::CWaveFile()
 566 // Desc: Constructs the class.  Call Open() to open a wave file for reading.  
 567 //       Then call Read() as needed.  Calling the destructor or Close() 
 568 //       will close the file.  
 569 //-----------------------------------------------------------------------------
 570 CWaveFile::CWaveFile()
 571 {
 572     m_pwfx    = NULL;
 573     m_hmmio   = NULL;
 574     m_pResourceBuffer = NULL;
 575     m_dwSize  = 0;
 576     m_bIsReadingFromMemory = FALSE;
 577 }
 578 
 579 
 580 
 581 
 582 //-----------------------------------------------------------------------------
 583 // Name: CWaveFile::~CWaveFile()
 584 // Desc: Destructs the class
 585 //-----------------------------------------------------------------------------
 586 CWaveFile::~CWaveFile()
 587 {
 588     Close();
 589 
 590     if( !m_bIsReadingFromMemory )
 591         SAFE_DELETE_ARRAY( m_pwfx );
 592 }
 593 
 594 
 595 
 596 
 597 //-----------------------------------------------------------------------------
 598 // Name: CWaveFile::Open()
 599 // Desc: Opens a wave file for reading
 600 //-----------------------------------------------------------------------------
 601 HRESULT CWaveFile::Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
 602 {
 603     HRESULT hr;
 604 
 605     m_dwFlags = dwFlags;
 606     m_bIsReadingFromMemory = FALSE;
 607 
 608     if( m_dwFlags == WAVEFILE_READ )
 609     {
 610         if( strFileName == NULL )
 611             return E_INVALIDARG;
 612         SAFE_DELETE_ARRAY( m_pwfx );
 613 
 614         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
 615 
 616         if( NULL == m_hmmio )
 617         {
 618             HRSRC   hResInfo;
 619             HGLOBAL hResData;
 620             DWORD   dwSize;
 621             VOID*   pvRes;
 622 
 623             // Loading it as a file failed, so try it as a resource
 624             if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
 625             {
 626                 if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
 627                     return DXTRACE_ERR( TEXT("FindResource"), E_FAIL );
 628             }
 629 
 630             if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
 631                 return DXTRACE_ERR( TEXT("LoadResource"), E_FAIL );
 632 
 633             if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
 634                 return DXTRACE_ERR( TEXT("SizeofResource"), E_FAIL );
 635 
 636             if( NULL == ( pvRes = LockResource( hResData ) ) )
 637                 return DXTRACE_ERR( TEXT("LockResource"), E_FAIL );
 638 
 639             m_pResourceBuffer = new CHAR[ dwSize ];
 640             memcpy( m_pResourceBuffer, pvRes, dwSize );
 641 
 642             MMIOINFO mmioInfo;
 643             ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
 644             mmioInfo.fccIOProc = FOURCC_MEM;
 645             mmioInfo.cchBuffer = dwSize;
 646             mmioInfo.pchBuffer = (CHAR*) m_pResourceBuffer;
 647 
 648             m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
 649         }
 650 
 651         if( FAILED( hr = ReadMMIO() ) )
 652         {
 653             // ReadMMIO will fail if its an not a wave file
 654             mmioClose( m_hmmio, 0 );
 655             return DXTRACE_ERR( TEXT("ReadMMIO"), hr );
 656         }
 657 
 658         if( FAILED( hr = ResetFile() ) )
 659             return DXTRACE_ERR( TEXT("ResetFile"), hr );
 660 
 661         // After the reset, the size of the wav file is m_ck.cksize so store it now
 662         m_dwSize = m_ck.cksize;
 663     }
 664     else
 665     {
 666         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF  | 
 667                                                   MMIO_READWRITE | 
 668                                                   MMIO_CREATE );
 669         if( NULL == m_hmmio )
 670             return DXTRACE_ERR( TEXT("mmioOpen"), E_FAIL );
 671 
 672         if( FAILED( hr = WriteMMIO( pwfx ) ) )
 673         {
 674             mmioClose( m_hmmio, 0 );
 675             return DXTRACE_ERR( TEXT("WriteMMIO"), hr );
 676         }
 677                         
 678         if( FAILED( hr = ResetFile() ) )
 679             return DXTRACE_ERR( TEXT("ResetFile"), hr );
 680     }
 681 
 682     return hr;
 683 }
 684 
 685 
 686 
 687 //-----------------------------------------------------------------------------
 688 // Name: CWaveFile::ReadMMIO()
 689 // Desc: Support function for reading from a multimedia I/O stream.
 690 //       m_hmmio must be valid before calling.  This function uses it to
 691 //       update m_ckRiff, and m_pwfx. 
 692 //-----------------------------------------------------------------------------
 693 HRESULT CWaveFile::ReadMMIO()
 694 {
 695     MMCKINFO        ckIn;           // chunk info. for general use.
 696     PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.       
 697 
 698     m_pwfx = NULL;
 699 
 700     if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
 701         return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
 702 
 703     // Check to make sure this is a valid wave file
 704     if( (m_ckRiff.ckid != FOURCC_RIFF) ||
 705         (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
 706         return DXTRACE_ERR( TEXT("mmioFOURCC"), E_FAIL ); 
 707 
 708     // Search the input file for for the 'fmt ' chunk.
 709     ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
 710     if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
 711         return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
 712 
 713     // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
 714     // if there are extra parameters at the end, we'll ignore them
 715        if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
 716            return DXTRACE_ERR( TEXT("sizeof(PCMWAVEFORMAT)"), E_FAIL );
 717 
 718     // Read the 'fmt ' chunk into <pcmWaveFormat>.
 719     if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, 
 720                   sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
 721         return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
 722 
 723     // Allocate the waveformatex, but if its not pcm format, read the next
 724     // word, and thats how many extra bytes to allocate.
 725     if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
 726     {
 727         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
 728         if( NULL == m_pwfx )
 729             return DXTRACE_ERR( TEXT("m_pwfx"), E_FAIL );
 730 
 731         // Copy the bytes from the pcm structure to the waveformatex structure
 732         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
 733         m_pwfx->cbSize = 0;
 734     }
 735     else
 736     {
 737         // Read in length of extra bytes.
 738         WORD cbExtraBytes = 0L;
 739         if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
 740             return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
 741 
 742         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
 743         if( NULL == m_pwfx )
 744             return DXTRACE_ERR( TEXT("new"), E_FAIL );
 745 
 746         // Copy the bytes from the pcm structure to the waveformatex structure
 747         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
 748         m_pwfx->cbSize = cbExtraBytes;
 749 
 750         // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
 751         if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)),
 752                       cbExtraBytes ) != cbExtraBytes )
 753         {
 754             SAFE_DELETE( m_pwfx );
 755             return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
 756         }
 757     }
 758 
 759     // Ascend the input file out of the 'fmt ' chunk.
 760     if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
 761     {
 762         SAFE_DELETE( m_pwfx );
 763         return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
 764     }
 765 
 766     return S_OK;
 767 }
 768 
 769 
 770 
 771 
 772 //-----------------------------------------------------------------------------
 773 // Name: CWaveFile::GetSize()
 774 // Desc: Retuns the size of the read access wave file 
 775 //-----------------------------------------------------------------------------
 776 DWORD CWaveFile::GetSize()
 777 {
 778     return m_dwSize;
 779 }
 780 
 781 
 782 
 783 
 784 //-----------------------------------------------------------------------------
 785 // Name: CWaveFile::ResetFile()
 786 // Desc: Resets the internal m_ck pointer so reading starts from the 
 787 //       beginning of the file again 
 788 //-----------------------------------------------------------------------------
 789 HRESULT CWaveFile::ResetFile()
 790 {
 791     if( m_bIsReadingFromMemory )
 792     {
 793         m_pbDataCur = m_pbData;
 794     }
 795     else 
 796     {
 797         if( m_hmmio == NULL )
 798             return CO_E_NOTINITIALIZED;
 799 
 800         if( m_dwFlags == WAVEFILE_READ )
 801         {
 802             // Seek to the data
 803             if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
 804                             SEEK_SET ) )
 805                 return DXTRACE_ERR( TEXT("mmioSeek"), E_FAIL );
 806 
 807             // Search the input file for the 'data' chunk.
 808             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
 809             if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
 810               return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
 811         }
 812         else
 813         {
 814             // Create the 'data' chunk that holds the waveform samples.  
 815             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
 816             m_ck.cksize = 0;
 817 
 818             if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) 
 819                 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
 820 
 821             if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
 822                 return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
 823         }
 824     }
 825     
 826     return S_OK;
 827 }
 828 
 829 
 830 
 831 
 832 //-----------------------------------------------------------------------------
 833 // Name: CWaveFile::Read()
 834 // Desc: Reads section of data from a wave file into pBuffer and returns 
 835 //       how much read in pdwSizeRead, reading not more than dwSizeToRead.
 836 //       This uses m_ck to determine where to start reading from.  So 
 837 //       subsequent calls will be continue where the last left off unless 
 838 //       Reset() is called.
 839 //-----------------------------------------------------------------------------
 840 HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
 841 {
 842     if( m_bIsReadingFromMemory )
 843     {
 844         if( m_pbDataCur == NULL )
 845             return CO_E_NOTINITIALIZED;
 846         if( pdwSizeRead != NULL )
 847             *pdwSizeRead = 0;
 848 
 849         if( (BYTE*)(m_pbDataCur + dwSizeToRead) > 
 850             (BYTE*)(m_pbData + m_ulDataSize) )
 851         {
 852             dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
 853         }
 854         
 855         CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
 856         
 857         if( pdwSizeRead != NULL )
 858             *pdwSizeRead = dwSizeToRead;
 859 
 860         return S_OK;
 861     }
 862     else 
 863     {
 864         MMIOINFO mmioinfoIn; // current status of m_hmmio
 865 
 866         if( m_hmmio == NULL )
 867             return CO_E_NOTINITIALIZED;
 868         if( pBuffer == NULL || pdwSizeRead == NULL )
 869             return E_INVALIDARG;
 870 
 871         if( pdwSizeRead != NULL )
 872             *pdwSizeRead = 0;
 873 
 874         if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
 875             return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
 876                 
 877         UINT cbDataIn = dwSizeToRead;
 878         if( cbDataIn > m_ck.cksize ) 
 879             cbDataIn = m_ck.cksize;       
 880 
 881         m_ck.cksize -= cbDataIn;
 882     
 883         for( DWORD cT = 0; cT < cbDataIn; cT++ )
 884         {
 885             // Copy the bytes from the io to the buffer.
 886             if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
 887             {
 888                 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
 889                     return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
 890 
 891                 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
 892                     return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
 893             }
 894 
 895             // Actual copy.
 896             *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
 897             mmioinfoIn.pchNext++;
 898         }
 899 
 900         if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
 901             return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
 902 
 903         if( pdwSizeRead != NULL )
 904             *pdwSizeRead = cbDataIn;
 905 
 906         return S_OK;
 907     }
 908 }
 909 
 910 
 911 
 912 
 913 //-----------------------------------------------------------------------------
 914 // Name: CWaveFile::Close()
 915 // Desc: Closes the wave file 
 916 //-----------------------------------------------------------------------------
 917 HRESULT CWaveFile::Close()
 918 {
 919     if( m_dwFlags == WAVEFILE_READ )
 920     {
 921         mmioClose( m_hmmio, 0 );
 922         m_hmmio = NULL;
 923         SAFE_DELETE_ARRAY( m_pResourceBuffer );
 924     }
 925     else
 926     {
 927         m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
 928 
 929         if( m_hmmio == NULL )
 930             return CO_E_NOTINITIALIZED;
 931 
 932         if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
 933             return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
 934     
 935         // Ascend the output file out of the 'data' chunk -- this will cause
 936         // the chunk size of the 'data' chunk to be written.
 937         if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
 938             return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
 939     
 940         // Do this here instead...
 941         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
 942             return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
 943         
 944         mmioSeek( m_hmmio, 0, SEEK_SET );
 945 
 946         if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) )
 947             return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
 948     
 949         m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
 950 
 951         if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) 
 952         {
 953             DWORD dwSamples = 0;
 954             mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) );
 955             mmioAscend( m_hmmio, &m_ck, 0 ); 
 956         }
 957     
 958         // Ascend the output file out of the 'RIFF' chunk -- this will cause
 959         // the chunk size of the 'RIFF' chunk to be written.
 960         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
 961             return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
 962     
 963         mmioClose( m_hmmio, 0 );
 964         m_hmmio = NULL;
 965     }
 966 
 967     return S_OK;
 968 }
 969 
 970 
 971 
 972 
 973 //-----------------------------------------------------------------------------
 974 // Name: CWaveFile::WriteMMIO()
 975 // Desc: Support function for reading from a multimedia I/O stream
 976 //       pwfxDest is the WAVEFORMATEX for this new wave file.  
 977 //       m_hmmio must be valid before calling.  This function uses it to
 978 //       update m_ckRiff, and m_ck.  
 979 //-----------------------------------------------------------------------------
 980 HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest )
 981 {
 982     DWORD    dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
 983     MMCKINFO ckOut1;
 984     
 985     dwFactChunk = (DWORD)-1;
 986 
 987     // Create the output file RIFF chunk of form type 'WAVE'.
 988     m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E');       
 989     m_ckRiff.cksize = 0;
 990 
 991     if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) )
 992         return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
 993     
 994     // We are now descended into the 'RIFF' chunk we just created.
 995     // Now create the 'fmt ' chunk. Since we know the size of this chunk,
 996     // specify it in the MMCKINFO structure so MMIO doesn't have to seek
 997     // back and set the chunk size after ascending from the chunk.
 998     m_ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
 999     m_ck.cksize = sizeof(PCMWAVEFORMAT);   
1000 
1001     if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
1002         return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
1003     
1004     // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type. 
1005     if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM )
1006     {
1007         if( mmioWrite( m_hmmio, (HPSTR) pwfxDest, 
1008                        sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
1009             return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
1010     }   
1011     else 
1012     {
1013         // Write the variable length size.
1014         if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest, 
1015                              sizeof(*pwfxDest) + pwfxDest->cbSize ) != 
1016                              ( sizeof(*pwfxDest) + pwfxDest->cbSize ) )
1017             return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
1018     }  
1019     
1020     // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
1021     if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
1022         return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
1023     
1024     // Now create the fact chunk, not required for PCM but nice to have.  This is filled
1025     // in when the close routine is called.
1026     ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't');
1027     ckOut1.cksize = 0;
1028 
1029     if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) )
1030         return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
1031     
1032     if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) != 
1033                     sizeof(dwFactChunk) )
1034          return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
1035     
1036     // Now ascend out of the fact chunk...
1037     if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) )
1038         return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
1039        
1040     return S_OK;
1041 }
1042 
1043 
1044 
1045 
1046 //-----------------------------------------------------------------------------
1047 // Name: CWaveFile::Write()
1048 // Desc: Writes data to the open wave file
1049 //-----------------------------------------------------------------------------
1050 HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote )
1051 {
1052     UINT cT;
1053 
1054     if( m_bIsReadingFromMemory )
1055         return E_NOTIMPL;
1056     if( m_hmmio == NULL )
1057         return CO_E_NOTINITIALIZED;
1058     if( pnSizeWrote == NULL || pbSrcData == NULL )
1059         return E_INVALIDARG;
1060 
1061     *pnSizeWrote = 0;
1062     
1063     for( cT = 0; cT < nSizeToWrite; cT++ )
1064     {       
1065         if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite )
1066         {
1067             m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
1068             if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) )
1069                 return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
1070         }
1071 
1072         *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT);
1073         (BYTE*)m_mmioinfoOut.pchNext++;
1074 
1075         (*pnSizeWrote)++;
1076     }
1077 
1078     return S_OK;
1079 }

 

DirectX.h

View Code
  1 #pragma once
  2 
  3 //header files
  4 #define WIN32_EXTRA_LEAN
  5 #define DIRECTINPUT_VERSION 0x0800
  6 #include <windows.h>
  7 #include <d3d9.h>
  8 #include <d3dx9.h>
  9 #include <dxerr.h>
 10 #include <dinput.h>
 11 #include <xinput.h>
 12 #include <ctime>
 13 #include <iostream>
 14 #include <iomanip>
 15 #include "DirectSound.h"
 16 using namespace std;
 17 
 18 //libraries
 19 #pragma comment(lib,"winmm.lib")
 20 #pragma comment(lib,"user32.lib")
 21 #pragma comment(lib,"gdi32.lib")
 22 #pragma comment(lib,"dxguid.lib")
 23 #pragma comment(lib,"d3d9.lib")
 24 #pragma comment(lib,"d3dx9.lib")
 25 #pragma comment(lib, "dxerr.lib")
 26 #pragma comment(lib,"dinput8.lib")
 27 #pragma comment(lib,"xinput.lib")
 28 
 29 //program values
 30 extern const string APPTITLE;
 31 extern const int SCREENW;
 32 extern const int SCREENH;
 33 extern bool gameover;
 34 
 35 //macro to detect key presses
 36 #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
 37 
 38 //Direct3D objects
 39 extern LPDIRECT3D9 d3d; 
 40 extern LPDIRECT3DDEVICE9 d3ddev; 
 41 extern LPDIRECT3DSURFACE9 backbuffer;
 42 extern LPD3DXSPRITE spriteobj;
 43 
 44 //DirectSound object
 45 extern CSoundManager *dsound;
 46 
 47 //sprite structure
 48 struct SPRITE
 49 {
 50     float x,y;
 51     int frame, columns;
 52     int width, height;
 53     float scaling, rotation;
 54     int startframe, endframe;
 55     int starttime, delay;
 56     int direction;
 57     float velx, vely;
 58     D3DCOLOR color;
 59 
 60     SPRITE() 
 61     {
 62         frame = 0;
 63         columns = 1;
 64         width = height = 0;
 65         scaling = 1.0f;
 66         rotation = 0.0f;
 67         startframe = endframe = 0;
 68         direction = 1;
 69         starttime = delay = 0;
 70         velx = vely = 0.0f;
 71         color = D3DCOLOR_XRGB(255,255,255);
 72     }
 73 };
 74 
 75 //Direct3D functions
 76 bool Direct3D_Init(HWND hwnd, int width, int height, bool fullscreen);
 77 void Direct3D_Shutdown();
 78 LPDIRECT3DSURFACE9 LoadSurface(string filename);
 79 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source);
 80 LPDIRECT3DTEXTURE9 LoadTexture(string filename, D3DCOLOR transcolor = D3DCOLOR_XRGB(0,0,0));
 81 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty, 
 82     int framenum, int framew, int frameh, int columns);
 83 void Sprite_Animate(int &frame, int startframe, int endframe, int direction, int &starttime, int delay);
 84 
 85 void Sprite_Transform_Draw(LPDIRECT3DTEXTURE9 image, int x, int y, int width, int height, 
 86     int frame = 0, int columns = 1, float rotation = 0.0f, float scaling = 1.0f, 
 87     D3DCOLOR color = D3DCOLOR_XRGB(255,255,255));
 88 
 89 //bounding box collision detection
 90 int Collision(SPRITE sprite1, SPRITE sprite2);
 91 
 92 //distance based collision detection
 93 bool CollisionD(SPRITE sprite1, SPRITE sprite2);
 94 
 95 //DirectInput objects, devices, and states
 96 extern LPDIRECTINPUT8 dinput;
 97 extern LPDIRECTINPUTDEVICE8 dimouse;
 98 extern LPDIRECTINPUTDEVICE8 dikeyboard;
 99 extern DIMOUSESTATE mouse_state;
100 extern XINPUT_GAMEPAD controllers[4];
101 
102 //DirectInput functions
103 bool DirectInput_Init(HWND);
104 void DirectInput_Update();
105 void DirectInput_Shutdown();
106 bool Key_Down(int);
107 int Mouse_Button(int);
108 int Mouse_X();
109 int Mouse_Y();
110 void XInput_Vibrate(int contNum = 0, int amount = 65535);
111 bool XInput_Controller_Found();
112 
113 //game functions
114 bool Game_Init(HWND window);
115 void Game_Run(HWND window);
116 void Game_End();
117 
118 
119 //font functions
120 LPD3DXFONT MakeFont(string name, int size);
121 void FontPrint(LPD3DXFONT font, int x, int y, string text, D3DCOLOR color = D3DCOLOR_XRGB(255,255,255));
122 
123 //DirectSound functions
124 bool DirectSound_Init(HWND hwnd);
125 void DirectSound_Shutdown();
126 CSound* LoadSound(string filename);
127 void PlaySound(CSound *sound);
128 void LoopSound(CSound *sound);
129 void StopSound(CSound *sound);

 

DirectX.cpp

View Code
  1 #include "DirectX.h"
  2 #include <iostream>
  3 #include <string>
  4 using namespace std;
  5 
  6 //DirectSound object
  7 CSoundManager *dsound = NULL;
  8 
  9 //Direct3D variables
 10 LPDIRECT3D9 d3d = NULL; 
 11 LPDIRECT3DDEVICE9 d3ddev = NULL; 
 12 LPDIRECT3DSURFACE9 backbuffer = NULL;
 13 LPD3DXSPRITE spriteobj;
 14 
 15 //DirectInput variables
 16 LPDIRECTINPUT8 dinput = NULL;
 17 LPDIRECTINPUTDEVICE8 dimouse = NULL;
 18 LPDIRECTINPUTDEVICE8 dikeyboard = NULL;
 19 DIMOUSESTATE mouse_state;
 20 char keys[256];
 21 XINPUT_GAMEPAD controllers[4];
 22 
 23 
 24 bool Direct3D_Init(HWND window, int width, int height, bool fullscreen)
 25 {
 26     //initialize Direct3D
 27     d3d = Direct3DCreate9(D3D_SDK_VERSION);
 28     if (!d3d) return false;
 29 
 30     //set Direct3D presentation parameters
 31     D3DPRESENT_PARAMETERS d3dpp; 
 32     ZeroMemory(&d3dpp, sizeof(d3dpp));
 33     d3dpp.hDeviceWindow = window;
 34     d3dpp.Windowed = (!fullscreen);
 35     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 36     d3dpp.EnableAutoDepthStencil = 1;
 37     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
 38     d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
 39     d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
 40     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
 41     d3dpp.BackBufferCount = 1;
 42     d3dpp.BackBufferWidth = width;
 43     d3dpp.BackBufferHeight = height;
 44 
 45     //create Direct3D device
 46     d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
 47         D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
 48     if (!d3ddev) return false;
 49 
 50 
 51     //get a pointer to the back buffer surface
 52     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
 53 
 54     //create sprite object
 55     D3DXCreateSprite(d3ddev, &spriteobj);
 56 
 57     return 1;
 58 }
 59 
 60 void Direct3D_Shutdown()
 61 {
 62     if (spriteobj) spriteobj->Release();
 63 
 64     if (d3ddev) d3ddev->Release();
 65     if (d3d) d3d->Release();
 66 }
 67 
 68 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source)
 69 {
 70     //get width/height from source surface
 71     D3DSURFACE_DESC desc;
 72     source->GetDesc(&desc);
 73 
 74     //create rects for drawing
 75     RECT source_rect = {0, 0, (long)desc.Width, (long)desc.Height };
 76     RECT dest_rect = { (long)x, (long)y, (long)x+desc.Width, (long)y+desc.Height};
 77     
 78     //draw the source surface onto the dest
 79     d3ddev->StretchRect(source, &source_rect, dest, &dest_rect, D3DTEXF_NONE);
 80 
 81 }
 82 
 83 LPDIRECT3DSURFACE9 LoadSurface(string filename)
 84 {
 85     LPDIRECT3DSURFACE9 image = NULL;
 86     
 87     //get width and height from bitmap file
 88     D3DXIMAGE_INFO info;
 89     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info);
 90     if (result != D3D_OK)
 91         return NULL;
 92 
 93     //create surface
 94     result = d3ddev->CreateOffscreenPlainSurface(
 95         info.Width,         //width of the surface
 96         info.Height,        //height of the surface
 97         D3DFMT_X8R8G8B8,    //surface format
 98         D3DPOOL_DEFAULT,    //memory pool to use
 99         &image,             //pointer to the surface
100         NULL);              //reserved (always NULL)
101 
102     if (result != D3D_OK) return NULL;
103 
104     //load surface from file into newly created surface
105     result = D3DXLoadSurfaceFromFile(
106         image,                  //destination surface
107         NULL,                   //destination palette
108         NULL,                   //destination rectangle
109         filename.c_str(),       //source filename
110         NULL,                   //source rectangle
111         D3DX_DEFAULT,           //controls how image is filtered
112         D3DCOLOR_XRGB(0,0,0),   //for transparency (0 for none)
113         NULL);                  //source image info (usually NULL)
114 
115     //make sure file was loaded okay
116     if (result != D3D_OK) return NULL;
117 
118     return image;
119 }
120 
121 
122 LPDIRECT3DTEXTURE9 LoadTexture(std::string filename, D3DCOLOR transcolor)
123 {  
124     LPDIRECT3DTEXTURE9 texture = NULL;
125 
126     //get width and height from bitmap file
127     D3DXIMAGE_INFO info;
128     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info);
129     if (result != D3D_OK) return NULL;
130 
131     //create the new texture by loading a bitmap image file
132     D3DXCreateTextureFromFileEx( 
133         d3ddev,                //Direct3D device object
134         filename.c_str(),      //bitmap filename
135         info.Width,            //bitmap image width
136         info.Height,           //bitmap image height
137         1,                     //mip-map levels (1 for no chain)
138         D3DPOOL_DEFAULT,       //the type of surface (standard)
139         D3DFMT_UNKNOWN,        //surface format (default)
140         D3DPOOL_DEFAULT,       //memory class for the texture
141         D3DX_DEFAULT,          //image filter
142         D3DX_DEFAULT,          //mip filter
143         transcolor,            //color key for transparency
144         &info,                 //bitmap file info (from loaded file)
145         NULL,                  //color palette
146         &texture );            //destination texture
147 
148     //make sure the bitmap textre was loaded correctly
149     if (result != D3D_OK) return NULL;
150 
151     return texture;
152 }
153 
154 
155 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty, int framenum, int framew, int frameh, int columns)
156 {
157     D3DXVECTOR3 position( (float)destx, (float)desty, 0 );
158     D3DCOLOR white = D3DCOLOR_XRGB(255,255,255);
159 
160     RECT rect;
161      rect.left = (framenum % columns) * framew;
162     rect.top = (framenum / columns) * frameh;
163     rect.right = rect.left + framew;
164     rect.bottom = rect.top + frameh;
165 
166     spriteobj->Draw( texture, &rect, NULL, &position, white);
167 }
168 
169 void Sprite_Animate(int &frame, int startframe, int endframe, int direction, int &starttime, int delay)
170 {
171     if ((int)GetTickCount() > starttime + delay)
172     {
173         starttime = GetTickCount();
174 
175         frame += direction;
176         if (frame > endframe) frame = startframe;
177         if (frame < startframe) frame = endframe;
178     }    
179 }
180 void Sprite_Transform_Draw(LPDIRECT3DTEXTURE9 image, int x, int y, int width, int height, 
181     int frame, int columns, float rotation, float scaling, D3DCOLOR color)
182 {
183     //create a scale vector
184     D3DXVECTOR2 scale( scaling, scaling );
185 
186     //create a translate vector
187     D3DXVECTOR2 trans( x, y );
188 
189     //set center by dividing width and height by two
190     D3DXVECTOR2 center( (float)( width * scaling )/2, (float)( height * scaling )/2);
191 
192     //create 2D transformation matrix
193     D3DXMATRIX mat;
194     D3DXMatrixTransformation2D( &mat, NULL, 0, &scale, &center, rotation, &trans );
195     
196     //tell sprite object to use the transform
197     spriteobj->SetTransform( &mat );
198 
199     //calculate frame location in source image
200     int fx = (frame % columns) * width;
201     int fy = (frame / columns) * height;
202     RECT srcRect = {fx, fy, fx + width, fy + height};
203 
204     //draw the sprite frame
205     spriteobj->Draw( image, &srcRect, NULL, NULL, color );
206 }
207 
208 //bounding  box collision detection
209 int Collision(SPRITE sprite1, SPRITE sprite2)
210 {
211     RECT rect1;
212     rect1.left = (long)sprite1.x;
213     rect1.top = (long)sprite1.y;
214     rect1.right = (long)sprite1.x + sprite1.width * sprite1.scaling;
215     rect1.bottom = (long)sprite1.y + sprite1.height * sprite1.scaling;
216 
217     RECT rect2;
218     rect2.left = (long)sprite2.x;
219     rect2.top = (long)sprite2.y;
220     rect2.right = (long)sprite2.x + sprite2.width * sprite2.scaling;
221     rect2.bottom = (long)sprite2.y + sprite2.height * sprite2.scaling;
222 
223     RECT dest; //ignored
224     return IntersectRect(&dest, &rect1, &rect2);
225 }
226 
227 
228 bool CollisionD(SPRITE sprite1, SPRITE sprite2)
229 {
230     double radius1, radius2;
231 
232     //calculate radius 1
233     if (sprite1.width > sprite1.height)
234         radius1 = (sprite1.width * sprite1.scaling) / 2.0;
235     else
236         radius1 = (sprite1.height * sprite1.scaling) / 2.0;
237 
238     //center point 1
239     double x1 = sprite1.x + radius1;
240     double y1 = sprite1.y + radius1;
241     D3DXVECTOR2 vector1(x1, y1);
242 
243     //calculate radius 2
244     if (sprite2.width > sprite2.height)
245         radius2 = (sprite2.width * sprite2.scaling) / 2.0;
246     else
247         radius2 = (sprite2.height * sprite2.scaling) / 2.0;
248 
249     //center point 2
250     double x2 = sprite2.x + radius2;
251     double y2 = sprite2.y + radius2;
252     D3DXVECTOR2 vector2(x2, y2);
253 
254     //calculate distance
255     double deltax = vector1.x - vector2.x;
256     double deltay = vector2.y - vector1.y;
257     double dist = sqrt((deltax * deltax) + (deltay * deltay));
258 
259     //return distance comparison
260     return (dist < radius1 + radius2);
261 }
262 
263 bool DirectInput_Init(HWND hwnd)
264 {
265     //initialize DirectInput object
266     DirectInput8Create(
267         GetModuleHandle(NULL), 
268         DIRECTINPUT_VERSION, 
269         IID_IDirectInput8,
270         (void**)&dinput,
271         NULL);
272 
273     //initialize the keyboard
274     dinput->CreateDevice(GUID_SysKeyboard, &dikeyboard, NULL);
275     dikeyboard->SetDataFormat(&c_dfDIKeyboard);
276     dikeyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
277     dikeyboard->Acquire();
278 
279     //initialize the mouse
280     dinput->CreateDevice(GUID_SysMouse, &dimouse, NULL);
281     dimouse->SetDataFormat(&c_dfDIMouse);
282     dimouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
283     dimouse->Acquire();
284     d3ddev->ShowCursor(false);
285 
286     return true;
287 }
288 
289 void DirectInput_Update()
290 {
291     //update mouse
292     dimouse->Poll();
293     if (!SUCCEEDED(dimouse->GetDeviceState(sizeof(DIMOUSESTATE),&mouse_state)))
294     {
295         //mouse device lose, try to re-acquire
296         dimouse->Acquire();
297     }
298 
299     //update keyboard
300     dikeyboard->Poll();
301     if (!SUCCEEDED(dikeyboard->GetDeviceState(256,(LPVOID)&keys)))
302     {
303         //keyboard device lost, try to re-acquire
304         dikeyboard->Acquire();
305     }
306 
307     //update controllers
308     for (int i=0; i< 4; i++ )
309     {
310         ZeroMemory( &controllers[i], sizeof(XINPUT_STATE) );
311 
312         //get the state of the controller
313         XINPUT_STATE state;
314         DWORD result = XInputGetState( i, &state );
315 
316         //store state in global controllers array
317         if (result == 0) controllers[i] = state.Gamepad;
318     }
319 }
320 
321 
322 int Mouse_X()
323 {
324     return mouse_state.lX;
325 }
326 
327 int Mouse_Y()
328 {
329     return mouse_state.lY;
330 }
331 
332 int Mouse_Button(int button)
333 {
334     return mouse_state.rgbButtons[button] & 0x80;
335 }
336 
337 bool Key_Down(int key) 
338 {
339     return (bool)(keys[key] & 0x80);
340 }
341 
342 
343 void DirectInput_Shutdown()
344 {
345     if (dikeyboard) 
346     {
347         dikeyboard->Unacquire();
348         dikeyboard->Release();
349         dikeyboard = NULL;
350     }
351     if (dimouse) 
352     {
353         dimouse->Unacquire();
354         dimouse->Release();
355         dimouse = NULL;
356     }
357 }
358 
359 bool XInput_Controller_Found()
360 {
361     XINPUT_CAPABILITIES caps;
362     ZeroMemory(&caps, sizeof(XINPUT_CAPABILITIES));
363     XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps);
364     if (caps.Type != 0) return false;
365     
366     return true;
367 }
368 
369 void XInput_Vibrate(int contNum, int amount)
370 {
371     XINPUT_VIBRATION vibration;
372     ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
373     vibration.wLeftMotorSpeed = amount;
374     vibration.wRightMotorSpeed = amount; 
375     XInputSetState( contNum, &vibration );
376 }
377 
378 LPD3DXFONT MakeFont(string name, int size)
379 {
380     LPD3DXFONT font = NULL;
381 
382     D3DXFONT_DESC desc = {
383         size,                   //height
384         0,                      //width
385         0,                      //weight
386         0,                      //miplevels
387         false,                  //italic
388         DEFAULT_CHARSET,        //charset
389         OUT_TT_PRECIS,          //output precision
390         CLIP_DEFAULT_PRECIS,    //quality
391         DEFAULT_PITCH,          //pitch and family
392         ""                      //font name
393     };
394 
395     strcpy(desc.FaceName, name.c_str());
396 
397     D3DXCreateFontIndirect(d3ddev, &desc, &font);
398 
399     return font;
400 }
401 
402 void FontPrint(LPD3DXFONT font, int x, int y, string text, D3DCOLOR color)
403 {
404     //figure out the text boundary
405     RECT rect = { x, y, 0, 0 };
406     font->DrawText( NULL, text.c_str(), text.length(), &rect, DT_CALCRECT, color); 
407 
408     //print the text
409     font->DrawText(spriteobj, text.c_str(), text.length(), &rect, DT_LEFT, color); 
410 }
411 
412 bool DirectSound_Init(HWND hwnd)
413 {
414     //create DirectSound manager object
415     dsound = new CSoundManager();
416 
417     //initialize DirectSound
418     HRESULT result;
419     result = dsound->Initialize(hwnd, DSSCL_PRIORITY);
420     if (FAILED(result))
421         return false;
422 
423     //set the primary buffer format
424     result = dsound->SetPrimaryBufferFormat(2, 22050, 16);
425     if (FAILED(result))
426         return false;
427 
428     return true;
429 }
430 
431 void DirectSound_Shutdown()
432 {
433     if (dsound)
434         delete dsound;
435 }
436 
437 CSound* LoadSound(string filename)
438 {
439     HRESULT result;
440 
441     //create local reference to wave data
442     CSound *wave = NULL;
443 
444     //attempt to load the wave file
445     char s[255];
446     sprintf(s, "%s", filename.c_str());
447     result = dsound->Create(&wave, s);
448     if (FAILED(result))
449         wave = NULL;
450 
451     return wave;
452 }
453 
454 void PlaySound(CSound *sound)
455 {
456     sound->Play();
457 }
458 
459 void LoopSound(CSound *sound)
460 {
461     sound->Play(0, DSBPLAY_LOOPING);
462 }
463 
464 void StopSound(CSound *sound)
465 {
466     sound->Stop();
467 }

 

main.cpp

View Code
 1 #include "DirectX.h"
 2 using namespace std;
 3 
 4 bool gameover = false;
 5 
 6 //windows event handling function
 7 LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 8 {
 9     switch (message)
10     {
11     case WM_DESTROY:
12         gameover = true;
13         PostQuitMessage(0);
14         return 0;
15     }
16 
17     return DefWindowProc(hwnd, message, wParam, lParam);
18 }
19 
20 
21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
22 {
23     //set the windows properties
24     WNDCLASSEX wc;
25     wc.cbSize = sizeof(WNDCLASSEX);
26     wc.style = CS_HREDRAW | CS_VREDRAW;
27     wc.lpfnWndProc = (WNDPROC)WinProc;
28     wc.cbClsExtra = 0;
29     wc.cbWndExtra = 0;
30     wc.hInstance = hInstance;
31     wc.hIcon = NULL;
32     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
33     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
34     wc.lpszMenuName = NULL;
35     wc.lpszClassName = APPTITLE.c_str();
36     wc.hIconSm = NULL;
37     RegisterClassEx(&wc);
38 
39     //determine the resolution of the clients desktop screen
40     int screenWidth = GetSystemMetrics(SM_CXSCREEN);
41     int screenHeight = GetSystemMetrics(SM_CYSCREEN);
42 
43     //place the window in the middle of screen
44     int posX = (GetSystemMetrics(SM_CXSCREEN) - SCREENW) / 2;
45     int posY = (GetSystemMetrics(SM_CYSCREEN) - SCREENH) / 2;
46 
47     //Create a window
48     HWND window;
49     window = CreateWindow(APPTITLE.c_str(), APPTITLE.c_str(),
50         WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, posX, posY, SCREENW, SCREENH,
51         NULL, NULL, hInstance, NULL); 
52     if (window == 0)
53         return false;
54 
55     //display the window
56     ShowWindow(window, nCmdShow);
57     UpdateWindow(window);
58 
59     //initialize the game
60     if (!Game_Init(window))
61         return 0;
62 
63     //main message loop
64     MSG msg;
65     while (!gameover)
66     {
67         //process windows events
68         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
69         {
70             //handle any event messages
71             TranslateMessage(&msg);
72             DispatchMessage(&msg);
73         }
74 
75         //process game loop
76         Game_Run(window);
77     }
78 
79     //free game resources
80     Game_End();
81 
82     return msg.wParam;
83 }

 

下面是game.cpp文件,先看开头部分。宏定义D3DFVF_MYVERTEX表示顶点流的类型,D3DFVF_XYZ与D3DFVF_TEX1分别代表顶点位置与纹理坐标。这里的cube数组是立方体的顶点信息,由6个面组成。

 1 #include "DirectX.h"
 2 using namespace std;
 3 
 4 const string APPTITLE = "Direct3D Cube";
 5 const int SCREENW = 1024;
 6 const int SCREENH = 576;
 7 
 8 DWORD screentimer = timeGetTime();
 9 
10 //vertex and quad definitions
11 #define D3DFVF_MYVERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
12 
13 struct VERTEX
14 {
15     float x, y, z;
16     float tu, tv;
17 };
18 
19 struct QUAD
20 {
21     VERTEX vertices[4];
22     LPDIRECT3DVERTEXBUFFER9 buffer;
23     LPDIRECT3DTEXTURE9 texture;
24 };
25 
26 VERTEX cube[] = {
27     {-1.0f, 1.0f,-1.0f, 0.0f,0.0f},     //side 1
28     { 1.0f, 1.0f,-1.0f, 1.0f,0.0f },
29     {-1.0f,-1.0f,-1.0f, 0.0f,1.0f },
30     { 1.0f,-1.0f,-1.0f, 1.0f,1.0f },
31     
32     {-1.0f, 1.0f, 1.0f, 1.0f,0.0f },    //side 2
33     {-1.0f,-1.0f, 1.0f, 1.0f,1.0f },
34     { 1.0f, 1.0f, 1.0f, 0.0f,0.0f },
35     { 1.0f,-1.0f, 1.0f, 0.0f,1.0f },
36     
37     {-1.0f, 1.0f, 1.0f, 0.0f,0.0f },    //side 3
38     { 1.0f, 1.0f, 1.0f, 1.0f,0.0f },
39     {-1.0f, 1.0f,-1.0f, 0.0f,1.0f },
40     { 1.0f, 1.0f,-1.0f, 1.0f,1.0f },
41     
42     {-1.0f,-1.0f, 1.0f, 0.0f,0.0f },    //side 4
43     {-1.0f,-1.0f,-1.0f, 1.0f,0.0f },
44     { 1.0f,-1.0f, 1.0f, 0.0f,1.0f },
45     { 1.0f,-1.0f,-1.0f, 1.0f,1.0f },
46 
47     { 1.0f, 1.0f,-1.0f, 0.0f,0.0f },    //side 5
48     { 1.0f, 1.0f, 1.0f, 1.0f,0.0f },
49     { 1.0f,-1.0f,-1.0f, 0.0f,1.0f },
50     { 1.0f,-1.0f, 1.0f, 1.0f,1.0f },
51     
52     {-1.0f, 1.0f,-1.0f, 1.0f,0.0f },    //side 6
53     {-1.0f,-1.0f,-1.0f, 1.0f,1.0f },
54     {-1.0f, 1.0f, 1.0f, 0.0f,0.0f },
55     {-1.0f,-1.0f, 1.0f, 0.0f,1.0f }
56 };
57 
58 QUAD *quads[6];
59 D3DXVECTOR3 cameraSource;
60 D3DXVECTOR3 cameraTarget;

 

 函数SetVertex调用SetPosition设置四边形的顶点信息。CreateVertex用于创建顶点。

 1 void SetPosition(QUAD* quad, int ivert, float x, float y, float z)
 2 {
 3     quad->vertices[ivert].x = x;
 4     quad->vertices[ivert].y = y;
 5     quad->vertices[ivert].z = z;
 6 }
 7 
 8 void SetVertex(QUAD *quad, int ivert, float x, float y, float z, float tu, float tv)
 9 {
10     SetPosition(quad, ivert, x, y, z);
11     quad->vertices[ivert].tu = tu;
12     quad->vertices[ivert].tv = tv;
13 }
14 
15 VERTEX CreateVertex(float x, float y, float z, float tu, float tv)
16 {
17     VERTEX vertex;
18     vertex.x = x;
19     vertex.y = y;
20     vertex.z = z;
21     vertex.tu = tu;
22     vertex.tv = tv;
23     
24     return vertex;
25 }

 

在下面的CreateQuad函数中,首先使用D3DXCreateTextureFromFile用图片文件创建了纹理,然后赋值给quad->texture。接着用CreateVertexBuffer创建了顶点缓存并赋值给quad->buffer,quad由3部分组成,下面就剩下顶点信息vertices了。下面对quad->vertices进行了默认的初始化工作,我们实际使用的顶点数据来源于之前的cube数组。

 1 QUAD* CreateQuad(char *textureFilename)
 2 {
 3     QUAD *quad = (QUAD*)malloc(sizeof(QUAD));
 4 
 5     //load the texture
 6     D3DXCreateTextureFromFile(d3ddev, textureFilename, &quad->texture);
 7 
 8     //create vertex buffer for quad
 9     d3ddev->CreateVertexBuffer(
10         4*sizeof(VERTEX),
11         0,
12         D3DFVF_MYVERTEX, D3DPOOL_DEFAULT,
13         &quad->buffer, NULL);
14 
15     //create the four corners of this quad triangle strip
16     quad->vertices[0] = CreateVertex(-1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
17     quad->vertices[1] = CreateVertex(1.0f, 1.0f, 0.0f, 1.0f, 0.0f);
18     quad->vertices[2] = CreateVertex(-1.0f, -1.0f, 0.0f, 0.0f, 1.0f);
19     quad->vertices[3] = CreateVertex(1.0f, -1.0f, 0.0f, 1.0f, 1.0f);
20 
21     return quad;
22 }
23 
24 void DeleteQuad(QUAD *quad)
25 {
26     if (quad == NULL)
27         return;
28 
29     if (quad->buffer != NULL)
30         quad->buffer->Release();
31 
32     if (quad->texture != NULL)
33         quad->texture->Release();
34 
35     free(quad);
36 }

 

下面的函数DrawQuad用于绘制四边形。首先,需要把初始化好的顶点信息传递给顶点缓存,这里定义了一个临时指针变量temp,然后调用lock函数来锁住顶点缓冲区,同时使用temp指向了顶点缓存。然后,就可以用memcpy给这个代表顶点缓存的temp赋值了,最后要Unlock才行。接着用SetTexture设置使用的纹理,用SetStreamSource设置流源,使Direct3D知道顶点的来源以及需要渲染多少顶点。最后,绘制流源指定的基元(primitive),即基础图元。在DirectX中,基础图元只有点、线、三角形,这里使用三角形条的方式绘制四边形。传入的参数0代表索引,2代表两个三角形,而参数TRIANGLESTRIP也能换成TRIANGLELIST,只是绘制方式不同,下面就是区别。三角形列表需要6个顶点来绘制一个四边形,而三角形条只需要4个顶点信息。

 1 void DrawQuad(QUAD *quad)
 2 {
 3     //fill vertex buffer with quad vertices
 4     void *temp = NULL;
 5     quad->buffer->Lock(0, sizeof(quad->vertices), (void**)&temp, 0);
 6     memcpy(temp, quad->vertices, sizeof(quad->vertices));
 7     quad->buffer->Unlock();
 8 
 9     //draw the textured dual triangle strip
10     d3ddev->SetTexture(0, quad->texture);
11     d3ddev->SetStreamSource(0, quad->buffer, 0, sizeof(VERTEX));
12     d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
13 }

 

SetIdentity函数最后使用了SetTransform,下面还会使用,这个函数用于在渲染之前进行矩阵转换,你不妨再看看前面的世界变换、视图变换、投影变换这些概念。D3DXMatrixTranslation用于设置平移矩阵,将设置的矩阵信息保存在矩阵matWorld中。D3DTS_WORLD是一个宏定义,这段就表示进行世界变换的操作。

 1 void SetIdentity()
 2 {
 3     //set default position, scale, rotation
 4     D3DXMATRIX matWorld;
 5     D3DXMatrixTranslation(&matWorld, 0.0f, 0.0f, 0.0f);
 6     d3ddev->SetTransform(D3DTS_WORLD, &matWorld);
 7 }
 8 
 9 void ClearScene(D3DXCOLOR color)
10 {
11     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, color, 1.0f, 0);
12 }

 

下面的SetCamera函数用于设置相机信息,显然这里的相机是虚拟的,用视图变换来表示。看到D3DXMatrixLookAtLH这个重要的函数,这个函数最终得到一个用于视图变换的观察矩阵matView,参数cameraSource表示相机的位置(观察点),cameraTarget表示相机朝着什么方向看的向量,updir代表当前世界方向向上的向量,默认为(0, 1, 0)。最后,通过SetTransform(D3DTS_VIEW,&matView)进行了视图变换。

 1 void SetCamera(float x, float y, float z, float lookx, float looky, float lookz)
 2 {
 3     D3DXMATRIX matView;
 4     D3DXVECTOR3 updir(0.0f, 1.0f, 0.0f);
 5 
 6     //move the camera
 7     cameraSource.x = x;
 8     cameraSource.y = y;
 9     cameraSource.z = z;
10 
11     //point the camera
12     cameraTarget.x = lookx;
13     cameraTarget.y = looky;
14     cameraTarget.z = lookz;
15 
16     //set up the camera view matrix
17     D3DXMatrixLookAtLH(&matView, &cameraSource, &cameraTarget, &updir);
18     d3ddev->SetTransform(D3DTS_VIEW, &matView);
19 }

 

最后还有一个投影变换,使用SetTransform(D3DTS_PROJECTION, &matProj)完成,Projection就是投影的意思。参数fieldOfView代表视景体,aspectRatio代表图像的宽高比,nearRange与farRange代表近平面与远平面。

1 void SetPerspective(float fieldOfView, float aspectRatio, float nearRange, float farRange)
2 {
3     //set the perspective so things in the distance will look smaller
4     D3DXMATRIX matProj;
5     D3DXMatrixPerspectiveFovLH(&matProj, fieldOfView, aspectRatio, nearRange, farRange);
6     d3ddev->SetTransform(D3DTS_PROJECTION, &matProj);
7 }

 

下面这个函数用于初始化立方体,调用CreateQuad函数创建了带纹理贴图的四边形,然后用cube数组对这些四边形的顶点赋值。

 1 void Init_Cube()
 2 {
 3     for (int q = 0; q < 6; q++)
 4     {
 5         int i = q*4;
 6         quads[q] = CreateQuad("cube.bmp");
 7         for (int v = 0; v < 4; v++)
 8         {
 9             quads[q]->vertices[v] = CreateVertex(
10                 cube[i].x, cube[i].y, cube[i].z,
11                 cube[i].tu, cube[i].tv);
12             i++;
13         }
14     }
15 }

 

我们还要做一件事,就是让立方体旋转起来。在Rotate_Cube中,xrot、yrot、zrot组成了旋转需要的旋转轴,而我们对3D物体进行旋转还需要进行旋转的旋转矩阵matRot。这里还设置了平移矩阵matTrans,不过这里暂时没有平移。函数D3DXMatrixRotationYawPitchRoll用于创建旋转矩阵,最后用matRot*matTrans来得到一个组合了平移与旋转的矩阵matWorld。

 1 void Rotate_Cube()
 2 {
 3     static float xrot = 0.0f;
 4     static float yrot = 0.0f;
 5     static float zrot = 0.0f;
 6 
 7     //rotate the x and y axes
 8     xrot += 0.2f;
 9     yrot += 0.00f;
10 
11     //create the matrices
12     D3DXMATRIX matWorld;
13     D3DXMATRIX matTrans;
14     D3DXMATRIX matRot;
15 
16     //get an identity matrix
17     D3DXMatrixTranslation(&matTrans, 0.0f, 0.0f, 0.0f);
18 
19     //rotate the cube
20     D3DXMatrixRotationYawPitchRoll(&matRot,D3DXToRadian(xrot),D3DXToRadian(yrot),D3DXToRadian(zrot));
21     matWorld = matRot * matTrans;
22 
23     //complete the operation
24     d3ddev->SetTransform(D3DTS_WORLD, &matWorld);
25 }

 

下面是Game_Init函数,可以直接从SetCamera开始看起。如果改变SetCamera函数的参数,就能使图像放大与缩小。

 1 bool Game_Init(HWND window)
 2 {
 3     srand(time(NULL));
 4 
 5     //initialize Direct3D
 6     if (!Direct3D_Init(window, SCREENW, SCREENH, false))
 7     {
 8         MessageBox(window,"Error initializing Direct3D",APPTITLE.c_str(),0);
 9         return false;
10     }
11 
12     //initialize DirectInput
13     if (!DirectInput_Init(window))
14     {
15         MessageBox(window,"Error initializing DirectInput",APPTITLE.c_str(),0);
16         return false;
17     }
18 
19     //initialize DirectSound
20     if (!DirectSound_Init(window))
21     {
22         MessageBox(window,"Error initializing DirectSound",APPTITLE.c_str(),0);
23         return false;
24     }
25 
26     //position the camera
27     SetCamera(0.0f, 2.0f, -4.0f, 0, 0, 0);
28 
29     float ratio = (float)SCREENW / (float)SCREENH;
30     SetPerspective(45.0f, ratio, 0.1f, 10000.0f);
31 
32     //turn dynamic lighting off, z-buffer on
33     d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
34     d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);
35 
36     //set the Direct3D stream to use the custom vertex
37     d3ddev->SetFVF(D3DFVF_MYVERTEX);
38 
39     //convert the cube values into quads
40     Init_Cube();
41 
42     return true;
43 }

 

函数Game_Run变得很简单,先Rotate_Cube使立方体产生旋转效果,接着在for循环中DrawQuad绘制立方体的6个面就可以了。

 1 void Game_Run(HWND window)
 2 {
 3     if (!d3ddev) return;
 4     DirectInput_Update();
 5     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
 6 
 7     //slow rendering to approximately 60 fps
 8     if (timeGetTime() > screentimer + 14)
 9     {
10         screentimer = GetTickCount();
11 
12         Rotate_Cube();
13 
14         //start rendering
15         if (d3ddev->BeginScene())
16         {
17            for (int n = 0; n < 6; n++)
18            {
19                DrawQuad(quads[n]);
20            }
21 
22             //stop rendering
23             d3ddev->EndScene();
24             d3ddev->Present(NULL, NULL, NULL, NULL);
25         }
26     }
27 
28     //exit with escape key or controller Back button
29     if (KEY_DOWN(VK_ESCAPE)) gameover = true;
30     if (controllers[0].wButtons & XINPUT_GAMEPAD_BACK) gameover = true;
31 
32 }

 

最后是Game_End函数

1 void Game_End()
2 {
3     for (int q = 0; q < 6; q++)
4         DeleteQuad(quads[q]);
5     DirectSound_Shutdown();
6     DirectInput_Shutdown();
7     Direct3D_Shutdown();
8 }

 

本文只是用DirectX 9的知识简单的绘制了一个立方体,如果不使用纹理图片,直接使用顶点颜色信息也是可以的。下面是运行截图,源代码,参考自游戏编程入门。

 

原文地址:https://www.cnblogs.com/shihui142857/p/2939538.html