Sprite Collision Masks (DirectX 9 C++)

Started by
5 comments, last by zyrolasting 15 years, 5 months ago
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?
Advertisement
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.
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.
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.
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.
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, etcDWORD dwWidth; // The width of the texture (From the D3DXIMAGE_INFO struct from D3DXCreateTextureFromFileEx, etcDWORD dwHeight; // The height of the texture (From the D3DXIMAGE_INFO struct from D3DXCreateTextureFromFileEx, etc// Lock the texture to access the pixel dataHRESULT hResult = pTexture->LockRect(0, &rect, NULL, D3DLOCK_READONLY);if(FAILED(hResult)){   // Error: Can't lock texture for some reason   return false;}// Allocate the bitmaskbool* pCollisionMask = new bool[dwWidth * dwHeight];// Process the texture, line by linefor(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 itpTexture->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.
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!

This topic is closed to new replies.

Advertisement