1 // nbvtf.h - v1.03 - 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
29 // qual - Image quality 0-18 (rgbcx, stb always highqual)
32 // src - file path of source image to convert
33 // w, h - MAXIMUM image dimentions of final product. Set as 0 to be automatic
36 // v1.03 - Added quality switch
37 // v1.02 - Improved box filtering, small bug fixes
38 // v1.01 - switch to OpenGL normal format for input
39 // v1.00 - (hgn) first release
42 // See end of file for license information.
48 #define NBVTF_MAX_MIPLEVELS 9
49 #define nbvtf__min(a,b) (((a)<(b))?(a):(b))
50 #define nbvtf__max(a,b) (((a)>(b))?(a):(b))
58 #define NBVTF_SHOW_STDERR
59 #define STB_IMAGE_IMPLEMENTATION
62 #include "stb/stb_image.h"
67 #define STB_DXT_IMPLEMENTATION
68 #include "stb/stb_dxt.h"
71 #ifdef NBVTF_SHOW_STDERR
72 #define NBVTF_ERR(...)
74 #define NBVTF_ERR(...) fprintf( stderr, __VA_ARGS__ )
79 typedef enum EImageFormat
82 k_EImageFormat_NONE
= -1,
83 k_EImageFormat_RGBA8888
= 0, // YES
84 k_EImageFormat_ABGR8888
,
85 k_EImageFormat_RGB888
, // YES
86 k_EImageFormat_BGR888
,
87 k_EImageFormat_RGB565
,
92 k_EImageFormat_RGB888_BLUESCREEN
,
93 k_EImageFormat_BGR888_BLUESCREEN
,
94 k_EImageFormat_ARGB8888
,
95 k_EImageFormat_BGRA8888
,
96 k_EImageFormat_DXT1
, // YES
98 k_EImageFormat_DXT5
, // YES
99 k_EImageFormat_BGRX8888
,
100 k_EImageFormat_BGR565
,
101 k_EImageFormat_BGRX5551
,
102 k_EImageFormat_BGRA4444
,
103 k_EImageFormat_DXT1_ONEBITALPHA
,
104 k_EImageFormat_BGRA5551
,
106 k_EImageFormat_UVWQ8888
,
107 k_EImageFormat_RGBA16161616F
,
108 k_EImageFormat_RGBA16161616
,
109 k_EImageFormat_UVLX8888
112 const char *vtf_format_strings
[] =
114 // Name // Supported?
154 // Flags from the *.txt config file
155 TEXTUREFLAGS_POINTSAMPLE
= 0x00000001,
156 TEXTUREFLAGS_TRILINEAR
= 0x00000002,
157 TEXTUREFLAGS_CLAMPS
= 0x00000004,
158 TEXTUREFLAGS_CLAMPT
= 0x00000008,
159 TEXTUREFLAGS_ANISOTROPIC
= 0x00000010,
160 TEXTUREFLAGS_HINT_DXT5
= 0x00000020,
161 TEXTUREFLAGS_PWL_CORRECTED
= 0x00000040,
162 TEXTUREFLAGS_NORMAL
= 0x00000080,
163 TEXTUREFLAGS_NOMIP
= 0x00000100,
164 TEXTUREFLAGS_NOLOD
= 0x00000200,
165 TEXTUREFLAGS_ALL_MIPS
= 0x00000400,
166 TEXTUREFLAGS_PROCEDURAL
= 0x00000800,
168 // These are automatically generated by vtex from the texture data.
169 TEXTUREFLAGS_ONEBITALPHA
= 0x00001000,
170 TEXTUREFLAGS_EIGHTBITALPHA
= 0x00002000,
172 // Newer flags from the *.txt config file
173 TEXTUREFLAGS_ENVMAP
= 0x00004000,
174 TEXTUREFLAGS_RENDERTARGET
= 0x00008000,
175 TEXTUREFLAGS_DEPTHRENDERTARGET
= 0x00010000,
176 TEXTUREFLAGS_NODEBUGOVERRIDE
= 0x00020000,
177 TEXTUREFLAGS_SINGLECOPY
= 0x00040000,
178 TEXTUREFLAGS_PRE_SRGB
= 0x00080000,
180 TEXTUREFLAGS_UNUSED_00100000
= 0x00100000,
181 TEXTUREFLAGS_UNUSED_00200000
= 0x00200000,
182 TEXTUREFLAGS_UNUSED_00400000
= 0x00400000,
184 TEXTUREFLAGS_NODEPTHBUFFER
= 0x00800000,
186 TEXTUREFLAGS_UNUSED_01000000
= 0x01000000,
188 TEXTUREFLAGS_CLAMPU
= 0x02000000,
189 TEXTUREFLAGS_VERTEXTEXTURE
= 0x04000000,
190 TEXTUREFLAGS_SSBUMP
= 0x08000000,
192 TEXTUREFLAGS_UNUSED_10000000
= 0x10000000,
194 TEXTUREFLAGS_BORDER
= 0x20000000,
196 TEXTUREFLAGS_UNUSED_40000000
= 0x40000000,
197 TEXTUREFLAGS_UNUSED_80000000
= 0x80000000,
200 typedef struct vtfheader
204 char signature
[4]; // File signature ("VTF\0"). (or as little-endian integer, 0x00465456)
208 unsigned int version
[2]; // version[0].version[1] (currently 7.2).
209 unsigned int headerSize
; // Size of the header struct (16 byte aligned; currently 80 bytes) + size of the resources dictionary (7.3+).
210 unsigned short width
; // Width of the largest mipmap in pixels. Must be a power of 2.
211 unsigned short height
; // Height of the largest mipmap in pixels. Must be a power of 2.
212 unsigned int flags
; // VTF flags.
213 unsigned short frames
; // Number of frames, if animated (1 for no animation).
214 unsigned short firstFrame
; // First frame in animation (0 based).
215 unsigned char padding0
[4]; // reflectivity padding (16 byte alignment).
216 float reflectivity
[3]; // reflectivity vector.
217 unsigned char padding1
[4]; // reflectivity padding (8 byte packing).
218 float bumpmapScale
; // Bumpmap scale.
219 unsigned int highResImageFormat
; // High resolution image format.
220 unsigned char mipmapCount
; // Number of mipmaps.
221 unsigned int lowResImageFormat
; // Low resolution image format (always DXT1).
222 unsigned char lowResImageWidth
; // Low resolution image width.
223 unsigned char lowResImageHeight
; // Low resolution image height.
226 unsigned short depth
; // Depth of the largest mipmap in pixels.
227 // Must be a power of 2. Can be 0 or 1 for a 2D texture (v7.2 only).
230 unsigned char padding2
[3]; // depth padding (4 byte alignment).
231 unsigned int numResources
; // Number of resources this vtf has
233 unsigned char padding3
[8]; // Necessary on certain compilers
238 typedef struct mipimg
245 int nbvtf_power2( uint32_t x
)
247 return (x
!= 0) && ((x
& (x
- 1)) == 0);
250 int nbvtf_power2x( uint32_t y
, uint32_t x
)
252 return nbvtf_power2( y
) && nbvtf_power2( x
);
255 int nbvtf_lower( int *x
, int *y
)
257 if( *x
== 1 && *y
== 1 )
262 *x
= nbvtf__max( 1, (*x
)/2 );
263 *y
= nbvtf__max( 1, (*y
)/2 );
268 int nbvtf_lowres_index( int w
, int h
)
278 if( (x
<= 16) && ( y
<= 16 ) )
285 nbvtf_lower( &x
, &y
);
289 // Simple box filter downscale
290 void nbvtf_downscale( uint8_t *src
, int w
, int h
, int dw
, int dh
, uint8_t *dest
)
296 for( int y
= 0; y
< dh
; y
++ )
297 for( int x
= 0; x
< dw
; x
++ )
299 // Average block colours
300 uint32_t tr
= 0, tg
= 0, tb
= 0, ta
= 0;
302 for( int yy
= 0; yy
< by
; yy
++ )
303 for( int xx
= 0; xx
< bx
; xx
++ )
305 uint8_t *psrc
= &src
[ (x
*bx
+xx
+ (y
*by
+yy
)*w
)*4 ];
312 uint8_t *pdst
= &dest
[ (y
*dw
+ x
)*4 ];
320 uint8_t *nbvtf_create_mipmaps( uint8_t *src
, int w
, int h
, mipimg_t
*offsets
, int *num
)
328 while( nbvtf_lower( &x
, &y
) )
331 uint8_t *mipmem
= (uint8_t *)malloc( memory
);
340 uint8_t *dest
= mipmem
;
344 if( !nbvtf_lower( &x
, &y
) )
347 nbvtf_downscale( src
, w
, h
, x
, y
, dest
);
349 offsets
[ i
].src_offset
= offset
;
355 dest
= mipmem
+ offset
;
363 NBVTF_ERR( "nbvtf_write:err out of memory allocating mipmap buffer!\n" );
368 void nbvtf_reflectivity( uint8_t *src
, int w
, int h
, float *dest
)
370 uint32_t totals
[3] = {0,0,0};
372 for( int i
= 0; i
< w
*h
; i
++ )
374 totals
[0] += src
[i
*4+0];
375 totals
[1] += src
[i
*4+1];
376 totals
[2] += src
[i
*4+2];
379 dest
[0] = (float)( totals
[0] / (w
*h
) ) / 255.0f
;
380 dest
[1] = (float)( totals
[1] / (w
*h
) ) / 255.0f
;
381 dest
[2] = (float)( totals
[2] / (w
*h
) ) / 255.0f
;
384 #ifdef NBVTF_ALLOW_EXPORT
385 void nbvtf_debug_view_mips( uint8_t *src
, int w
, int h
)
396 while( nbvtf_lower( &x
, &y
) )
398 sprintf( fnbuf
, "mip_%d.png", i
++ );
400 stbi_write_png( fnbuf
, x
,y
, 4, dest
, x
*4 );
406 void nbvtf_dxt_pad( uint8_t *src
, int bx
, int by
, int w
, int h
, uint8_t *dest
)
411 uint32_t *stream
= (uint32_t *)src
;
412 uint32_t *stream_out
= (uint32_t *)dest
;
414 for( int y
= 0; y
< 4; y
++ )
416 for( int x
= 0; x
< 4; x
++ )
418 stream_out
[ y
*4+x
] = stream
[ nbvtf__min( py
+y
, h
-1 )*w
+ nbvtf__min( px
+x
, w
-1 ) ];
423 uint32_t nbvtf_dxt_sizeimg( int w
, int h
, int alpha
)
425 uint32_t blocks_x
, blocks_y
;
426 int block_size
= alpha
? 16: 8;
428 blocks_x
= ((uint32_t)w
) >> 2;
429 blocks_y
= ((uint32_t)h
) >> 2;
431 int padx
= w
% 4 != 0? 1: 0;
432 int pady
= h
% 4 != 0? 1: 0;
434 return (blocks_x
+padx
)*(blocks_y
+pady
)*block_size
;
437 uint32_t nbvtf_sizeimg( int w
, int h
, EImageFormat_t format
)
439 if( format
== k_EImageFormat_DXT5
|| format
== k_EImageFormat_DXT1
)
441 return nbvtf_dxt_sizeimg( w
, h
, format
== k_EImageFormat_DXT1
? 0: 1 );
444 if( format
== k_EImageFormat_BGR888
)
447 if( format
== k_EImageFormat_ABGR8888
)
453 void nbvtf_dxt_block( uint8_t *dest
, uint8_t *src
, int alpha
, int qual
)
456 // TODO: move this somewehre else
466 rgbcx__encode_bc3( qual
, dest
, src
);
470 rgbcx__encode_bc1( qual
, dest
, src
, 0, 0 );
473 stb_compress_dxt_block( dest
, src
, alpha
, STB_DXT_HIGHQUAL
);
477 void nbvtf_compress_dxt( uint8_t *src
, int w
, int h
, int alpha
, int qual
,
480 uint32_t blocks_x
, blocks_y
;
482 blocks_x
= ((uint32_t)w
) >> 2;
483 blocks_y
= ((uint32_t)h
) >> 2;
485 int padx
= w
% 4 != 0? 1: 0;
486 int pady
= h
% 4 != 0? 1: 0;
488 int block_size
= alpha
? 16: 8;
490 uint8_t *dest_block
= dest
;
492 uint8_t working_block
[ 4*4*4 ];
495 for( int y
= 0; y
< blocks_y
; y
++ )
497 for( int x
= 0; x
< blocks_x
; x
++ )
499 uint8_t *src_begin
= src
+ (y
*w
*4 + x
*4)*4;
500 for( int i
= 0; i
< 4; i
++ )
502 memcpy( working_block
+ i
*4*4, src_begin
+ w
*4*i
, 4*4 );
505 nbvtf_dxt_block( dest_block
, working_block
, alpha
, qual
);
506 dest_block
+= block_size
;
511 nbvtf_dxt_pad( src
, blocks_x
, y
, w
, h
, working_block
);
512 nbvtf_dxt_block( dest_block
, working_block
, alpha
, qual
);
513 dest_block
+= block_size
;
517 // Compress remainder row
520 for( int x
= 0; x
< blocks_x
; x
++ )
522 nbvtf_dxt_pad( src
, x
, blocks_y
, w
, h
, working_block
);
523 nbvtf_dxt_block( dest_block
, working_block
, alpha
, qual
);
524 dest_block
+= block_size
;
528 // Compress last little corner
531 nbvtf_dxt_pad( src
, blocks_x
, blocks_y
, w
, h
, working_block
);
532 nbvtf_dxt_block( dest_block
, working_block
, alpha
, qual
);
536 void nbvtf_swizzle_to( uint8_t *src
, int n
, int nc
, uint8_t *dest
)
538 for( int i
= 0; i
< n
; i
++ )
540 for( int j
= 0; j
< nc
; j
++ )
542 dest
[ i
*nc
+nc
-j
-1 ] = src
[ i
*4+j
];
547 void nbvtf_write_img_data( uint8_t *src
, int w
, int h
,
548 EImageFormat_t format
, int qual
, uint8_t *wb
, FILE *file
)
552 case k_EImageFormat_DXT1
:
553 nbvtf_compress_dxt( src
, w
, h
, 0, qual
, wb
);
554 fwrite( wb
, nbvtf_dxt_sizeimg( w
, h
, 0 ), 1, file
);
556 case k_EImageFormat_DXT5
:
557 nbvtf_compress_dxt( src
, w
, h
, 1, qual
, wb
);
558 fwrite( wb
, nbvtf_dxt_sizeimg( w
, h
, 1 ), 1, file
);
560 case k_EImageFormat_ABGR8888
:
561 nbvtf_swizzle_to( src
, w
*h
, 4, wb
);
562 fwrite( wb
, w
*h
*4, 1, file
);
564 case k_EImageFormat_BGR888
:
565 nbvtf_swizzle_to( src
, w
*h
, 3, wb
);
566 fwrite( wb
, w
*h
*3, 1, file
);
575 __attribute__((visibility("default")))
577 int nbvtf_write( uint8_t *reference
, int w
, int h
, int mipmap
,
578 EImageFormat_t format
, int qual
, uint32_t usr_flags
, const char *dest
)
580 if( !nbvtf_power2x(w
,h
) )
582 NBVTF_ERR( "nbvtf_write:err image dimentions were not power of two (%d %d)\n", w
, h
);
586 mipimg_t mip_offsets
[ 16 ];
591 // Convert to directx normal
592 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
594 src
= malloc( w
*h
*4 );
595 for( int i
= 0; i
< w
*h
; i
++ )
597 src
[i
*4+0] = reference
[i
*4+0];
598 src
[i
*4+1] = 0xFF-reference
[i
*4+1];
599 src
[i
*4+2] = reference
[i
*4+2];
600 src
[i
*4+3] = reference
[i
*4+3];
606 uint8_t *mip_data
= nbvtf_create_mipmaps( src
, w
, h
, mip_offsets
, &num_mips
);
610 NBVTF_ERR( "nbvtf_write:err mipmap data failed to generate" );
612 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
618 vtfheader_t header
= {0};
620 header
.usig
= 0x00465456;
621 header
.headerSize
= sizeof( vtfheader_t
);
622 header
.version
[0] = 7;
623 header
.version
[1] = 2;
627 header
.flags
= usr_flags
;
629 // Append format flags
632 header
.flags
|= TEXTUREFLAGS_NOLOD
;
633 header
.flags
|= TEXTUREFLAGS_NOMIP
;
636 if( format
== k_EImageFormat_DXT5
|| format
== k_EImageFormat_ABGR8888
)
638 header
.flags
|= TEXTUREFLAGS_EIGHTBITALPHA
;
642 header
.firstFrame
= 0;
643 nbvtf_reflectivity( mip_data
+ mip_offsets
[ num_mips
-1 ].src_offset
, 1,1, header
.reflectivity
);
644 header
.bumpmapScale
= 1.0f
;
646 header
.highResImageFormat
= format
;
647 header
.mipmapCount
= mipmap
?
648 nbvtf__min(num_mips
,NBVTF_MAX_MIPLEVELS
)+1: 1;
650 header
.lowResImageFormat
= k_EImageFormat_DXT1
;
653 header
.numResources
= 0;
655 int lr_index
= nbvtf_lowres_index( w
, h
);
661 mipimg_t
*mip
= mip_offsets
+ (lr_index
-1);
662 lr_src
= mip_data
+ mip
->src_offset
;
664 header
.lowResImageWidth
= mip
->w
;
665 header
.lowResImageHeight
= mip
->h
;
671 header
.lowResImageWidth
= w
;
672 header
.lowResImageHeight
= h
;
675 uint32_t size_highres
= nbvtf_sizeimg( w
, h
, format
);
676 uint32_t size_lowres
=
677 nbvtf_dxt_sizeimg( header
.lowResImageWidth
, header
.lowResImageHeight
, 0 );
679 uint8_t *working_buffer
=
680 (uint8_t *)malloc( nbvtf__max( size_highres
, size_lowres
) );
682 if( !working_buffer
)
684 NBVTF_ERR( "nbvtf_write:err out of memory allocating working buffer\n" );
687 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
693 FILE *file
= fopen( dest
, "wb" );
697 NBVTF_ERR( "nbvtf_write:err could not open file stream for writing\n" );
699 free( working_buffer
);
702 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
709 fwrite( &header
, sizeof( vtfheader_t
), 1, file
);
712 nbvtf_write_img_data(
713 lr_src
, header
.lowResImageWidth
, header
.lowResImageHeight
,
714 k_EImageFormat_DXT1
, qual
, working_buffer
, file
717 // Write texture data
720 // !! Experimental !!
721 int start
= nbvtf__max( 0, num_mips
-NBVTF_MAX_MIPLEVELS
);
723 for( int i
= start
; i
< num_mips
; i
++ )
725 mipimg_t
*mip
= mip_offsets
+ (num_mips
- i
-1);
726 nbvtf_write_img_data( mip_data
+ mip
->src_offset
, mip
->w
, mip
->h
,
727 format
, qual
, working_buffer
, file
);
731 // Write high resolution
732 nbvtf_write_img_data( src
, w
, h
, format
, qual
, working_buffer
, file
);
736 free( working_buffer
);
739 if( usr_flags
& TEXTUREFLAGS_NORMAL
)
746 __attribute__((visibility("default")))
748 int nbvtf_convert( const char *src
, int w
, int h
, int mipmap
,
749 EImageFormat_t format
, int qual
, uint32_t usr_flags
, const char *dest
)
751 if( (w
&& h
) && !nbvtf_power2x(w
,h
) )
753 NBVTF_ERR( "nbvtf_convert:err requested dimentions were not power of two (%d %d)\n", w
, h
);
758 uint8_t *data
= stbi_load( src
, &x
, &y
, &n
, 4 );
762 if( !nbvtf_power2x(x
,y
) )
764 NBVTF_ERR( "nbvtf_convert:err loaded image dimentions were not power two (%d %d)\n", x
, y
);
765 stbi_image_free( data
);
769 // Image size needs retargeting
770 if( (w
&& h
) && ( x
> w
|| y
> h
) )
771 nbvtf_downscale( data
, x
, y
, w
, h
, data
);
773 int status
= nbvtf_write( data
, w
, h
, mipmap
, format
, qual
,
776 stbi_image_free( data
);
791 LICENSE - Applies to nbvtf.h, pynbvtf.py, librgbcx.cc, librgbcx.h, vtf_cmd.c
792 ------------------------------------------------------------------------------
793 Public Domain (www.unlicense.org)
794 This is free and unencumbered software released into the public domain.
795 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
796 software, either in source code form or as a compiled binary, for any purpose,
797 commercial or non-commercial, and by any means.
798 In jurisdictions that recognize copyright laws, the author or authors of this
799 software dedicate any and all copyright interest in the software to the public
800 domain. We make this dedication for the benefit of the public at large and to
801 the detriment of our heirs and successors. We intend this dedication to be an
802 overt act of relinquishment in perpetuity of all present and future rights to
803 this software under copyright law.
804 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
805 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
806 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
807 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
808 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
809 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
810 ------------------------------------------------------------------------------