Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DirectXTexNormalMaps.cpp
Go to the documentation of this file.
1 //-------------------------------------------------------------------------------------
2 // DirectXTexNormalMaps.cpp
3 //
4 // DirectX Texture Library - Normal map operations
5 //
6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
7 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9 // PARTICULAR PURPOSE.
10 //
11 // Copyright (c) Microsoft Corporation. All rights reserved.
12 //
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
15 
16 #include "directxtexp.h"
17 
18 namespace DirectX
19 {
20 
21 #pragma prefast(suppress : 25000, "FXMVECTOR is 16 bytes")
22 static inline float _EvaluateColor( _In_ FXMVECTOR val, _In_ DWORD flags )
23 {
24  XMFLOAT4A f;
25 
26  static XMVECTORF32 lScale = { 0.2125f, 0.7154f, 0.0721f, 1.f };
27 
28  static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
29  switch( flags & 0xf )
30  {
31  case 0:
32  case CNMAP_CHANNEL_RED: return XMVectorGetX( val );
33  case CNMAP_CHANNEL_GREEN: return XMVectorGetY( val );
34  case CNMAP_CHANNEL_BLUE: return XMVectorGetZ( val );
35  case CNMAP_CHANNEL_ALPHA: return XMVectorGetW( val );
36 
38  {
39  XMVECTOR v = XMVectorMultiply( val, lScale );
40  XMStoreFloat4A( &f, v );
41  return f.x + f.y + f.z;
42  }
43  break;
44 
45  default:
46  assert(false);
47  return 0.f;
48  }
49 }
50 
51 static void _EvaluateRow( _In_reads_(width) const XMVECTOR* pSource, _Out_writes_(width+2) float* pDest,
52  _In_ size_t width, _In_ DWORD flags )
53 {
54  assert( pSource && pDest );
55  assert( width > 0 );
56 
57  for( size_t x = 0; x < width; ++x )
58  {
59  pDest[x+1] = _EvaluateColor( pSource[x], flags );
60  }
61 
62  if ( flags & CNMAP_MIRROR_U )
63  {
64  // Mirror in U
65  pDest[0] = _EvaluateColor( pSource[0], flags );
66  pDest[width+1] = _EvaluateColor( pSource[width-1], flags );
67  }
68  else
69  {
70  // Wrap in U
71  pDest[0] = _EvaluateColor( pSource[width-1], flags );
72  pDest[width+1] = _EvaluateColor( pSource[0], flags );
73  }
74 }
75 
76 static HRESULT _ComputeNMap( _In_ const Image& srcImage, _In_ DWORD flags, _In_ float amplitude,
77  _In_ DXGI_FORMAT format, _In_ const Image& normalMap )
78 {
79  if ( !srcImage.pixels || !normalMap.pixels )
80  return E_INVALIDARG;
81 
82  const DWORD convFlags = _GetConvertFlags( format );
83  if ( !convFlags )
84  return E_FAIL;
85 
86  if ( !( convFlags & (CONVF_UNORM | CONVF_SNORM | CONVF_FLOAT) ) )
87  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
88 
89  const size_t width = srcImage.width;
90  const size_t height = srcImage.height;
91  if ( width != normalMap.width || height != normalMap.height )
92  return E_FAIL;
93 
94  // Allocate temporary space (4 scanlines and 3 evaluated rows)
95  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*4), 16 ) ) );
96  if ( !scanline )
97  return E_OUTOFMEMORY;
98 
99  ScopedAlignedArrayFloat buffer( reinterpret_cast<float*>( _aligned_malloc( ( ( sizeof(float) * ( width + 2 ) ) * 3 ), 16 ) ) );
100  if ( !buffer )
101  return E_OUTOFMEMORY;
102 
103  uint8_t* pDest = normalMap.pixels;
104  if ( !pDest )
105  return E_POINTER;
106 
107  XMVECTOR* row0 = scanline.get();
108  XMVECTOR* row1 = row0 + width;
109  XMVECTOR* row2 = row1 + width;
110  XMVECTOR* target = row2 + width;
111 
112  float* val0 = buffer.get();
113  float* val1 = val0 + width + 2;
114  float* val2 = val1 + width + 2;
115 
116  const size_t rowPitch = srcImage.rowPitch;
117  const uint8_t* pSrc = srcImage.pixels;
118 
119  // Read first scanline row into 'row1'
120  if ( !_LoadScanline( row1, width, pSrc, rowPitch, srcImage.format ) )
121  return E_FAIL;
122 
123  // Setup 'row0'
124  if ( flags & CNMAP_MIRROR_V )
125  {
126  // Mirror first row
127  memcpy_s( row0, rowPitch, row1, rowPitch );
128  }
129  else
130  {
131  // Read last row (Wrap V)
132  if ( !_LoadScanline( row0, width, pSrc + (rowPitch * (height-1)), rowPitch, srcImage.format ) )
133  return E_FAIL;
134  }
135 
136  // Evaluate the initial rows
137  _EvaluateRow( row0, val0, width, flags );
138  _EvaluateRow( row1, val1, width, flags );
139 
140  pSrc += rowPitch;
141 
142  for( size_t y = 0; y < height; ++y )
143  {
144  // Load next scanline of source image
145  if ( y < (height-1) )
146  {
147  if ( !_LoadScanline( row2, width, pSrc, rowPitch, srcImage.format ) )
148  return E_FAIL;
149  }
150  else
151  {
152  if ( flags & CNMAP_MIRROR_V )
153  {
154  // Use last row of source image
155  if ( !_LoadScanline( row2, width, srcImage.pixels + (rowPitch * (height-1)), rowPitch, srcImage.format ) )
156  return E_FAIL;
157  }
158  else
159  {
160  // Use first row of source image (Wrap V)
161  if ( !_LoadScanline( row2, width, srcImage.pixels, rowPitch, srcImage.format ) )
162  return E_FAIL;
163  }
164  }
165 
166  // Evaluate row
167  _EvaluateRow( row2, val2, width, flags );
168 
169  // Generate target scanline
170  XMVECTOR *dptr = target;
171  for( size_t x = 0; x < width; ++x )
172  {
173  // Compute normal via central differencing
174  float totDelta = ( val0[x] - val0[x+2] ) + ( val1[x] - val1[x+2] ) + ( val2[x] - val2[x+2] );
175  float deltaZX = totDelta * amplitude / 6.f;
176 
177  totDelta = ( val0[x] - val2[x] ) + ( val0[x+1] - val2[x+1] ) + ( val0[x+2] - val2[x+2] );
178  float deltaZY = totDelta * amplitude / 6.f;
179 
180  XMVECTOR vx = XMVectorSetZ( g_XMNegIdentityR0, deltaZX ); // (-1.0f, 0.0f, deltaZX)
181  XMVECTOR vy = XMVectorSetZ( g_XMNegIdentityR1, deltaZY ); // (0.0f, -1.0f, deltaZY)
182 
183  XMVECTOR normal = XMVector3Normalize( XMVector3Cross( vx, vy ) );
184 
185  // Compute alpha (1.0 or an occlusion term)
186  float alpha = 1.f;
187 
188  if ( flags & CNMAP_COMPUTE_OCCLUSION )
189  {
190  float delta = 0.f;
191  float c = val1[x+1];
192 
193  float t = val0[x] - c; if ( t > 0.f ) delta += t;
194  t = val0[x+1] - c; if ( t > 0.f ) delta += t;
195  t = val0[x+2] - c; if ( t > 0.f ) delta += t;
196  t = val1[x] - c; if ( t > 0.f ) delta += t;
197  // Skip current pixel
198  t = val1[x+2] - c; if ( t > 0.f ) delta += t;
199  t = val2[x] - c; if ( t > 0.f ) delta += t;
200  t = val2[x+1] - c; if ( t > 0.f ) delta += t;
201  t = val2[x+2] - c; if ( t > 0.f ) delta += t;
202 
203  // Average delta (divide by 8, scale by amplitude factor)
204  delta *= 0.125f * amplitude;
205  if ( delta > 0.f )
206  {
207  // If < 0, then no occlusion
208  float r = sqrtf( 1.f + delta*delta );
209  alpha = (r - delta) / r;
210  }
211  }
212 
213  // Encode based on target format
214  if ( convFlags & CONVF_UNORM )
215  {
216  // 0.5f*normal + 0.5f -or- invert sign case: -0.5f*normal + 0.5f
217  XMVECTOR n1 = XMVectorMultiplyAdd( (flags & CNMAP_INVERT_SIGN) ? g_XMNegativeOneHalf : g_XMOneHalf, normal, g_XMOneHalf );
218  *dptr++ = XMVectorSetW( n1, alpha );
219  }
220  else if ( flags & CNMAP_INVERT_SIGN )
221  {
222  *dptr++ = XMVectorSetW( XMVectorNegate( normal ), alpha );
223  }
224  else
225  {
226  *dptr++ = XMVectorSetW( normal, alpha );
227  }
228  }
229 
230  if ( !_StoreScanline( pDest, normalMap.rowPitch, format, target, width ) )
231  return E_FAIL;
232 
233  // Cycle buffers
234  float* temp = val0;
235  val0 = val1;
236  val1 = val2;
237  val2 = temp;
238 
239  pSrc += rowPitch;
240  pDest += normalMap.rowPitch;
241  }
242 
243  return S_OK;
244 }
245 
246 
247 //=====================================================================================
248 // Entry points
249 //=====================================================================================
250 
251 //-------------------------------------------------------------------------------------
252 // Generates a normal map from a height-map
253 //-------------------------------------------------------------------------------------
254 _Use_decl_annotations_
255 HRESULT ComputeNormalMap( const Image& srcImage, DWORD flags, float amplitude,
256  DXGI_FORMAT format, ScratchImage& normalMap )
257 {
258  if ( !srcImage.pixels || !IsValid(format) )
259  return E_INVALIDARG;
260 
261  static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
262  switch( flags & 0xf )
263  {
264  case 0:
265  case CNMAP_CHANNEL_RED:
266  case CNMAP_CHANNEL_GREEN:
267  case CNMAP_CHANNEL_BLUE:
268  case CNMAP_CHANNEL_ALPHA:
270  break;
271 
272  default:
273  return E_INVALIDARG;
274  }
275 
276  if ( IsCompressed(format) || IsCompressed(srcImage.format)
277  || IsTypeless(format) || IsTypeless(srcImage.format)
278  || IsPlanar(format) || IsPlanar(srcImage.format)
279  || IsPalettized(format) || IsPalettized(srcImage.format) )
280  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
281 
282  // Setup target image
283  normalMap.Release();
284 
285  HRESULT hr = normalMap.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 );
286  if ( FAILED(hr) )
287  return hr;
288 
289  const Image *img = normalMap.GetImage( 0, 0, 0 );
290  if ( !img )
291  {
292  normalMap.Release();
293  return E_POINTER;
294  }
295 
296  hr = _ComputeNMap( srcImage, flags, amplitude, format, *img );
297  if ( FAILED(hr) )
298  {
299  normalMap.Release();
300  return hr;
301  }
302 
303  return S_OK;
304 }
305 
306 _Use_decl_annotations_
307 HRESULT ComputeNormalMap( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
308  DWORD flags, float amplitude, DXGI_FORMAT format, ScratchImage& normalMaps )
309 {
310  if ( !srcImages || !nimages || !IsValid(format) )
311  return E_INVALIDARG;
312 
313  if ( IsCompressed(format) || IsCompressed(metadata.format)
314  || IsTypeless(format) || IsTypeless(metadata.format)
315  || IsPlanar(format) || IsPlanar(metadata.format)
316  || IsPalettized(format) || IsPalettized(metadata.format) )
317  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
318 
319  static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" );
320  switch( flags & 0xf )
321  {
322  case 0:
323  case CNMAP_CHANNEL_RED:
324  case CNMAP_CHANNEL_GREEN:
325  case CNMAP_CHANNEL_BLUE:
326  case CNMAP_CHANNEL_ALPHA:
328  break;
329 
330  default:
331  return E_INVALIDARG;
332  }
333 
334  normalMaps.Release();
335 
336  TexMetadata mdata2 = metadata;
337  mdata2.format = format;
338  HRESULT hr = normalMaps.Initialize( mdata2 );
339  if ( FAILED(hr) )
340  return hr;
341 
342  if ( nimages != normalMaps.GetImageCount() )
343  {
344  normalMaps.Release();
345  return E_FAIL;
346  }
347 
348  const Image* dest = normalMaps.GetImages();
349  if ( !dest )
350  {
351  normalMaps.Release();
352  return E_POINTER;
353  }
354 
355  for( size_t index=0; index < nimages; ++index )
356  {
357  assert( dest[ index ].format == format );
358 
359  const Image& src = srcImages[ index ];
360  if ( IsCompressed( src.format ) || IsTypeless( src.format ) )
361  {
362  normalMaps.Release();
363  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
364  }
365 
366  if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
367  {
368  normalMaps.Release();
369  return E_FAIL;
370  }
371 
372  hr = _ComputeNMap( src, flags, amplitude, format, dest[ index ] );
373  if ( FAILED(hr) )
374  {
375  normalMaps.Release();
376  return hr;
377  }
378  }
379 
380  return S_OK;
381 }
382 
383 }; // namespace
std::unique_ptr< DirectX::XMVECTOR, aligned_deleter > ScopedAlignedArrayXMVECTOR
Definition: scoped.h:27
const Image * GetImage(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const
uint8_t * pixels
Definition: DirectXTex.h:230
bool IsPlanar(_In_ DXGI_FORMAT fmt)
DXGI_FORMAT format
Definition: DirectXTex.h:125
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
size_t _In_ DXGI_FORMAT size_t _In_ TEXP_LEGACY_FORMAT _In_ DWORD flags assert(pDestination &&outSize > 0)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
HRESULT Initialize(_In_ const TexMetadata &mdata, _In_ DWORD flags=CP_FLAGS_NONE)
HRESULT Initialize2D(_In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ DWORD flags=CP_FLAGS_NONE)
_Use_decl_annotations_ bool _LoadScanline(XMVECTOR *pDestination, size_t count, LPCVOID pSource, size_t size, DXGI_FORMAT format)
bool IsCompressed(_In_ DXGI_FORMAT fmt)
size_t GetImageCount() const
Definition: DirectXTex.h:264
_In_ size_t _In_ const TexMetadata & metadata
Definition: DirectXTexP.h:116
_Use_decl_annotations_ DWORD _GetConvertFlags(DXGI_FORMAT format)
bool IsValid(_In_ DXGI_FORMAT fmt)
std::unique_ptr< float, aligned_deleter > ScopedAlignedArrayFloat
Definition: scoped.h:25
static float _EvaluateColor(_In_ FXMVECTOR val, _In_ DWORD flags)
_In_ size_t _In_ const TexMetadata _In_ DWORD _Out_writes_(nImages) Image *images
bool IsPalettized(_In_ DXGI_FORMAT fmt)
_In_ size_t _In_ DXGI_FORMAT _In_reads_(count) const XMVECTOR *pSource
bool IsTypeless(_In_ DXGI_FORMAT fmt, _In_ bool partialTypeless=true)
static HRESULT _ComputeNMap(_In_ const Image &srcImage, _In_ DWORD flags, _In_ float amplitude, _In_ DXGI_FORMAT format, _In_ const Image &normalMap)
char * dest
Definition: lz4.h:61
HRESULT ComputeNormalMap(_In_ const Image &srcImage, _In_ DWORD flags, _In_ float amplitude, _In_ DXGI_FORMAT format, _Out_ ScratchImage &normalMap)
DXGI_FORMAT format
Definition: DirectXTex.h:227
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Definition: DirectXTexP.h:175
const Image * GetImages() const
Definition: DirectXTex.h:263
static void _EvaluateRow(_In_reads_(width) const XMVECTOR *pSource, _Out_writes_(width+2) float *pDest, _In_ size_t width, _In_ DWORD flags)
_Use_decl_annotations_ bool _StoreScanline(LPVOID pDestination, size_t size, DXGI_FORMAT format, const XMVECTOR *pSource, size_t count, float threshold)