1. Trang chủ
  2. » Công Nghệ Thông Tin

Advanced 3D Game Programming with DirectX - phần 3 pot

71 414 0

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

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 71
Dung lượng 558,47 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Sample code to create the sound interface appears in the following: LPDIRECTSOUND8 m_pDSound = 0; // Create IDirectSound using the primary sound device hr = DirectSoundCreate8 NULL,

Trang 1

HRESULT WINAPI DirectSoundCreate8(

LPCGUID lpcGuid,

LPDIRECTSOUND8 * ppDS,

LPUNKNOWN pUnkOuter

);

lpcGuid A pointer to a GUID that describes the device you wish to create While you can

enumerate all of the sound devices with DirectSoundEnumerate, generally there is only one sound card on a machine To get the default device (which is what you want, usually), set this to NULL

ppDS A pointer to an LPDIRECTSOUND8 interface pointer that will be filled with a valid

interface pointer if the function succeeds

pUnkOuter Used for COM aggregation; leave this as NULL

Sample code to create the sound interface appears in the following:

LPDIRECTSOUND8 m_pDSound = 0;

// Create IDirectSound using the primary sound device

hr = DirectSoundCreate8( NULL, &m_pDSound, NULL );

if( FAILED( hr ) )

{

// Handle critical error

}

Setting the Cooperative Level

After you acquire the interface pointer, the next step is to declare how cooperative you intend on being Just like DirectInput, this is done using the SetCooperativeLevel command

Trang 2

hwnd Handle to the window to be associated with the DirectSound object This should be the

primary window

dwLevel One of the following flags, describing the desired cooperative level

DSSCL_EXCLUSIVE—Grab exclusive control of the sound device When the application has focus, it is the only audible application

DSSCL_NORMAL—Smoothest, yet most restrictive cooperative level The primary format cannot be changed This is the cooperative level the sound layer uses

DSSCL_PRIORITY—Like DDSCL_NORMAL except the primary format may be changed

DSSCL_WRITEPRIMARY—This is the highest possible priority for an application

to have It can't play any secondary buffers, and it has the ability to manually mangle the bits of the primary buffer Only for the extremely hardcore!

This code will be changing the primary format of the sound buffer, so I'll go ahead and set this to

DSSCL_PRIORITY Sample code to do this appears in the following:

// pDSound is a valid LPDIRECTSOUND8 object

HRESULT hr = pDSound->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );

if( FAILED( hr ) )

{

/* handle error */

}

Grabbing the Primary Buffer

Since the sound layer sets the cooperative level's priority, it can do some crazy things like change the format of the primary buffer Generally it's best to set the primary buffer to the same format that all of your secondary buffers will be in; this makes the mixer's job easier, as it doesn't have to resample any sound effects to be able to mix them into the primary buffer You can imagine what would happen if you tried to play a 22 KHz sound effect in a 44 KHz buffer without resampling: You would run out of samples twice as soon as you would expect, and the sound effect would have sort of a chipmunkish quality to it

To change the format of the primary buffer, you just need to grab it using CreateSoundBuffer, fill out a new format description, and set it using the SetFormat() method on the primary buffer Listing 4.2 has code that sets the primary format to 22 KHz, 16-bit stereo

Listing 4.2: Sample code to change the format of the primary buffer

Trang 3

// pDSound is a valid LPDIRECTSOUND object

LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;

wfx.nBlockAlign = wfx.wBitsPerSample/8* wfx.nChannels;

wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

Trang 4

* copyright (c) 2002 by Peter A Walsh and Adrian Perez

* See license.txt for modification and distribution information

LPDIRECTSOUNDBUFFER8 m_pPrimary; // primary mixer

static cSoundLayer* m_pGlobalSLayer;

Trang 6

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* Desc: Sample application for Direct3D

*

* copyright (c) 2002 by Peter A Walsh and Adrian Perez

* See license.txt for modification and distribution information ******************************************************************/

LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;

// Create IDirectSound using the primary sound device

hr = DirectSoundCreate8( NULL, &m_pDSound, NULL );

if( FAILED( hr ) )

{

Trang 7

throw cGameError( "DirectSoundCreate failed!" );

}

// Set coop level to DSSCL_PRIORITY

hr = m_pDSound->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); if( FAILED( hr ) )

wfx.nBlockAlign = wfx.wBitsPerSample/8* wfx.nChannels;

wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

Trang 8

if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )

The cSound Class

To help facilitate the creation and playback of secondary buffers, I constructed an encapsulation class called cSound A cSound object can be constructed either from a filename or from another cSound object The copy constructor uses a ref-counting map so that all cSounds based on the same WAV file use the same CWaveSoundRead object The overhead of the map could have been avoided if the CWaveSoundRead code was changed to accommodate the needed functionality, but I felt it was better

to leave the code unchanged from the DirectX SDK

Without any further ado, let's just dive into the code The details of how this code works isn't terribly interesting but have a look through it anyway to get accustomed to it

* copyright (c) 2002 by Peter A Walsh and Adrian Perez

* See license.txt for modification and distribution information

Trang 9

* Multiple sounds that use the same

* file shouldn't reread it, they should

* share the CWSR object This map

* implements rudimentary reference counting

* I would have just changed CWaveSoundRead,

* but I wanted to keep it unchanged from the

Trang 10

cSound& operator=( const cSound &in );

* copyright (c) 2002 by Peter A Walsh and Adrian Perez

* See license.txt for modification and distribution information ******************************************************************/

#include "stdafx.h"

#include "WavRead.h"

#include "Sound.h"

using std::map;

Trang 11

map< CWaveSoundRead*, int > cSound::m_waveMap;

cSound::cSound( char* filename )

{

m_pWaveSoundRead = NULL;

m_pBuffer = NULL;

// Create a new wave file class

m_pWaveSoundRead = new CWaveSoundRead();

m_waveMap[ m_pWaveSoundRead]=1;

// Load the wave file

if( FAILED( m_pWaveSoundRead->Open( filename))) {

throw cGameError("couldn't open file!"); }

Trang 12

* Destroy the old object

Trang 13

* Set up the DirectSound surface The size of the sound file

* and the format of the data can be retrieved from the wave

* sound object Besides that, we only set the STATIC flag,

* so that the driver isn't restricted in setting up the

// Create the sound buffer

hr = Sound()->GetDSound()->CreateSoundBuffer( &dsbd, &pTempBuffer, NULL );

Trang 14

pTempBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer );

Trang 15

/**

* Chances are, we got here because the app /just/

* started, and DirectSound hasn't given us any

* control yet Just spin until we can restore

uchar* pbWavData; // Pointer to actual wav data

uint cbWavSize; // Size of data

void* pbData = NULL;

void* pbData2 = NULL;

ulong dwLength;

ulong dwLength2;

/**

Trang 16

* How big the wav file is

pbWavData = new uchar[ nWaveFileSize ];

if( NULL == pbWavData )

Trang 17

0, m_bufferSize, &pbData, &dwLength,

memcpy( pbData, pbWavData, m_bufferSize );

m_pBuffer->Unlock( pbData, m_bufferSize, NULL, 0 );

Trang 18

DWORD dwLooped = bLoop ? DSBPLAY_LOOPING : 0L;

if( FAILED( hr = m_pBuffer->Play( 0, 0, dwLooped)))

Application: DirectSound Sample

Adrian, the lead author of the DirectX 7.0 version of this book, has a few interesting hobbies As part of

an ongoing effort, he does extracurricular activities that actually have nothing to do with programming One of them is an a cappella group that he sings bass in One of his jobs in the a cappella group is to take care of some of the vocal percussion

A cappella music can't have any sort of accompaniment, so any percussion needs to be done with the human voice This has spawned an entire subculture of vocal percussionists, each trying to make that

Trang 19

perfect snare sound or cymbal crash using only their mouths The DirectSound sample for this chapter was created using Adrian's unique vocal abilities

When you load the file DSSAMPLE from the companion files, you're presented with a small window that lists six different vocal percussion sounds The keys 1 through 6 play each of the sounds, and you can press multiple keys simultaneously to play multiple sounds

You'll note that I didn't show you a DirectInput sample, because I figured it would be better to roll DirectSound and DirectInput into one sample DirectInput is used to capture the keystrokes With some practice you can get a pretty swank beat going The code behind the sample appears in Listing 4.7

Listing 4.7: The vocal percussion DirectSound sample app

* copyright (c) 2002 by Peter A Walsh and Adrian Perez

* See license.txt for modification and distribution information

Trang 20

public:

void PlaySound( int num );

//========== - cApplication

virtual void DoFrame( float timeDelta );

virtual void SceneInit();

virtual void KeyUp( int key );

virtual void KeyDown( int key );

Trang 22

* for a sound that isn't currently playing

DP("spawning a new sound\n");

cSound* pNew = new cSound( *m_sounds[num][0] );

string help;

help += "DirectSound Sample application\n";

help += "Vocal Percussion with Adrian Perez\n";

Trang 23

help += " [1]: Keg drum\n";

help += " [2]: Crash 1\n";

help += " [3]: Crash 2\n";

help += " [4]: Bass drum\n";

help += " [5]: Snare drum\n";

// Tell Direct3D we are done rendering

Trang 26

And that, ladies and gentlemen, is DirectSound You should be able to build on top of the code I have shown you in this chapter to create the perfect acoustic accompaniment to your own projects without too much difficulty

Now it's time to focus on the 3D mathematics that you need to start exploring the next dimension with Direct3D What is the matrix? You're about to find out…

When you really get down to it, using 3D graphics is an exercise in math Some of the math can be intensely interesting; some of it can be seriously dull It all depends on the eye of the beholder Love it

or hate it, however, you still have to learn it A solid foundation in math is a requirement if you want to be

a successful 3D coder Don't worry, though; I'll try to keep this chapter as interesting as possible

Points

Let's start with the most basic of basic primitives: the 3D point, or vector Points are paramount in 3D

graphics The vertices of objects, the objects' locations and directions, and their velocities/forces are all described with 3D points Three-dimensional objects have width, height, and depth, which are

represented with the shorthand components x (width), y (height), and z (depth) Points and vectors,

when used in equations, are referred to as vector quantities, while regular numbers are referred to as

scalar quantities In this book I'll refer to 3D vectors with lower-case, boldface letters (examples would

be v and p) The three components are written separated by commas like this: <x,y,z> They are also

represented as a single row matrix (or, equivalently, a transposed single column matrix) If you're

unfamiliar with the concept of matrices, have patience: I'll get there soon At the right is an example of how points are represented using matrix notation:

Note

In the book I use the terms "point" and "vector" interchangeably They loosely mean the same thing A point is a location in 3D space, and a vector is a line that goes from the origin to a location in 3D space For all the math that I'm discussing in this book, they can be used interchangeably

Here are a few examples of three-dimensional points It's difficult to represent the dimensions on paper,

so please be tolerant of the illustrations used in this book

Trang 27

Figure 5.1: Examples of 3D vectors

3D points are graphed in a way analogous to the 2D Cartesian coordinate system There are three principal axes stretching off into infinity in both directions These are the x, y, and z axes They meet at

the origin, a specific point that represents the center of the current coordinate system (typically you have

several coordinate systems to worry about, but I'll leave this until later) The coordinates of the origin are, of course, <0,0,0>

Which way do the axes point? In some systems (for example, some 3D modelers like 3D Studio Max), x increases to the right, y increases forward (into the page), and z increases up These directions are all dependent on the orientation of the viewer of the scene My choice of axes direction is the one used in most 3D games: x increases to the right, y increases up, and z increases forward, into the monitor (or away from you if that makes it clearer)

Note

This book uses a left-handed coordinate space, where x increases to the right, y increases up, and z increases forward (into the screen) In right-handed coordinate systems, z increases coming out of the screen

A point always exists some distance away from the origin of the coordinate space; this quantity is called

the magnitude of the vector (or, more intuitively, the length of the vector) To compute the magnitude of

vectors in 2D, you use the Pythagorean theorem:

Luckily, the Pythagorean theorem extends into 3D to measure the length of 3D vectors by simply adding the extra z component into the equation You can see that the 2D Pythagorean equation is simply a special case of the 3D equation where the z-distance from the origin is zero

Trang 28

There is a shorthand notation used to denote the magnitude of a vector when used in more complex equations The notation is the vector surrounded on both sides by double vertical lines The equation for

vector length, given a vector v with components x, y, and z is:

A special type of vector is one that has a length of 1 This type of vector is called a unit vector Each unit vector touches a point on what is called the unit sphere, a conceptual sphere with radius 1, situated at

the origin

It's often the case that you want a unit-length version of a given vector For example, the unit-length

version n of a given vector m would be:

For simplicity's sake, however, I'll introduce some shorthand notation The same equation can be

represented by putting a bar over m to signify the unit-length version:

There are three specific unit vectors that represent the directions along the three primary axes: i

<1,0,0>, j <0,1,0>, and k <0,0,1>

Figure 5.2: The i, j, and k vectors

Many physics texts use the i, j, and k vectors as primitives to describe other 3D vectors, so it is worth mentioning it here Any point in 3D can be represented as a linear combination of the i, j, and k vectors

You can define any vector as a sum of the scalar components with the three principal vectors For

example, if you had the 3D vector a = <3,5,2>, you could represent it like this:

Trang 29

This trait will become more important later on, when I discuss matrices and the spaces they represent

Aside

While it isn't really pertinent to the level of expertise you need to reach in this book, the concept of a linear combination is important when talking about spaces and

transformations

Given n vectors b0 bn−1, any vector v is a linear combination of the set of the vectors if

the following equation can be satisfied:

where k0 kn−1 are scalars

That is, if you want to get to v, you can start at the origin and walk along any or all of the vectors some amount and reach v

You can say the set of b vectors is linearly independent if no single b vector is a linear

combination of the others

The point3 Structure

It is always useful to design a class to encapsulate a generic 3D point The class name I use is point3 Unlike most of the other classes you have seen so far, the intent of the point3 structure is to act as a mathematical primitive like float or int The 3 suffix denotes the dimension of the point I'll also define 2D and 4D versions of points, which are named point2 and point4, respectively

Listing 5.1: The point3 structure (defined in point3.h)

Trang 30

// Default constructor

point3(){}

// Construct a point with 3 given inputs

point3( float X, float Y, floatZ):

Aside

The non-default constructor uses initialization lists C++ classes should use these whenever possible They clarify the intent of the code to the compiler, which lets it do its job better (it has a better chance to inline the code, and the code will end up being considerably more efficient, especially for complex structures)

Finally, you may wonder why I'm choosing floats (32 bits/4 bytes) instead of doubles (64 bits/8 bytes) or

long doubles (80 bits/10 bytes) Well, I could just implement the point as a template class, but there are

too many other interactions with other classes to complicate the code that much Using it as a template

in a way defeats the concept of using the point as a generic primitive, especially since there is a space

of only three types I would use

Doubles and long doubles are slower than floats, about twice as slow for things like divides (19 versus

39 cycles), and on top of that they require twice the space The added precision really isn't important unless you really need a wide range of precision Within a few years worlds will be big enough and model resolution will be fine enough that you may need to employ larger floating-point resolutions to get the job done Until then I'd suggest sticking with traditional floats

Basic point3 Functions

Trang 31

The point3 structure is pretty lame right now All it can do is construct structures! To spice it up, I'll add some member functions to help perform some basic operations on 3D points, and explain what they are used for

Assign

Setting a point to a certain value is a common task It could be done explicitly in three lines, setting the

x, y, and z values separately However, for simplicity's sake, it's easier to set them all at once, with a single function call This is also better than just creating a new variable on the stack with a point3 constructor; it's more efficient to reuse stack variables whenever possible The code to do this appears

in Listing 5.2

Listing 5.2: point3::Assign

// Reassign a point without making a temporary structure

inline void point3::Assign( float X, float Y, float Z )

Mag and MagSquared

The function Mag uses the 3D version of the Pythagorean theorem mentioned previously to calculate the length of the point structure (the distance from the point to the origin) The code appears in Listing 5.3

Trang 32

Sometimes you want the squared distance (for example, when calculating the attenuation factor for a point-source light) Rather than computing the expensive square root and squaring it, you can avoid the cost and simply make an extra function to do it for you, which appears in Listing 5.4

Dist is a static function that calculates the distance between two point structures Conceptually, it finds

the vector that connects them (which is the vector b–a) and computes its length The code appears in

Listing 5.6

Listing 5.6: point3::Dist

inline static float point3::Dist( const point3 &a, const point3 &b )

Trang 33

Addition/Subtraction

Vector addition and subtraction are useful in moving points around in 3D Conceptually, adding a vector

to another moves the location of the first vector in the direction of the second Figure 5.3 shows what the result of vector addition looks like, and Figure 5.4 shows the result of vector subtraction

Figure 5.3: Vector addition example

Trang 34

Figure 5.4: Vector subtraction example

In many respects, vector addition/subtraction is incredibly similar to the normal scalar addition that I'm sure you know and love For example, if you wanted to find the average location of a set of vectors, you simply add them together and divide the result by the number of vectors added, which is, of course, the same averaging formula used for scalars

The code for adding/subtracting vectors is equally similar to their scalar cousins: Simply add (or

subtract) each component together separately I'll give the + and − operators; in the code you'll find the += and −= operators

Listing 5.7: Addition and subtraction operators for point3

inline point3 operator+(point3 const &a, point3 const &b)

Trang 35

Figure 5.5: Multiplying/dividing vectors by scalars

Doing this in code is easy enough; just multiply (or divide) each component in the vector by the provided scalar Listing 5.8 has the * and / operators; the *= and /= operators are defined in the header Note that

I defined two multiplicative operators; one for vector * scalar and another for scalar * vector

Listing 5.8: Scalar multiplication/division operators for point3

inline point3 operator*(point3 const &a, float const &b)

{

return point3

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

TỪ KHÓA LIÊN QUAN