1 // nbvtf.h - v1.02 - Writer for Valve Texture Format - public domain
2 // Written by Harry 'hgn' Godden
5 // Rich Geldreich - bc7enc (BC1/BC3 High Quality texture compression)
6 // Fabian "ryg" Giesen, stb - stb_dxt (BC1/BC3 realtime compressors)
7 // Sean Barrett - stb_image.h, stb_image_write.h (Image I/O)
10 // This library assumes normal maps are input in OpenGL(correct) format, and will be converted into DirectX(incorrect) at
11 // compile time automatically. Do not submit DirectX normals into this software.
13 // Since this project uses stb_image, use '#define STB_IMAGE_IMPLEMENTATION' before including
14 // Additionally, to use high quality DXT, '#define USE_LIBRGBCX'
17 // Call these functions:
18 // int nbvtf_convert( const char *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
19 // int nbvtf_write( uint8_t *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
22 // src - RGBA byte data of image
24 // h - height of image
25 // mipmap - enable mipmap generation (box filter), 1/0
26 // format - Choose from: k_EImageFormat_DXT1, compressedk_EImageFormat_DXT5, k_EImageFormat_BGR888, k_EImageFormat_ABGR8888
27 // usr_flags - You can append any flags but only really need TEXTUREFLAGS_NORMAL if texture is normal map
28 // dest - file path to write vtf to
31 // src - file path of source image to convert
32 // w, h - MAXIMUM image dimentions of final product. Set as 0 to be automatic
35 // v1.02 - Improved box filtering, small bug fixes
36 // v1.01 - switch to OpenGL normal format for input
37 // v1.00 - (hgn) first release
40 // See end of file for license information.
46 #define NBVTF_MAX_MIPLEVELS 9
47 #define nbvtf__min(a,b) (((a)<(b))?(a):(b))
48 #define nbvtf__max(a,b) (((a)>(b))?(a):(b))
56 #define NBVTF_SHOW_STDERR
57 #define STB_IMAGE_IMPLEMENTATION
60 #include "stb/stb_image.h"
65 #define STB_DXT_IMPLEMENTATION
66 #include "stb/stb_dxt.h"
69 #ifdef NBVTF_SHOW_STDERR
70 #define NBVTF_ERR(...)
72 #define NBVTF_ERR(...) fprintf( stderr, __VA_ARGS__ )
77 typedef enum EImageFormat
80 k_EImageFormat_NONE
= -1,
81 k_EImageFormat_RGBA8888
= 0, // YES
82 k_EImageFormat_ABGR8888
,
83 k_EImageFormat_RGB888
, // YES
84 k_EImageFormat_BGR888
,
85 k_EImageFormat_RGB565
,
90 k_EImageFormat_RGB888_BLUESCREEN
,
91 k_EImageFormat_BGR888_BLUESCREEN
,
92 k_EImageFormat_ARGB8888
,
93 k_EImageFormat_BGRA8888
,
94 k_EImageFormat_DXT1
, // YES
96 k_EImageFormat_DXT5
, // YES
97 k_EImageFormat_BGRX8888
,
98 k_EImageFormat_BGR565
,
99 k_EImageFormat_BGRX5551
,
100 k_EImageFormat_BGRA4444
,
101 k_EImageFormat_DXT1_ONEBITALPHA
,
102 k_EImageFormat_BGRA5551
,
104 k_EImageFormat_UVWQ8888
,
105 k_EImageFormat_RGBA16161616F
,
106 k_EImageFormat_RGBA16161616
,
107 k_EImageFormat_UVLX8888
110 const char *vtf_format_strings
[] =
112 // Name // Supported?
152 // Flags from the *.txt config file
153 TEXTUREFLAGS_POINTSAMPLE
= 0x00000001,
154 TEXTUREFLAGS_TRILINEAR
= 0x00000002,
155 TEXTUREFLAGS_CLAMPS
= 0x00000004,
156 TEXTUREFLAGS_CLAMPT
= 0x00000008,
157 TEXTUREFLAGS_ANISOTROPIC
= 0x00000010,
158 TEXTUREFLAGS_HINT_DXT5
= 0x00000020,
159 TEXTUREFLAGS_PWL_CORRECTED
= 0x00000040,
160 TEXTUREFLAGS_NORMAL
= 0x00000080,
161 TEXTUREFLAGS_NOMIP
= 0x00000100,
162 TEXTUREFLAGS_NOLOD
= 0x00000200,
163 TEXTUREFLAGS_ALL_MIPS
= 0x00000400,
164 TEXTUREFLAGS_PROCEDURAL
= 0x00000800,
166 // These are automatically generated by vtex from the texture data.
167 TEXTUREFLAGS_ONEBITALPHA
= 0x00001000,
168 TEXTUREFLAGS_EIGHTBITALPHA
= 0x00002000,
170 // Newer flags from the *.txt config file
171 TEXTUREFLAGS_ENVMAP
= 0x00004000,
172 TEXTUREFLAGS_RENDERTARGET
= 0x00008000,
173 TEXTUREFLAGS_DEPTHRENDERTARGET
= 0x00010000,
174 TEXTUREFLAGS_NODEBUGOVERRIDE
= 0x00020000,
175 TEXTUREFLAGS_SINGLECOPY
= 0x00040000,
176 TEXTUREFLAGS_PRE_SRGB
= 0x00080000,
178 TEXTUREFLAGS_UNUSED_00100000
= 0x00100000,
179 TEXTUREFLAGS_UNUSED_00200000
= 0x00200000,
180 TEXTUREFLAGS_UNUSED_00400000
= 0x00400000,
182 TEXTUREFLAGS_NODEPTHBUFFER
= 0x00800000,
184 TEXTUREFLAGS_UNUSED_01000000
= 0x01000000,
186 TEXTUREFLAGS_CLAMPU
= 0x02000000,
187 TEXTUREFLAGS_VERTEXTEXTURE
= 0x04000000,
188 TEXTUREFLAGS_SSBUMP
= 0x08000000,
190 TEXTUREFLAGS_UNUSED_10000000
= 0x10000000,
192 TEXTUREFLAGS_BORDER
= 0x20000000,
194 TEXTUREFLAGS_UNUSED_40000000
= 0x40000000,
195 TEXTUREFLAGS_UNUSED_80000000
= 0x80000000,
198 typedef struct vtfheader
202 char signature
[4]; // File signature ("VTF\0"). (or as little-endian integer, 0x00465456)
206 unsigned int version
[2]; // version[0].version[1] (currently 7.2).
207 unsigned int headerSize
; // Size of the header struct (16 byte aligned; currently 80 bytes) + size of the resources dictionary (7.3+).
208 unsigned short width
; // Width of the largest mipmap in pixels. Must be a power of 2.
209 unsigned short height
; // Height of the largest mipmap in pixels. Must be a power of 2.
210 unsigned int flags
; // VTF flags.
211 unsigned short frames
; // Number of frames, if animated (1 for no animation).
212 unsigned short firstFrame
; // First frame in animation (0 based).
213 unsigned char padding0
[4]; // reflectivity padding (16 byte alignment).
214 float reflectivity
[3]; // reflectivity vector.
215 unsigned char padding1
[4]; // reflectivity padding (8 byte packing).
216 float bumpmapScale
; // Bumpmap scale.
217 unsigned int highResImageFormat
; // High resolution image format.
218 unsigned char mipmapCount
; // Number of mipmaps.
219 unsigned int lowResImageFormat
; // Low resolution image format (always DXT1).
220 unsigned char lowResImageWidth
; // Low resolution image width.
221 unsigned char lowResImageHeight
; // Low resolution image height.
224 unsigned short depth
; // Depth of the largest mipmap in pixels.
225 // Must be a power of 2. Can be 0 or 1 for a 2D texture (v7.2 only).
228 unsigned char padding2
[3]; // depth padding (4 byte alignment).
229 unsigned int numResources
; // Number of resources this vtf has
231 unsigned char padding3
[8]; // Necessary on certain compilers
236 typedef struct mipimg
243 int nbvtf_power2( uint32_t x
)
245 return (x
!= 0) && ((x
& (x
- 1)) == 0);
248 int nbvtf_power2x( uint32_t y
, uint32_t x
)
250 return nbvtf_power2( y
) && nbvtf_power2( x
);
253 int nbvtf_lower( int *x
, int *y
)
255 if( *x
== 1 && *y
== 1 )
260 *x
= nbvtf__max( 1, (*x
)/2 );
261 *y
= nbvtf__max( 1, (*y
)/2 );
266 int nbvtf_lowres_index( int w
, int h
)
276 if( (x
<= 16) && ( y
<= 16 ) )
283 nbvtf_lower( &x
, &y
);
287 // Simple box filter downscale
288 void nbvtf_downscale( uint8_t *src
, int w
, int h
, int dw
, int dh
, uint8_t *dest
)
294 for( int y
= 0; y
< dh
; y
++ )
295 for( int x
= 0; x
< dw
; x
++ )
297 // Average block colours
298 uint32_t tr
= 0, tg
= 0, tb
= 0, ta
= 0;
300 for( int yy
= 0; yy
< by
; yy
++ )
301 for( int xx
= 0; xx
< bx
; xx
++ )
303 uint8_t *psrc
= &src
[ (x
*bx
+xx
+ (y
*by
+yy
)*w
)*4 ];
310 uint8_t *pdst
= &dest
[ (y
*dw
+ x
)*4 ];
318 uint8_t *nbvtf_create_mipmaps( uint8_t *src
, int w
, int h
, mipimg_t
*offsets
, int *num
)
326 while( nbvtf_lower( &x
, &y
) )
329 uint8_t *mipmem
= (uint8_t *)malloc( memory
);
338 uint8_t *dest
= mipmem
;
342 if( !nbvtf_lower( &x
, &y
) )
345 nbvtf_downscale( src
, w
, h
, x
, y
, dest
);
347 offsets
[ i
].src_offset
= offset
;
353 dest
= mipmem
+ offset
;
361 NBVTF_ERR( "nbvtf_write:err out of memory allocating mipmap buffer!\n" );
366 void nbvtf_reflectivity( uint8_t *src
, int w
, int h
, float *dest
)
368 uint32_t totals
[3] = {0,0,0};
370 for( int i
= 0; i
< w
*h
; i
++ )
372 totals
[0] += src
[i
*4+0];
373 totals
[1] += src
[i
*4+1];
374 totals
[2] += src
[i
*4+2];
377 dest
[0] = (float)( totals
[0] / (w
*h
) ) / 255.0f
;
378 dest
[1] = (float)( totals
[1] / (w
*h
) ) / 255.0f
;
379 dest
[2] = (float)( totals
[2] / (w
*h
) ) / 255.0f
;
382 #ifdef NBVTF_ALLOW_EXPORT
383 void nbvtf_debug_view_mips( uint8_t *src
, int w
, int h
)
394 while( nbvtf_lower( &x
, &y
) )
396 sprintf( fnbuf
, "mip_%d.png", i
++ );
398 stbi_write_png( fnbuf
, x
,y
, 4, dest
, x
*4 );
404 void nbvtf_dxt_pad( uint8_t *src
, int bx
, int by
, int w
, int h
, uint8_t *dest
)
409 uint32_t *stream
= (uint32_t *)src
;
410 uint32_t *stream_out
= (uint32_t *)dest
;
412 for( int y
= 0; y
< 4; y
++ )
414 for( int x
= 0; x
< 4; x
++ )
416 stream_out
[ y
*4+x
] = stream
[ nbvtf__min( py
+y
, h
-1 )*w
+ nbvtf__min( px
+x
, w
-1 ) ];
421 uint32_t nbvtf_dxt_sizeimg( int w
, int h
, int alpha
)
423 uint32_t blocks_x
, blocks_y
;
424 int block_size
= alpha
? 16: 8;
426 blocks_x
= ((uint32_t)w
) >> 2;
427 blocks_y
= ((uint32_t)h
) >> 2;
429 int padx
= w
% 4 != 0? 1: 0;
430 int pady
= h
% 4 != 0? 1: 0;
432 return (blocks_x
+padx
)*(blocks_y
+pady
)*block_size
;
435 uint32_t nbvtf_sizeimg( int w
, int h
, EImageFormat_t format
)
437 if( format
== k_EImageFormat_DXT5
|| format
== k_EImageFormat_DXT1
)
439 return nbvtf_dxt_sizeimg( w
, h
, format
== k_EImageFormat_DXT1
? 0: 1 );
442 if( format
== k_EImageFormat_BGR888
)
445 if( format
== k_EImageFormat_ABGR8888
)
451 void nbvtf_dxt_block( uint8_t *dest
, uint8_t *src
, int alpha
)
454 // TODO: move this somewehre else
464 rgbcx__encode_bc3( 12, dest
, src
);
468 rgbcx__encode_bc1( 12, dest
, src
, 0, 0 );
471 stb_compress_dxt_block( dest
, src
, alpha
, STB_DXT_HIGHQUAL
);
475 void nbvtf_compress_dxt( uint8_t *src
, int w
, int h
, int alpha
, uint8_t *dest
)
477 uint32_t blocks_x
, blocks_y
;
479 blocks_x
= ((uint32_t)w
) >> 2;
480 blocks_y
= ((uint32_t)h
) >> 2;
482 int padx
= w
% 4 != 0? 1: 0;
483 int pady
= h
% 4 != 0? 1: 0;
485 int block_size
= alpha
? 16: 8;
487 uint8_t *dest_block
= dest
;
489 uint8_t working_block
[ 4*4*4 ];
492 for( int y
= 0; y
< blocks_y
; y
++ )
494 for( int x
= 0; x
< blocks_x
; x
++ )
496 uint8_t *src_begin
= src
+ (y
*w
*4 + x
*4)*4;
497 for( int i
= 0; i
< 4; i
++ )
499 memcpy( working_block
+ i
*4*4, src_begin
+ w
*4*i
, 4*4 );
502 nbvtf_dxt_block( dest_block
, working_block
, alpha
);
503 dest_block
+= block_size
;
508 nbvtf_dxt_pad( src
, blocks_x
, y
, w
, h
, working_block
);
509 nbvtf_dxt_block( dest_block
, working_block
, alpha
);
510 dest_block
+= block_size
;
514 // Compress remainder row
517 for( int x
= 0; x
< blocks_x
; x
++ )
519 nbvtf_dxt_pad( src
, x
, blocks_y
, w
, h
, working_block
);
520 nbvtf_dxt_block( dest_block
, working_block
, alpha
);
521 dest_block
+= block_size
;
525 // Compress last little corner
528 nbvtf_dxt_pad( src
, blocks_x
, blocks_y
, w
, h
, working_block
);
529 nbvtf_dxt_block( dest_block
, working_block
, alpha
);
533 void nbvtf_swizzle_to( uint8_t *src
, int n
, int nc
, uint8_t *dest
)
535 for( int i
= 0; i
< n
; i
++ )
537 for( int j
= 0; j
< nc
; j
++ )
539 dest
[ i
*nc
+nc
-j
-1 ] = src
[ i
*4+j
];
544 void nbvtf_write_img_data( uint8_t *src
, int w
, int h
, EImageFormat_t format
, uint8_t *wb
, FILE *file
)
548 case k_EImageFormat_DXT1
:
549 nbvtf_compress_dxt( src
, w
, h
, 0, wb
);
550 fwrite( wb
, nbvtf_dxt_sizeimg( w
, h
, 0 ), 1, file
);
552 case k_EImageFormat_DXT5
:
553 nbvtf_compress_dxt( src
, w
, h
, 1, wb
);
554 fwrite( wb
, nbvtf_dxt_sizeimg( w
, h
, 1 ), 1, file
);
556 case k_EImageFormat_ABGR8888
:
557 nbvtf_swizzle_to( src
, w
*h
, 4, wb
);
558 fwrite( wb
, w
*h
*4, 1, file
);
560 case k_EImageFormat_BGR888
:
561 nbvtf_swizzle_to( src
, w
*h
, 3, wb
);
562 fwrite( wb
, w
*h
*3, 1, file
);
571 __attribute__((visibility("default")))
573 int nbvtf_write( uint8_t *reference
, int w
, int h
, int mipmap
, EImageFormat_t format
, uint32_t usr_flags
, const char *dest
)
575 if( !nbvtf_power2x(w
,h
) )
577 NBVTF_ERR( "nbvtf_write:err image dimentions were not power of two (%d %d)\n", w
, h
);
581 mipimg_t mip_offsets
[ 16 ];
586 // Convert to directx normal
587 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
589 src
= malloc( w
*h
*4 );
590 for( int i
= 0; i
< w
*h
; i
++ )
592 src
[i
*4+0] = reference
[i
*4+0];
593 src
[i
*4+1] = 0xFF-reference
[i
*4+1];
594 src
[i
*4+2] = reference
[i
*4+2];
595 src
[i
*4+3] = reference
[i
*4+3];
601 uint8_t *mip_data
= nbvtf_create_mipmaps( src
, w
, h
, mip_offsets
, &num_mips
);
605 NBVTF_ERR( "nbvtf_write:err mipmap data failed to generate" );
607 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
613 vtfheader_t header
= {0};
615 header
.usig
= 0x00465456;
616 header
.headerSize
= sizeof( vtfheader_t
);
617 header
.version
[0] = 7;
618 header
.version
[1] = 2;
622 header
.flags
= usr_flags
;
624 // Append format flags
627 header
.flags
|= TEXTUREFLAGS_NOLOD
;
628 header
.flags
|= TEXTUREFLAGS_NOMIP
;
631 if( format
== k_EImageFormat_DXT5
|| format
== k_EImageFormat_ABGR8888
)
633 header
.flags
|= TEXTUREFLAGS_EIGHTBITALPHA
;
637 header
.firstFrame
= 0;
638 nbvtf_reflectivity( mip_data
+ mip_offsets
[ num_mips
-1 ].src_offset
, 1,1, header
.reflectivity
);
639 header
.bumpmapScale
= 1.0f
;
641 header
.highResImageFormat
= format
;
642 header
.mipmapCount
= mipmap
?
643 nbvtf__min(num_mips
,NBVTF_MAX_MIPLEVELS
)+1: 1;
645 header
.lowResImageFormat
= k_EImageFormat_DXT1
;
648 header
.numResources
= 0;
650 int lr_index
= nbvtf_lowres_index( w
, h
);
656 mipimg_t
*mip
= mip_offsets
+ (lr_index
-1);
657 lr_src
= mip_data
+ mip
->src_offset
;
659 header
.lowResImageWidth
= mip
->w
;
660 header
.lowResImageHeight
= mip
->h
;
666 header
.lowResImageWidth
= w
;
667 header
.lowResImageHeight
= h
;
670 uint32_t size_highres
= nbvtf_sizeimg( w
, h
, format
);
671 uint32_t size_lowres
= nbvtf_dxt_sizeimg( header
.lowResImageWidth
, header
.lowResImageHeight
, 0 );
673 uint8_t *working_buffer
= (uint8_t *)malloc( nbvtf__max( size_highres
, size_lowres
) );
675 if( !working_buffer
)
677 NBVTF_ERR( "nbvtf_write:err out of memory allocating working buffer\n" );
680 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
686 FILE *file
= fopen( dest
, "wb" );
690 NBVTF_ERR( "nbvtf_write:err could not open file stream for writing\n" );
692 free( working_buffer
);
695 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
702 fwrite( &header
, sizeof( vtfheader_t
), 1, file
);
705 nbvtf_write_img_data(
706 lr_src
, header
.lowResImageWidth
, header
.lowResImageHeight
, k_EImageFormat_DXT1
, working_buffer
, file
709 // Write texture data
712 // !! Experimental !!
713 int start
= nbvtf__max( 0, num_mips
-NBVTF_MAX_MIPLEVELS
);
715 for( int i
= start
; i
< num_mips
; i
++ )
717 mipimg_t
*mip
= mip_offsets
+ (num_mips
- i
-1);
718 nbvtf_write_img_data( mip_data
+ mip
->src_offset
, mip
->w
, mip
->h
, format
, working_buffer
, file
);
722 // Write high resolution
723 nbvtf_write_img_data( src
, w
, h
, format
, working_buffer
, file
);
727 free( working_buffer
);
730 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
737 __attribute__((visibility("default")))
739 int nbvtf_convert( const char *src
, int w
, int h
, int mipmap
, EImageFormat_t format
, uint32_t usr_flags
, const char *dest
)
741 if( (w
&& h
) && !nbvtf_power2x(w
,h
) )
743 NBVTF_ERR( "nbvtf_convert:err requested dimentions were not power of two (%d %d)\n", w
, h
);
748 uint8_t *data
= stbi_load( src
, &x
, &y
, &n
, 4 );
752 if( !nbvtf_power2x(x
,y
) )
754 NBVTF_ERR( "nbvtf_convert:err loaded image dimentions were not power two (%d %d)\n", x
, y
);
755 stbi_image_free( data
);
759 // Image size needs retargeting
760 if( (w
&& h
) && ( x
> w
|| y
> h
) )
761 nbvtf_downscale( data
, x
, y
, w
, h
, data
);
763 int status
= nbvtf_write( data
, w
, h
, mipmap
, format
, usr_flags
, dest
);
764 stbi_image_free( data
);
779 LICENSE - Applies to nbvtf.h, pynbvtf.py, librgbcx.cc, librgbcx.h, vtf_cmd.c
780 ------------------------------------------------------------------------------
781 Public Domain (www.unlicense.org)
782 This is free and unencumbered software released into the public domain.
783 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
784 software, either in source code form or as a compiled binary, for any purpose,
785 commercial or non-commercial, and by any means.
786 In jurisdictions that recognize copyright laws, the author or authors of this
787 software dedicate any and all copyright interest in the software to the public
788 domain. We make this dedication for the benefit of the public at large and to
789 the detriment of our heirs and successors. We intend this dedication to be an
790 overt act of relinquishment in perpetuity of all present and future rights to
791 this software under copyright law.
792 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
793 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
794 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
795 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
796 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
797 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
798 ------------------------------------------------------------------------------