X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=thirdparty%2Fdirectxtex%2FDirectXTex%2FDirectXTexNormalMaps.cpp;fp=thirdparty%2Fdirectxtex%2FDirectXTex%2FDirectXTexNormalMaps.cpp;h=ad594933db714f2aaf835f885ac961cd505f8156;hb=d7c738e13decf8a8a891008c51b437ccbe3434fb;hp=0000000000000000000000000000000000000000;hpb=4f9982f5ec3dccae65d5a49dfd5a81b9737d90cd;p=apitrace diff --git a/thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp b/thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp new file mode 100644 index 0000000..ad59493 --- /dev/null +++ b/thirdparty/directxtex/DirectXTex/DirectXTexNormalMaps.cpp @@ -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( _aligned_malloc( (sizeof(XMVECTOR)*width*4), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + ScopedAlignedArrayFloat buffer( reinterpret_cast( _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