Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D3DFMT_X8L8V8U8 support (BumpDuDv/BumpLuminance) #485

Closed
iMrShadow opened this issue Jun 29, 2024 · 8 comments · Fixed by #501
Closed

D3DFMT_X8L8V8U8 support (BumpDuDv/BumpLuminance) #485

iMrShadow opened this issue Jun 29, 2024 · 8 comments · Fixed by #501
Assignees
Labels
dds DirectDraw Surface (DDS) enhancement

Comments

@iMrShadow
Copy link

Hello! I am working on a texture converter, which extracts texture data from proprietary game files to DDS, and vice versa.
The project is built with Avalonia UI, so I am using a wrapper of DirectXTex. Additionally, I have an image previewer in order for users to preview the textures they are converting.

The way the previewer works is to load the image using DirectXTex, convert it to raw pixel data and encode it in PNG format, because Avalonia's UI Image control only loads PNG files.

That worked great, until recently, I stumbled upon older textures using D3DFMT_X8L8V8U8, which is not supported by the library. Encoding is not really a priority, since noone will edit these types of textures (and there are alternative ways to create bumpmaps for those games), but being able to preview them would be nice.

One additional side question: Is there any more information regarding textures using the CTX1 (Xbox 360) compression? There's very limited material available, I am curious about it

Thanks

@walbourn walbourn added question dds DirectDraw Surface (DDS) labels Jun 30, 2024
@walbourn
Copy link
Member

walbourn commented Jun 30, 2024

Do you have an example D3DFMT_X8L8V8U8 texture to share?

What format would you expect BumpDuDv and BumpLuminance files to load as? Luminance would normally be loaded as a monochromatic format. The main problem is that in Direct3D 9 these are mixes of unsigned and signed channels, so they really don't work with any DXGI format like that.

  • D3DFMT_L6V5U5 could be DXGI_FORMAT_R8G8B8A8_SNORM

  • D3DFMT_A2W10V10U10 is a problem because there is no DXGI_FORMAT_R10G10B10A2_SNORM.

  • D3DFMT_X8L8V8U8 is a problem because any SNORM format would need 9+ bit for L, and again there is no DXGI_FORMAT_R10G10B10A2_SNORM

CTX1 is a proprietary compression so I don't believe details on it are public.

Note that D3DFMT_CxV8U8 could be loaded as DXGI_FORMAT_R8G8B8A8_SNORM, but I've never seen such a file.

@iMrShadow
Copy link
Author

iMrShadow commented Jun 30, 2024

Here are some examples (I could provide more if needed)
X8L8V8U8_Textures.zip

As for loading - I have seen NVIDIA's Texture Tool loading them correctly as RGBA, with alpha channel being 1. Maybe it can function similarly, but I am not knowledgable enough on this topic. I could provide screenshots on what the bumpmaps look like from their tool

@walbourn walbourn changed the title D3DFMT_X8L8V8U8 support D3DFMT_X8L8V8U8 support (BumpDuDv) Jul 18, 2024
@walbourn walbourn changed the title D3DFMT_X8L8V8U8 support (BumpDuDv) D3DFMT_X8L8V8U8 support (BumpDuDv/BumpLuminance) Jul 18, 2024
@walbourn walbourn self-assigned this Jul 30, 2024
@walbourn
Copy link
Member

walbourn commented Aug 3, 2024

Another option is to convert D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 to DXGI_FORMAT_R8G8B8A8 where the UV signed components are "x2 biased" to the UNORM range. That's probably what NVidia's Texture Tool is doing.

The same "x2 biased" trick could be applied to D3DFMT_A2W10V10U10 loading as DXGI_FORMAT_R10G10B10A2.

Now I just need to find test assets for all three formats. Your test case covers D3DFMT_X8L8V8U8.

@walbourn
Copy link
Member

walbourn commented Aug 3, 2024

The DirectX SDK 8.1 sample "BumpEarth" used this code to generate the three flavors. I need to see if I can write a little test app to generate DDS Files for these three:

//-----------------------------------------------------------------------------
// Name: InitBumpMap()
// Desc: Converts data from m_pEarthBumpTexture into the type of bump map requested
//       as m_BumpMapFormat into m_psBumpMap.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::InitBumpMap()
{
    LPDIRECT3DTEXTURE8 psBumpSrc = m_pEarthBumpTexture;
    D3DSURFACE_DESC    d3dsd;
    D3DLOCKED_RECT     d3dlr;

    psBumpSrc->GetLevelDesc( 0, &d3dsd );
    // Create the bumpmap's surface and texture objects
    if( FAILED( m_pd3dDevice->CreateTexture( d3dsd.Width, d3dsd.Height, 1, 0, 
        m_BumpMapFormat, D3DPOOL_MANAGED, &m_psBumpMap ) ) )
    {
        return E_FAIL;
    }

    // Fill the bits of the new texture surface with bits from
    // a private format.
    psBumpSrc->LockRect( 0, &d3dlr, 0, 0 );
    DWORD dwSrcPitch = (DWORD)d3dlr.Pitch;
    BYTE* pSrcTopRow = (BYTE*)d3dlr.pBits;
    BYTE* pSrcCurRow = pSrcTopRow;
    BYTE* pSrcBotRow = pSrcTopRow + (dwSrcPitch * (d3dsd.Height - 1) );

    m_psBumpMap->LockRect( 0, &d3dlr, 0, 0 );
    DWORD dwDstPitch = (DWORD)d3dlr.Pitch;
    BYTE* pDstTopRow = (BYTE*)d3dlr.pBits;
    BYTE* pDstCurRow = pDstTopRow;
    BYTE* pDstBotRow = pDstTopRow + (dwDstPitch * (d3dsd.Height - 1) );

    for( DWORD y=0; y<d3dsd.Height; y++ )
    {
        BYTE* pSrcB0; // addr of current pixel
        BYTE* pSrcB1; // addr of pixel below current pixel, wrapping to top if necessary
        BYTE* pSrcB2; // addr of pixel above current pixel, wrapping to bottom if necessary
        BYTE* pDstT;  // addr of dest pixel;

        pSrcB0 = pSrcCurRow;

        if( y == d3dsd.Height - 1)
            pSrcB1 = pSrcTopRow;
        else
            pSrcB1 = pSrcCurRow + dwSrcPitch;

        if( y == 0 )
            pSrcB2 = pSrcBotRow;
        else
            pSrcB2 = pSrcCurRow - dwSrcPitch;

        pDstT = pDstCurRow;

        for( DWORD x=0; x<d3dsd.Width; x++ )
        {
            LONG v00; // Current pixel
            LONG v01; // Pixel to the right of current pixel, wrapping to left edge if necessary
            LONG vM1; // Pixel to the left of current pixel, wrapping to right edge if necessary
            LONG v10; // Pixel one line below.
            LONG v1M; // Pixel one line above.

            v00 = *(pSrcB0+0);
            
            if( x == d3dsd.Width - 1 )
                v01 = *(pSrcCurRow);
            else
                v01 = *(pSrcB0+4);
            
            if( x == 0 )
                vM1 = *(pSrcCurRow + (4 * (d3dsd.Width - 1)));
            else
                vM1 = *(pSrcB0-4);
            v10 = *(pSrcB1+0);
            v1M = *(pSrcB2+0);

            LONG iDu = (vM1-v01); // The delta-u bump value
            LONG iDv = (v1M-v10); // The delta-v bump value

            // The luminance bump value (land masses are less shiny)
            WORD uL = ( v00>1 ) ? 63 : 127;

            switch( m_BumpMapFormat )
            {
                case D3DFMT_V8U8:
                    *pDstT++ = (BYTE)iDu;
                    *pDstT++ = (BYTE)iDv;
                    break;

                case D3DFMT_L6V5U5:
                    *(WORD*)pDstT  = (WORD)( ( (iDu>>3) & 0x1f ) <<  0 );
                    *(WORD*)pDstT |= (WORD)( ( (iDv>>3) & 0x1f ) <<  5 );
                    *(WORD*)pDstT |= (WORD)( ( ( uL>>2) & 0x3f ) << 10 );
                    pDstT += 2;
                    break;

                case D3DFMT_X8L8V8U8:
                    *pDstT++ = (BYTE)iDu;
                    *pDstT++ = (BYTE)iDv;
                    *pDstT++ = (BYTE)uL;
                    *pDstT++ = (BYTE)0L;
                    break;
            }

            // Move one pixel to the right (src is 32-bpp)
            pSrcB0+=4;
            pSrcB1+=4;
            pSrcB2+=4;
        }

        // Move to the next line
        pSrcCurRow += dwSrcPitch;
        pDstCurRow += dwDstPitch;
    }

    m_psBumpMap->UnlockRect(0);
    psBumpSrc->UnlockRect(0);

    return S_OK;
}

The input texture was bumpearth.bmp.
earthbump.zip

@iMrShadow
Copy link
Author

I remember that the Legacy DirectX Texture Tool can create such files (or at least can convert to bumpmap formats). Maybe that can be used to generate other samples?

@walbourn walbourn linked a pull request Aug 29, 2024 that will close this issue
@walbourn
Copy link
Member

I think I'm now doing something reasonable. For the A2WVU10 format, it looks like this in DXTex:

image

and when converted using this new code via texconv to a BMP:

image

@walbourn
Copy link
Member

walbourn commented Aug 29, 2024

The LVU888 and LVU565 do not end up with the same image in DXTex's visualization and through DirectXTex BMP output. It looks like legacy DXTex is doing something to map one of those channels to alpha, which makes it look a lot different. I don't think that is a logical mapping of the channels, so I'm happy with the DirectXTex output (using the same kind of conversion math as the UVW case above where the alpha channel is set to 1.0):

image

image

@iMrShadow
Copy link
Author

Thanks you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dds DirectDraw Surface (DDS) enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants