1

I am trying to implement a pixel perfect collision detection algorithm from an online tutorial, but it is written using d3d9. I am writing my game in d3d11, so I would like to know the best way to convert it. The original code, uses locked rects, whic,h after researching, I found were not in D3D11. By the way, I am using DirectX TK, so I have to convert form the ShaderResourceView to the texture. Here is the original code:

int Bird::PixelPerfectCollision(Vector2* birdPoints, Vector2* pipePoints, Renderer& renderer)
{
    // Creation of the bounding rectangles from the SPRITE values 
    // Remember that coordinates start in the upper left corner of the screen 
    //bird
    RECT rect1; 
    rect1.left = (long)birdPoints[4].x;
    rect1.top = (long)birdPoints[0].y;
    rect1.right = (long)birdPoints[2].x;
    rect1.bottom = (long)birdPoints[3].y;
    //pipe
    RECT rect2; 
    rect2.left = (long)pipePoints[0].x;
    rect2.top = (long)pipePoints[0].y;
    rect2.right = (long)pipePoints[1].x;
    rect2.bottom = (long)pipePoints[2].y;
    // Intersection of the bounding rectangles 
    // Up to here the code is just bounding box collision detection 
    RECT dest;

    if (IntersectRect(&dest, &rect1, &rect2))
    {
        ComPtr<ID3D11ShaderResourceView> birdTexSRV = renderer.GetBirdTexture();
        ComPtr<ID3D11ShaderResourceView> pipeTexSRV = renderer.GetPipeTexture();

        // Loking of the textures 
        // In this case the SPRITE object holds the texture to draw 
        // We will access it and invoke the LockRect method of LPDIRECT3DTEXTURE9 
        // The pixels will be saved in each D3DLOCKED_RECT object 
        Microsoft::WRL::ComPtr<ID3D11Resource> resource;
        birdTexSRV->GetResource(resource.GetAddressOf());

        ComPtr<IDirect3DTexture9> birdTex;
        DX::ThrowIfFailed(resource.As(&birdTex));

        D3DLOCKED_RECT rectS1;
        HRESULT hResult = birdTex->LockRect(0, &rectS1, NULL, NULL);

        if (FAILED(hResult))
        {
            MessageBox(0, L"Failed", L"Info", 0);
            return 0;
        }
        Microsoft::WRL::ComPtr<ID3D11Resource> resource;
        pipeTexSRV->GetResource(resource.GetAddressOf());

        ComPtr<IDirect3DTexture9> pipeTex;
        DX::ThrowIfFailed(resource.As(&pipeTex));

        D3DLOCKED_RECT rectS2;
        hResult = pipeTex -> LockRect(0, &rectS2, NULL, NULL);

        if (FAILED(hResult))
        {
            MessageBox(0, L"Failed", L"Info", 0);
            return 0;
        }
        // Get the pointer to the color values 
        // From now on, we will read that pointer as an array
        D3DCOLOR* pixelsS1 = (D3DCOLOR*)rectS1.pBits;
        D3DCOLOR* pixelsS2 = (D3DCOLOR*)rectS2.pBits;
        // We will check the area of the intersected rect (dest) 
        // In this rectangle, we have to check that in the same spot: 
        // A pixel from each texture collide 
        for (int rx = dest.left; rx < dest.right; rx++)
        {
            for (int ry = dest.top; ry < dest.bottom; ry++)
            {
                // Translation from the "dest" rect to sprite1 coordinates 
                int s1x = rx - screenPos.x;
                int s1y = ry - screenPos.y;
                // Translation from the "dest" rect to sprite2 coordinates
                int s2x = rx - pipePoints[0].x; 
                int s2y = ry - pipePoints[0].y;
                // Check the alpha value of each texture pixel 
                // The alpha value is the leftmost byte
                BYTE a = (pixelsS1[s1y * 128 + s1x] & 0xFF000000) >> 24;
                BYTE b = (pixelsS2[s2y * 480 + s2x] & 0xFF000000) >> 24;
                // If both pixels are opaque, we found a collision 
                // We have to unlock the textures and return 
                if (a == 255 && b == 255)
                {
                    birdTex->UnlockRect(0); pipeTex->UnlockRect(0);
                    return 1;
                }
            }
        }
        // If we reached here, it means that we did not find a collision
        birdTex->UnlockRect(0);
        pipeTex->UnlockRect(0);
        return 0;

        return true;
    }
    return 0;
} 

1 Answers1

0

If you want to do the collision on the CPU, your best bet is to create the textures twice. The first time as normal, and another time as a STAGING resource. Use the Ex version of the loader and provide it D3D11_USAGE_STAGING.

ComPtr<ID3D11Resource> birdTexCPU;

...

hr = CreateWICTextureFromFileEx(device, "birdtex.png", 0, 
   D3D11_USAGE_STAGING, 0, D3D11_CPU_ACCESS_READ, 0,
   WIC_LOADER_DEFAULT,
   birdTexCPU.ReleaseAndGetAddressOf(), nullptr);

You can of course just create the texture from disk as a STAGING, then create a DEFAULT copy from that using CopyTexture if you want to minimize disk bandwidth.

At that point, you can access the actual texture data in CPU memory via Map. DirectX Tool Kit has a helper class for it to make sure it always calls Unmap.

#include "DirectXHelpers.h"

...

{
    DirectX::MapGuard texdata(context, birdTexCPU.Get(), 0, D3D11_MAP_READ, 0);

    // You have a pointer now from texdata.get();

}

You should catch the texture data on the CPU in some standard memory instead of a Direct3D 11 resource at this point as it's easier to use, but you could leave it in STAGING memory if you wanted.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81