Sign in to follow this  
zyrolasting

Sprite Collision Masks (DirectX 9 C++)

Recommended Posts

In Game Maker, the engine I formerly used, sprites could have collisions be detected by actual content of the sprite. Rather, it was done by the content of a sprite called a collision mask. This was a Black and White image, where white was content that was considered "Solid" and ready for collision. This could allow me to use say a "bounding plus sign" or a "bounding circle". This mask would always follow the sprite around with the same coordinates. I would like to know how to accomplish this in DirectX (C++). I think I might be able to figure it out if I knew how to address pixels in a DirectX texture, and well... Actually the entire process is hazy. Lets just go with: How would I start?

Share this post


Link to post
Share on other sites
Quote:
Original post by zyrolasting
In Game Maker, the engine I formerly used, sprites could have collisions be detected by actual content of the sprite. Rather, it was done by the content of a sprite called a collision mask. This was a Black and White image, where white was content that was considered "Solid" and ready for collision. This could allow me to use say a "bounding plus sign" or a "bounding circle".
This mask would always follow the sprite around with the same coordinates.

I would like to know how to accomplish this in DirectX (C++).
I think I might be able to figure it out if I knew how to address pixels in a DirectX texture, and well... Actually the entire process is hazy.

Lets just go with: How would I start?
You don't want to read from the sprite texture at all, that'd be slow. You'd be better to keep a system memory copy of the masks (As in, in an array allocated with new[], not related to D3DPOOL_SYSTEMMEM), and then doing collision detection on the CPU with them. If you're not rotating the sprites then it should be pretty straightforward to compare the overlapping region of two sprites, but if you do need to rotate them, things get more "interesting", but I'd still recommend sticking with the same method.

Share this post


Link to post
Share on other sites
Alright, while I appreciate the input, I still have no idea what needs to be done.
I don't know how to check pixel by pixel in a texture image or how to send that data up to the program for a final result.

Share this post


Link to post
Share on other sites
Quote:
Original post by zyrolasting
Alright, while I appreciate the input, I still have no idea what needs to be done.
I don't know how to check pixel by pixel in a texture image or how to send that data up to the program for a final result.
You could use LockRect() to lock the texture at load time, and read the pixel values out of it. That'd let you see what pixels should be collidable, and which ones should be "empty".
Once you have that, you can create a 2D array of bools for the sprite, which is your collision mask.
To test for a collision, you first see if the bounding volumes for the two squares intersect, and if so, you can see if the collision mask is set for both sprites in any of the overlapping pixels, and if so - you have a collision.

Share this post


Link to post
Share on other sites
Quote:
Original post by zyrolasting
Works for me. Apparently I need some required reading, since I still do not know how to actually read each pixel. At least I think I know what to do from there.
LockRect() gives you a pointer to the pixel data in the texture surface. Unfortunately, the exact format of those pixels depends on the format of the texture. If you have a 32-bit texture (E.g. D3DFMT_A8R8G8B8), you can cast the pixel pointer to a DWORD* to get an array of ARGB pixels. Example:

LPDIRECT3DTEXTURE9 pTexture; // Your texture pointer (Assume it's initialised from D3DXCreateTextureFromFileEx, etc
DWORD dwWidth; // The width of the texture (From the D3DXIMAGE_INFO struct from D3DXCreateTextureFromFileEx, etc
DWORD dwHeight; // The height of the texture (From the D3DXIMAGE_INFO struct from D3DXCreateTextureFromFileEx, etc

// Lock the texture to access the pixel data
HRESULT hResult = pTexture->LockRect(0, &rect, NULL, D3DLOCK_READONLY);
if(FAILED(hResult))
{
// Error: Can't lock texture for some reason
return false;
}

// Allocate the bitmask
bool* pCollisionMask = new bool[dwWidth * dwHeight];

// Process the texture, line by line
for(DWORD y=0; y<dwHeight; ++y)
{
// Get a pointer to the start of this scanline in the texture
DWORD* pBits = (DWORD*)((BYTE*)rect.pBits + y*rect.Pitch); // See note 1 below

// Process this line of pixels
for(DWORD x=0; x<dwWidth; ++x)
{
// Get this pixel
DWORD dwPixel = pBits[x];

// If the alpha is 0x00 (transparent), set this entry of the collision mask to false, else set it to true
pCollisionMask[y*dwWidth + x] = (dwPixel&0xff000000) != 0x00;
}
}

// Done with the texture, so unlock it
pTexture->UnlockRect(0);

That code will allocate and fill in pCollisionMask with bools set to true for opaque pixels (Although it's untested).
As for "See note 1 below", above, that probably needs a bit of explanation.
The Pitch of a surface is the number of bytes between two consecutive lines of pixels - that may not be equal to the width of the surface times the bytes-per-pixel, since some drivers may store extra information at the end of each scanline, or need to pad them for various reasons - this is particularly true for compressed textures and non-power-of-2 textures.
So, you cast the bits pointer to a BYTE*, and add on y*Pitch to get a pointer to the start of line Y, then cast the pointer to a DWORD*, since the pixels are 4 bytes across.

If you're not using 32-bit textures, you'll need to change the code to cope with other pixel strides.

Share this post


Link to post
Share on other sites
You didn't need to write code for me, but thank you very much.
This is the, I don't know, twentieth time you helped me out. Wish I could do something in return. I'll just keep voting your rating up as of now...
I'll also see if I can make my own variation of this.

Thanks so much for the continued help!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this