« March 2005 | Main | July 2005 »

May 29, 2005

BASS Sound Library (Crashing is Bad)

That project I'm working on in the moonlit hours specifies that the BASS sound library be used.

Seems like a pretty adequate tool for playing WAV, MP3, OGG, MOD, MIDI, and probably other formats that I can't even recall at this moment.

One comment I heard about "real hackers" was that they didn't like closed source tools. When a problem turns up in one of the libraries that you've licensed, a real hacker will want to get into the code, find the problem, fix it, and get back to work. Closed source libraries prevent this from happening. (The original author of this comment was blurring the definition of "open source" a bit, talking about libraries distributed in binary form alone, but you get the idea.)

I am reminded of this just now, because I have a BASS DLL and no source code. I've been seeing crashes when running in the debugger that seem to be happening in the DLL, and not in my code.

I'm of the opinion that if you're going to sell your code, it shouldn't crash. If it does crash, consider allowing the licensees to see the code so that they can help you fix it.

My current workaround is to limit the number of channels I have going at once. There may be a hardware maximum, but it seems like I shouldn't have to care about that - any extra sounds that can't be played should trigger an error condition. An error condition that higher level code can catch and respond to. Crashing is bad.

This is almost as bad as the case that an ex-coworker once found himself in - he was working on a console game, and they had a bug where the game could not go on. It wasn't precisely a crash, because the code was still running, but maybe the player control didn't work anymore, so there was nothing for the player to do but reboot the system. The crazy thing is that from within the game they could detect, but not fix, this phenomenon. So they hacked in a faux easter egg screen saying "Congratulations! You have discovered the secret bonus! Reboot your system to go back to the game!"

Posted by tsmaster at 10:47 PM | Comments (2)

May 28, 2005

Using ID3DXSprite

I'm currently working on a 2d game that mostly wants to draw unrotated rectangular images with one-bit (cookie cutter) alpha. Occasionally, though, the design requires that some of these images rotate on the screen (within the plane of the screen, nothing too fancy... yet). One route that I've taken down this path previously is to use OpenGL with an orthographic projection, but for reasons outside my control, OpenGL isn't an option for this project.

Back in earlier versions of DirectX, you might use DirectDraw for 2D games. But these days, DirectDraw and D3D are integrated, so I'm using Direct3D.

Since I've got the fullon 3D pipe available to me, I could certainly draw textured quads to the screen, similar to what I did with "Cars With Guns", but this seems like overkill when most of what I want to do is a simple copy of the image information, unmodified, from the source texture to the screen. In particular, I want the graphics to show up pixel-accurate on the screen without antialiasing or filtering.

In my research, I found CodeSampler.com, which is a terrific source for examples. At first, I was planning to use DirectX 9.0c, and the ID3DX sample got me running, giving me a simple interface for drawing sprites to the screen, with or without rotation, with scaling and other fancy stuff should I make that decision.

One thing that happens in just about any software project is changing requirements. I discovered after getting a working version of the game up that the game had to work on stock Windows XP machines, which meant dropping back to DX8.1.

Fortunately, DX8.1 has support for ID3DXSprite. Unfortunately, the interface is just about completely different. The MSDN documentation for each version, 8.1 and 9.0, is of no help.

    /* during device creation */
    D3DXCreateSprite( g_pd3dDevice, &g_pSpriteContext );


    /* during render */
    g_pSpriteContext->Begin();

    D3DCOLOR c = D3DCOLOR_COLORVALUE(1.0f,
                                     1.0f,
                                     1.0f,
                                     1.0f);
    
    float px=(pSpr->right+pSpr->left)/2.0f;
    float py=(pSpr->top+pSpr->bottom)/2.0f;
        
    float ox=pSpr->right-px;
    float oy=pSpr->bottom-py;
        
    D3DXVECTOR2 scaling(1.0, 1.0);
    D3DXVECTOR2 rotCenter(ox,oy);
			  
    D3DXVECTOR2 translate(pSpr->posX-ox,
                          pSpr->posY-oy);
    
    g_pSpriteContext->Draw(pSpr->textureHandle,
                          &pSpr->srcRect,
                          &scaling,
                          &rotCenter,
                          pSpr->angle,
                          &translate,
                          c);
The code above is a rough sketch of how I use the code. I begin by creating a "sprite context object" - my own term, as calling it a "sprite object" is misleading. I create my sprite object just after I have acquired the device.

Later on, when I'm rendering my scene, first I call Begin() on my context object, at which point, I'm ready to draw the individual sprites.

First, I create a color for the sprite. In this case, I want the original colors to be used, so I use a solid white color.

Next, I set px, py to the center of the sprite within the texture. It's important to understand that the intended use of ID3DXSprite is to draw rectangular sections of source art to the screen, so several of the arguments to Draw() are relative to the upper left corner of the texture.

Second, I set ox, oy to be the "half size" of the sprite (or 'offset', if you prefer). This is the distance from upper left corner of the sprite to the center.

I create a scaling vector, but I don't currently make use of it - so I leave it at a neutral value, (1.0, 1.0).

Next, rotCenter gets the half size offset value, specifying the point around which the rotation should be centered. For my purposes, the center of the bounding box of the sprite is a good point to center the rotation about, but for odd-sized objects (like a fryingpan) you might want to set the rotation center to be somewhere closer to one of the corners.

The last part of the setup is to create the vector describing the location where the sprite should be drawn on the screen. The peculiar bit is that the sprite is positioned by its upper left hand corner, which makes little sense when you start rotating the sprites. So I subtract off the half size offset from the point on the screen where I want the center to be drawn.

Finally, I call Draw(). The arguments are the texture handle, the sprite bounding box (in texture pixels), our scaling vector, the rotation center, the angle (in radions), the screen position coordinates, and finally the color.

Phew.


the MSDN documentation of ID3DXSprite is authoritative, but very difficult to get useful information out of.

Posted by tsmaster at 08:46 PM | Comments (0)