下面将学习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
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
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
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
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, ¢er, 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
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的知识简单的绘制了一个立方体,如果不使用纹理图片,直接使用顶点颜色信息也是可以的。下面是运行截图,源代码,参考自游戏编程入门。