123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- //-------------------------------------------------------------------------------------
- // 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_reads_(width) const XMVECTOR* pSource, _Out_writes_(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;
- const DWORD convFlags = _GetConvertFlags( format );
- if ( !convFlags )
- return E_FAIL;
- if ( !( convFlags & (CONVF_UNORM | CONVF_SNORM | CONVF_FLOAT) ) )
- return 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
- //-------------------------------------------------------------------------------------
- _Use_decl_annotations_
- HRESULT ComputeNormalMap( const Image& srcImage, DWORD flags, float amplitude,
- DXGI_FORMAT format, ScratchImage& normalMap )
- {
- if ( !srcImage.pixels || !IsValid(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(format) || IsCompressed(srcImage.format)
- || IsTypeless(format) || IsTypeless(srcImage.format)
- || IsPlanar(format) || IsPlanar(srcImage.format)
- || IsPalettized(format) || IsPalettized(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;
- }
- _Use_decl_annotations_
- HRESULT ComputeNormalMap( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
- DWORD flags, float amplitude, DXGI_FORMAT format, ScratchImage& normalMaps )
- {
- if ( !srcImages || !nimages || !IsValid(format) )
- return E_INVALIDARG;
- if ( IsCompressed(format) || IsCompressed(metadata.format)
- || IsTypeless(format) || IsTypeless(metadata.format)
- || IsPlanar(format) || IsPlanar(metadata.format)
- || IsPalettized(format) || IsPalettized(metadata.format) )
- return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
- 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
|