]> git.cworth.org Git - apitrace/blobdiff - thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp
thirdparty/directxtex: Import DirectXTex library.
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexNormalMaps.cpp
diff --git a/thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp b/thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp
new file mode 100644 (file)
index 0000000..7591b91
--- /dev/null
@@ -0,0 +1,377 @@
+//-------------------------------------------------------------------------------------
+// DirectXTexNormalMaps.cpp
+//  
+// DirectX Texture Library - Normal map operations
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+// PARTICULAR PURPOSE.
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// http://go.microsoft.com/fwlink/?LinkId=248926
+//-------------------------------------------------------------------------------------
+
+#include "directxtexp.h"
+
+namespace DirectX
+{
+
+#pragma prefast(suppress : 25000, "FXMVECTOR is 16 bytes")
+static inline float _EvaluateColor( _In_ FXMVECTOR val, _In_ DWORD flags )
+{
+    XMFLOAT4A f;
+
+    static XMVECTORF32 lScale = { 0.2125f, 0.7154f, 0.0721f, 1.f };
+
+    static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
+    switch( flags & 0xf )
+    {
+    case 0:
+    case CNMAP_CHANNEL_RED:     return XMVectorGetX( val );
+    case CNMAP_CHANNEL_GREEN:   return XMVectorGetY( val );
+    case CNMAP_CHANNEL_BLUE:    return XMVectorGetZ( val );
+    case CNMAP_CHANNEL_ALPHA:   return XMVectorGetW( val );
+
+    case CNMAP_CHANNEL_LUMINANCE:
+        {
+            XMVECTOR v = XMVectorMultiply( val, lScale );
+            XMStoreFloat4A( &f, v );
+            return f.x + f.y + f.z;
+        }
+        break;
+
+    default:
+        assert(false);
+        return 0.f;
+    }
+}
+
+static void _EvaluateRow( _In_count_(width) const XMVECTOR* pSource, _Out_cap_(width+2) float* pDest,
+                          _In_ size_t width, _In_ DWORD flags )
+{
+    assert( pSource && pDest );
+    assert( width > 0 );
+
+    for( size_t x = 0; x < width; ++x )
+    {
+        pDest[x+1] = _EvaluateColor( pSource[x], flags );
+    }
+
+    if ( flags & CNMAP_MIRROR_U )
+    {
+        // Mirror in U
+        pDest[0] = _EvaluateColor( pSource[0], flags );
+        pDest[width+1] = _EvaluateColor( pSource[width-1], flags );
+    }
+    else
+    {
+        // Wrap in U
+        pDest[0] = _EvaluateColor( pSource[width-1], flags );
+        pDest[width+1] = _EvaluateColor( pSource[0], flags );
+    }
+}
+
+static HRESULT _ComputeNMap( _In_ const Image& srcImage, _In_ DWORD flags, _In_ float amplitude,
+                             _In_ DXGI_FORMAT format, _In_ const Image& normalMap )
+{
+    if ( !srcImage.pixels || !normalMap.pixels )
+        return E_INVALIDARG;
+
+    assert( !IsCompressed(format) && !IsTypeless( format ) );
+
+    const DWORD convFlags = _GetConvertFlags( format );
+    if ( !convFlags )
+        return E_FAIL;
+
+    if ( !( convFlags & (CONVF_UNORM | CONVF_SNORM | CONVF_FLOAT) ) )
+        HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+
+    const size_t width = srcImage.width;
+    const size_t height = srcImage.height;
+    if ( width != normalMap.width || height != normalMap.height )
+        return E_FAIL;
+
+    // Allocate temporary space (4 scanlines and 3 evaluated rows)
+    ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*4), 16 ) ) );
+    if ( !scanline )
+        return E_OUTOFMEMORY;
+
+    ScopedAlignedArrayFloat buffer( reinterpret_cast<float*>( _aligned_malloc( ( ( sizeof(float) * ( width + 2 ) ) * 3 ), 16 ) ) );
+    if ( !buffer )
+        return E_OUTOFMEMORY;
+
+    uint8_t* pDest = normalMap.pixels;
+    if ( !pDest )
+        return E_POINTER;
+
+    XMVECTOR* row0 = scanline.get();
+    XMVECTOR* row1 = row0 + width;
+    XMVECTOR* row2 = row1 + width;
+    XMVECTOR* target = row2 + width;
+
+    float* val0 = buffer.get();
+    float* val1 = val0 + width + 2;
+    float* val2 = val1 + width + 2;
+
+    const size_t rowPitch = srcImage.rowPitch;
+    const uint8_t* pSrc = srcImage.pixels;
+
+    // Read first scanline row into 'row1'
+    if ( !_LoadScanline( row1, width, pSrc, rowPitch, srcImage.format ) )
+        return E_FAIL;
+
+    // Setup 'row0'
+    if ( flags & CNMAP_MIRROR_V )
+    {
+        // Mirror first row
+        memcpy_s( row0, rowPitch, row1, rowPitch );
+    }
+    else
+    {
+        // Read last row (Wrap V)
+        if ( !_LoadScanline( row0, width, pSrc + (rowPitch * (height-1)), rowPitch, srcImage.format ) )
+            return E_FAIL;
+    }
+
+    // Evaluate the initial rows
+    _EvaluateRow( row0, val0, width, flags );
+    _EvaluateRow( row1, val1, width, flags );
+
+    pSrc += rowPitch;
+
+    for( size_t y = 0; y < height; ++y )
+    {
+        // Load next scanline of source image
+        if ( y < (height-1) )
+        {
+            if ( !_LoadScanline( row2, width, pSrc, rowPitch, srcImage.format ) )
+                return E_FAIL;
+        }
+        else
+        {
+            if ( flags & CNMAP_MIRROR_V )
+            {
+                // Use last row of source image
+                if ( !_LoadScanline( row2, width, srcImage.pixels + (rowPitch * (height-1)), rowPitch, srcImage.format ) )
+                    return E_FAIL;
+            }
+            else
+            {
+                // Use first row of source image (Wrap V)
+                if ( !_LoadScanline( row2, width, srcImage.pixels, rowPitch, srcImage.format ) )
+                    return E_FAIL;
+            }
+        }
+
+        // Evaluate row
+        _EvaluateRow( row2, val2, width, flags );
+
+        // Generate target scanline
+        XMVECTOR *dptr = target;
+        for( size_t x = 0; x < width; ++x )
+        {
+            // Compute normal via central differencing
+            float totDelta = ( val0[x] - val0[x+2] ) + ( val1[x] - val1[x+2] ) + ( val2[x] - val2[x+2] );
+            float deltaZX = totDelta * amplitude / 6.f;
+
+            totDelta = ( val0[x] - val2[x] ) + ( val0[x+1] - val2[x+1] ) + ( val0[x+2] - val2[x+2] );
+            float deltaZY = totDelta * amplitude / 6.f;
+
+            XMVECTOR vx = XMVectorSetZ( g_XMNegIdentityR0, deltaZX );   // (-1.0f, 0.0f, deltaZX)
+            XMVECTOR vy = XMVectorSetZ( g_XMNegIdentityR1, deltaZY );   // (0.0f, -1.0f, deltaZY)
+
+            XMVECTOR normal = XMVector3Normalize( XMVector3Cross( vx, vy ) );
+
+            // Compute alpha (1.0 or an occlusion term)
+            float alpha = 1.f;
+
+            if ( flags & CNMAP_COMPUTE_OCCLUSION )
+            {
+                float delta = 0.f;
+                float c = val1[x+1];
+
+                float t = val0[x] - c;  if ( t > 0.f ) delta += t;
+                t = val0[x+1]   - c;    if ( t > 0.f ) delta += t;
+                t = val0[x+2]   - c;    if ( t > 0.f ) delta += t;
+                t = val1[x]     - c;    if ( t > 0.f ) delta += t;
+                // Skip current pixel
+                t = val1[x+2]   - c;    if ( t > 0.f ) delta += t;
+                t = val2[x]     - c;    if ( t > 0.f ) delta += t;
+                t = val2[x+1]   - c;    if ( t > 0.f ) delta += t;
+                t = val2[x+2]   - c;    if ( t > 0.f ) delta += t;
+
+                // Average delta (divide by 8, scale by amplitude factor)
+                delta *= 0.125f * amplitude;
+                if ( delta > 0.f )
+                {
+                    // If < 0, then no occlusion
+                    float r = sqrtf( 1.f + delta*delta );
+                    alpha = (r - delta) / r;
+                }
+            }
+
+            // Encode based on target format
+            if ( convFlags & CONVF_UNORM )
+            {
+                // 0.5f*normal + 0.5f -or- invert sign case: -0.5f*normal + 0.5f
+                XMVECTOR n1 = XMVectorMultiplyAdd( (flags & CNMAP_INVERT_SIGN) ? g_XMNegativeOneHalf : g_XMOneHalf, normal, g_XMOneHalf ); 
+                *dptr++ = XMVectorSetW( n1, alpha );
+            }
+            else if ( flags & CNMAP_INVERT_SIGN )
+            {
+                *dptr++ = XMVectorSetW( XMVectorNegate( normal ), alpha );
+            }
+            else
+            {
+                *dptr++ = XMVectorSetW( normal, alpha );
+            }
+        }
+
+        if ( !_StoreScanline( pDest, normalMap.rowPitch, format, target, width ) )
+            return E_FAIL;
+
+        // Cycle buffers
+        float* temp = val0;
+        val0 = val1;
+        val1 = val2;
+        val2 = temp;
+
+        pSrc += rowPitch;
+        pDest += normalMap.rowPitch;
+    }
+
+    return S_OK;
+}
+
+
+//=====================================================================================
+// Entry points
+//=====================================================================================
+        
+//-------------------------------------------------------------------------------------
+// Generates a normal map from a height-map
+//-------------------------------------------------------------------------------------
+HRESULT ComputeNormalMap( const Image& srcImage, DWORD flags, float amplitude,
+                          DXGI_FORMAT format, ScratchImage& normalMap )
+{
+    if ( !srcImage.pixels || !IsValid(format) || IsCompressed( format ) || IsTypeless( format ) )
+        return E_INVALIDARG;
+
+    static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
+    switch( flags & 0xf )
+    {
+    case 0:
+    case CNMAP_CHANNEL_RED:
+    case CNMAP_CHANNEL_GREEN:
+    case CNMAP_CHANNEL_BLUE:
+    case CNMAP_CHANNEL_ALPHA:
+    case CNMAP_CHANNEL_LUMINANCE:
+        break;
+
+    default:
+        return E_INVALIDARG;
+    }
+
+    if ( IsCompressed( srcImage.format ) || IsTypeless( srcImage.format ) )
+        return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+
+    // Setup target image
+    normalMap.Release();
+
+    HRESULT hr = normalMap.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 );
+    if ( FAILED(hr) )
+        return hr;
+
+    const Image *img = normalMap.GetImage( 0, 0, 0 );
+    if ( !img )
+    {
+        normalMap.Release();
+        return E_POINTER;
+    }
+
+    hr = _ComputeNMap( srcImage, flags, amplitude, format, *img );
+    if ( FAILED(hr) )
+    {
+        normalMap.Release();
+        return hr;
+    }
+
+    return S_OK;
+}
+
+HRESULT ComputeNormalMap( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
+                          DWORD flags, float amplitude, DXGI_FORMAT format, ScratchImage& normalMaps )
+{
+    if ( !srcImages || !nimages )
+        return E_INVALIDARG;
+
+    if ( !IsValid(format) || IsCompressed(format) || IsTypeless(format) )
+        return E_INVALIDARG;
+
+    static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
+    switch( flags & 0xf )
+    {
+    case 0:
+    case CNMAP_CHANNEL_RED:
+    case CNMAP_CHANNEL_GREEN:
+    case CNMAP_CHANNEL_BLUE:
+    case CNMAP_CHANNEL_ALPHA:
+    case CNMAP_CHANNEL_LUMINANCE:
+        break;
+
+    default:
+        return E_INVALIDARG;
+    }
+
+    normalMaps.Release();
+
+    TexMetadata mdata2 = metadata;
+    mdata2.format = format;
+    HRESULT hr = normalMaps.Initialize( mdata2 );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( nimages != normalMaps.GetImageCount() )
+    {
+        normalMaps.Release();
+        return E_FAIL;
+    }
+
+    const Image* dest = normalMaps.GetImages();
+    if ( !dest )
+    {
+        normalMaps.Release();
+        return E_POINTER;
+    }
+
+    for( size_t index=0; index < nimages; ++index )
+    {
+        assert( dest[ index ].format == format );
+
+        const Image& src = srcImages[ index ];
+        if ( IsCompressed( src.format ) || IsTypeless( src.format ) )
+        {
+            normalMaps.Release();
+            return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+        }
+
+        if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
+        {
+            normalMaps.Release();
+            return E_FAIL;
+        }
+
+        hr = _ComputeNMap( src, flags, amplitude, format, dest[ index ] );
+        if ( FAILED(hr) )
+        {
+            normalMaps.Release();
+            return hr;
+        }
+    }
+
+    return S_OK;
+}
+
+}; // namespace