From: hgn Date: Thu, 28 Apr 2022 16:06:18 +0000 (+0100) Subject: Update to CMake, tweaks & dds X-Git-Url: https://harrygodden.com/git/?p=convexer.git;a=commitdiff_plain;h=05e7fa40fd47dd5bfeaa1de9e9eba73319ae8564 Update to CMake, tweaks & dds --- diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e8c83fa..0000000 --- a/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -* -!*/ - -!.gitignore -!*.c -!*.cc -!*.cpp -!*.hpp -!*.h -!*.py - -!Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..24260da --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) + +project( convexer ) + +set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) + +add_subdirectory( cxr ) +add_subdirectory( nbvtf ) diff --git a/Makefile b/Makefile deleted file mode 100644 index 9a4c41c..0000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -all: objdir libcxr.so libnbvtf.so - -objdir: - mkdir -p nbvtf/obj - -libcxr.so: cxr/cxr.h cxr/cxr_math.h cxr/cxr_mem.h - gcc -O1 -ggdb -fPIC -shared \ - -Wall -Wno-unused-variable -Wno-unused-function -std=c99 -pedantic \ - -DCXR_SO -DCXR_DEBUG -DCXR_VALVE_MAP_FILE \ - -xc cxr/cxr.h \ - -o libcxr.so \ - -lm - -tovtf: nbvtf/obj/librgbcx.o nbvtf/obj/tovtf.o - g++ -O3 \ - -Wno-unused-variable -Wno-unused-function -fsanitize=address -Werror=vla \ - nbvtf/obj/tovtf.o nbvtf/obj/librgbcx.o \ - -o tovtf - -nbvtf/obj/librgbcx.o: nbvtf/librgbcx.cc nbvtf/rgbcx.h - g++ -O3 -c \ - nbvtf/librgbcx.cc \ - -o nbvtf/obj/librgbcx.o - -nbvtf/obj/tovtf.o: nbvtf/vtf_cmd.c nbvtf/nbvtf.h - gcc -O3 -c \ - -DUSE_LIBRGBCX \ - -I./nbvtf/ \ - nbvtf/vtf_cmd.c \ - -o nbvtf/obj/tovtf.o - -nbvtf/obj/libnbvtf.o: nbvtf/nbvtf.h - gcc -O3 -fPIC -c \ - -DUSE_LIBRGBCX -DNBVTF_AS_SO \ - -xc nbvtf/nbvtf.h \ - -o nbvtf/obj/libnbvtf.o - -libnbvtf.so: nbvtf/obj/librgbcx.o nbvtf/obj/libnbvtf.o - g++ -O3 -shared \ - nbvtf/obj/librgbcx.o nbvtf/obj/libnbvtf.o \ - -o libnbvtf.so - -test: cxr/test.c cxr/cxr.h cxr/cxr_math.h cxr/solid.h - gcc -ggdb -O1 -Wall \ - -Wno-unused-variable -Wno-unused-function -fsanitize=address -Werror=vla \ - cxr/test.c \ - -o test \ - -lm diff --git a/__init__.py b/__init__.py index b136c39..7ba2584 100644 --- a/__init__.py +++ b/__init__.py @@ -255,14 +255,20 @@ class cxr_tri_mesh(Structure): ("indices_count",c_int32), ("vertex_count",c_int32)] +class cxr_visgroup(Structure): + _fields_ = [("name",c_char_p)] + class cxr_vmf_context(Structure): _fields_ = [("mapversion",c_int32), ("skyname",c_char_p), ("detailvbsp",c_char_p), ("detailmaterial",c_char_p), + ("visgroups",POINTER(cxr_visgroup)), + ("visgroup_count",c_int32), ("scale",c_double), ("offset",c_double *3), ("lightmap_scale",c_int32), + ("visgroupid",c_int32), ("brush_count",c_int32), ("entity_count",c_int32), ("face_count",c_int32)] @@ -556,6 +562,16 @@ def cxr_baseclass(classes, other): base.update(x.copy()) return base +def ent_soundscape(context): + obj = context['object'] + kvs = cxr_baseclass([ent_origin],\ + { + "radius": obj.scale.x * bpy.context.scene.cxr_data.scale_factor, + "soundscape": {"type":"string","default":""} + }) + + return kvs + # EEVEE Light component converter -> Source 1 # def ent_lights(context): @@ -1036,6 +1052,12 @@ def cxr_export_vmf(sceneinfo, output_vmf): vmfinfo.entity_count = 0 vmfinfo.face_count = 0 + visgroups = (cxr_visgroup*len(cxr_visgroups))() + for i, vg in enumerate(cxr_visgroups): + visgroups[i].name = vg.encode('utf-8') + vmfinfo.visgroups = cast(visgroups, POINTER(cxr_visgroup)) + vmfinfo.visgroup_count = len(cxr_visgroups) + libcxr_begin_vmf.call( pointer(vmfinfo), m.fp ) def _buildsolid( cmd ): @@ -1056,6 +1078,11 @@ def cxr_export_vmf(sceneinfo, output_vmf): vmfinfo.offset[1] = offset[1] vmfinfo.offset[2] = offset[2] + if cmd['object'].cxr_data.lightmap_override > 0: + vmfinfo.lightmap_scale = cmd['object'].cxr_data.lightmap_override + else: + vmfinfo.lightmap_scale = bpy.context.scene.cxr_data.lightmap_scale + libcxr_push_world_vmf.call( world, pointer(vmfinfo), m.fp ) libcxr_free_world.call( world ) @@ -1063,10 +1090,12 @@ def cxr_export_vmf(sceneinfo, output_vmf): # World geometry for brush in sceneinfo['geo']: + vmfinfo.visgroupid = int(brush['object'].cxr_data.visgroup) if not _buildsolid( brush ): cxr_batch_lines() scene_redraw() return False + vmfinfo.visgroupid = 0 libcxr_vmf_begin_entities.call(pointer(vmfinfo), m.fp) @@ -1090,12 +1119,21 @@ def cxr_export_vmf(sceneinfo, output_vmf): pass elif not isinstance( obj, bpy.types.Collection ): if obj.type == 'MESH': + vmfinfo.visgroupid = int(obj.cxr_data.visgroup) if not _buildsolid( ent ): cxr_batch_lines() scene_redraw() return False + if obj != None: + m.node( 'editor' ) + m.kv( 'visgroupid', str(obj.cxr_data.visgroup) ) + m.kv( 'visgroupshown', '1' ) + m.kv( 'visgroupautoshown', '1' ) + m.edon() + m.edon() + vmfinfo.visgroupid = 0 print( "Done" ) return True @@ -1829,33 +1867,38 @@ class CXR_COMPILER_CHAIN(bpy.types.Operator): # VBSP stage if settings.comp_compile: - static.JOBINFO += [{ - "title": "VBSP", - "w": 25, - "colour": (0.1,0.2,1.0,1.0), - "exec": "vbsp", - "jobs": [[settings[F'exe_vbsp']] + args], - "cwd": directory - }] + if not settings.opt_vbsp.startswith( 'disable' ): + vbsp_opt = settings.opt_vbsp.split() + static.JOBINFO += [{ + "title": "VBSP", + "w": 25, + "colour": (0.1,0.2,1.0,1.0), + "exec": "vbsp", + "jobs": [[settings[F'exe_vbsp']] + vbsp_opt + args], + "cwd": directory + }] - static.JOBINFO += [{ - "title": "VVIS", - "w": 25, - "colour": (0.9,0.5,0.5,1.0), - "exec": "vvis", - "jobs": [[settings[F'exe_vvis']] + ['-fast'] + args ], - "cwd": directory - }] + if not settings.opt_vvis.startswith( 'disable' ): + vvis_opt = settings.opt_vvis.split() + static.JOBINFO += [{ + "title": "VVIS", + "w": 25, + "colour": (0.9,0.5,0.5,1.0), + "exec": "vvis", + "jobs": [[settings[F'exe_vvis']] + vvis_opt + args ], + "cwd": directory + }] - vrad_opt = settings.opt_vrad.split() - static.JOBINFO += [{ - "title": "VRAD", - "w": 25, - "colour": (0.9,0.2,0.3,1.0), - "exec": "vrad", - "jobs": [[settings[F'exe_vrad']] + vrad_opt + args ], - "cwd": directory - }] + if not settings.opt_vrad.startswith( 'disable' ): + vrad_opt = settings.opt_vrad.split() + static.JOBINFO += [{ + "title": "VRAD", + "w": 25, + "colour": (0.9,0.2,0.3,1.0), + "exec": "vrad", + "jobs": [[settings[F'exe_vrad']] + vrad_opt + args ], + "cwd": directory + }] static.JOBINFO += [{ "title": "CXR", @@ -1998,7 +2041,11 @@ class CXR_INTERFACE(bpy.types.Panel): box.operator("convexer.detect_compilers") box.prop(settings, "exe_studiomdl") box.prop(settings, "exe_vbsp") + box.prop(settings, "opt_vbsp") + box.prop(settings, "exe_vvis") + box.prop(settings, "opt_vvis") + box.prop(settings, "exe_vrad") box.prop(settings, "opt_vrad") @@ -2132,7 +2179,7 @@ def cxr_entity_changeclass(_,context): entdef = cxr_entities[classname] kvs = entdef['keyvalues'] - if callable(kvs): kvs = kvs(active_object) + if callable(kvs): kvs = kvs( {'object': active_object} ) for k in kvs: kv = kvs[k] @@ -2170,6 +2217,9 @@ class CXR_ENTITY_PANEL(bpy.types.Panel): _.layout.prop( active_object.cxr_data, 'brushclass' ) else: _.layout.prop( active_object.cxr_data, 'classname' ) + _.layout.prop( active_object.cxr_data, 'visgroup' ) + _.layout.prop( active_object.cxr_data, 'lightmap_override' ) + if classname == 'NONE': return else: @@ -2236,6 +2286,7 @@ class CXR_COLLECTION_PANEL(bpy.types.Panel): layout.prop( active_collection.cxr_data, "texture_shadows" ) layout.prop( active_collection.cxr_data, "preserve_order" ) layout.prop( active_collection.cxr_data, "surfaceprop" ) + layout.prop( active_collection.cxr_data, "visgroup" ) # Settings groups # ------------------------------------------------------------------------------ @@ -2306,6 +2357,12 @@ class CXR_ENTITY_SETTINGS(bpy.types.PropertyGroup): brushclass: bpy.props.EnumProperty(items=enum_brushents, name="Class", \ update=cxr_entity_changeclass, default='NONE' ) + + enum_classes = [('0',"None","")] + for i, vg in enumerate(cxr_visgroups): + enum_classes += [(str(i+1),vg,"")] + visgroup: bpy.props.EnumProperty(name="visgroup",items=enum_classes,default=0) + lightmap_override: bpy.props.IntProperty(name="Lightmap Override",default=0) class CXR_MODEL_SETTINGS(bpy.types.PropertyGroup): last_hash: bpy.props.StringProperty( name="" ) @@ -2315,6 +2372,11 @@ class CXR_MODEL_SETTINGS(bpy.types.PropertyGroup): preserve_order: bpy.props.BoolProperty( name="Preserve Order", default=False ) surfaceprop: bpy.props.StringProperty( name="Suface prop",default="default" ) + enum_classes = [('0',"None","")] + for i, vg in enumerate(cxr_visgroups): + enum_classes += [(str(i+1),vg,"")] + visgroup: bpy.props.EnumProperty(name="visgroup",items=enum_classes,default=0) + class CXR_SCENE_SETTINGS(bpy.types.PropertyGroup): project_name: bpy.props.StringProperty( name="Project Name" ) subdir: bpy.props.StringProperty( name="Subdirectory" ) diff --git a/config.py b/config.py index 5ee294d..cb5597c 100644 --- a/config.py +++ b/config.py @@ -234,6 +234,12 @@ cxr_shader_params = \ "type": "bool", "default": False } + }, + "$surfaceprop": + { + "name": "Surface", + "type": "string", + "default": "" } } @@ -303,7 +309,18 @@ cxr_entities = \ "damage": { "type":"int", "default": 10}, "damagecap": { "type":"int", "default": 20}, "damagetype": { "type":"int", "default": 0}, - "damagemodel": { "type":"int", "default": 0} + "damagemodel": { "type":"int", "default": 0}, + "damagetype": {"type":"int","default":0}, + "nodmgforce": {"type":"int","default":0}, + "spawnflags": {"type":"int","default":4097}, + "StartDisabled": {"type":"int","default":0} } + }, + "env_soundscape": + { + "allow": ('EMPTY',), + "keyvalues": ent_soundscape } } + +cxr_visgroups = ['layout','overlap','remove','cover','user0','user1','user2','user3'] diff --git a/cxr/CMakeLists.txt b/cxr/CMakeLists.txt new file mode 100644 index 0000000..eb0455f --- /dev/null +++ b/cxr/CMakeLists.txt @@ -0,0 +1,19 @@ +project( cxr ) + +add_library( ${PROJECT_NAME} SHARED + cxr.c + cxr.h + cxr_math.h + cxr_mem.h +) + +target_compile_definitions( ${PROJECT_NAME} + PRIVATE CXR_SO CXR_DEBUG CXR_VALVE_MAP_FILE +) + +target_compile_options( ${PROJECT_NAME} + PRIVATE -Wall -Wno-unused-variable -Wno-unused-function + -std=c99 -pedantic +) + +target_link_libraries( ${PROJECT_NAME} m ) diff --git a/cxr/cxr.c b/cxr/cxr.c new file mode 100644 index 0000000..fa7b54c --- /dev/null +++ b/cxr/cxr.c @@ -0,0 +1,6 @@ +/* This file is purely to get CMake to shut up + * + * I don't know how to emulate gcc -xc behaviour + * + */ +#include "cxr.h" diff --git a/cxr/cxr.h b/cxr/cxr.h index a664313..1f1c649 100644 --- a/cxr/cxr.h +++ b/cxr/cxr.h @@ -99,6 +99,7 @@ typedef struct cxr_tri_mesh cxr_tri_mesh; #ifdef CXR_VALVE_MAP_FILE typedef struct cxr_vdf cxr_vdf; typedef struct cxr_texinfo cxr_texinfo; + typedef struct cxr_visgroup cxr_visgroup; typedef struct cxr_vmf_context cxr_vmf_context; #endif /* CXR_VALVE_MAP_FILE */ @@ -242,6 +243,11 @@ struct cxr_texinfo double winding; }; +struct cxr_visgroup +{ + const char *name; +}; + /* * Simplified VDF writing interface. No allocations or nodes, just write to file */ @@ -259,10 +265,14 @@ struct cxr_vmf_context *detailvbsp, *detailmaterial; + cxr_visgroup *visgroups; + i32 visgroup_count; + /* Transform settings */ double scale; v3f offset; - i32 lightmap_scale; + i32 lightmap_scale, + visgroupid; /* Current stats */ i32 brush_count, @@ -3076,6 +3086,8 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, v3_muladds( face_center, refn, 1.5, pn ); v3_muladds( face_center, refv, 1.5, pv ); v3_muladds( face_center, refu, 1.5, pu ); + + v3_muladds( face_center, refn, 2.0, face_center ); } /* Create world coordinates */ @@ -3239,7 +3251,8 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, cxr_vdf_node( output, "editor"); cxr_vdf_colour255( output, "color", colours_random[cxr_range(ctx->brush_count,8)]); - + + cxr_vdf_ki32( output, "visgroupid", ctx->visgroupid ); cxr_vdf_ki32( output, "visgroupshown",1); cxr_vdf_ki32( output, "visgroupautoshown",1); cxr_vdf_edon( output ); @@ -3269,6 +3282,15 @@ CXR_API void cxr_begin_vmf( cxr_vmf_context *ctx, cxr_vdf *output ) cxr_vdf_edon( output ); cxr_vdf_node( output, "visgroups" ); + + for( int i=0; ivisgroup_count; i++ ) + { + cxr_vdf_node( output, "visgroup" ); + cxr_vdf_kv( output, "name", ctx->visgroups[i].name ); + cxr_vdf_ki32( output, "visgroupid", i+1 ); + cxr_vdf_edon( output ); + } + cxr_vdf_edon( output ); cxr_vdf_node( output, "viewsettings" ); @@ -3373,6 +3395,7 @@ CXR_API void cxr_push_world_vmf( cxr_world *world, cxr_vmf_context *ctx, cxr_vdf_colour255( output, "color", colours_random[cxr_range(ctx->brush_count,8)]); + cxr_vdf_ki32( output, "visgroupid", ctx->visgroupid ); cxr_vdf_ki32( output, "visgroupshown", 1 ); cxr_vdf_ki32( output, "visgroupautoshown", 1 ); cxr_vdf_edon( output ); diff --git a/nbvtf/CMakeLists.txt b/nbvtf/CMakeLists.txt new file mode 100644 index 0000000..8b3eb41 --- /dev/null +++ b/nbvtf/CMakeLists.txt @@ -0,0 +1,29 @@ +project( nbvtf ) + +# RGBCX C++ -> C Wrapper object +add_library( rgbcx OBJECT librgbcx.cpp ) + +# NBVTF C object +add_library( onbvtf OBJECT nbvtf.c ) +target_compile_definitions( onbvtf PRIVATE USE_LIBRGBCX ) +set_property( TARGET onbvtf PROPERTY POSITION_INDEPENDENT_CODE ON ) + +# NBVTF Shared object +add_library( ${PROJECT_NAME} SHARED ) +target_link_libraries( ${PROJECT_NAME} PRIVATE rgbcx onbvtf ) + +target_compile_options( ${PROJECT_NAME} + PRIVATE -Wall -Wno-unused-variable -Wno-unused-function + -std=c99 -pedantic +) + +# Extra tools +add_library( otovtf OBJECT vtf_cmd.c nbvtf.h ) +target_compile_definitions( otovtf PRIVATE USE_LIBRGBCX ) +add_executable( tovtf ) +target_link_libraries( tovtf PRIVATE rgbcx otovtf ) + +add_library( otodds OBJECT dds_cmd.c nbvtf.h ) +target_compile_definitions( otodds PRIVATE USE_LIBRGBCX ) +add_executable( todds ) +target_link_libraries( todds PRIVATE rgbcx otodds ) diff --git a/nbvtf/dds_cmd.c b/nbvtf/dds_cmd.c new file mode 100644 index 0000000..2b39189 --- /dev/null +++ b/nbvtf/dds_cmd.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define NBVTF_SHOW_STDERR +#include "nbvtf.h" + +int main( int argc, char *argv[] ) +{ + if( argc < 3 ) + { + printf( "Usage: todds input_file.png output.dds\n" ); + return 0; + } + + printf( "todds: converting to dds... " ); + + int x,y,n; + uint8_t *data = stbi_load( argv[1], &x, &y, &n, 4 ); + + if( data ) + { + nbvtf_init(); + nbvtf_write_dds_dxt1( data, x, y, 16, argv[2] ); + free( data ); + + printf( "Success\n" ); + } + else + printf( "Failed\n" ); + + return 0; +} diff --git a/nbvtf/librgbcx.cc b/nbvtf/librgbcx.cc deleted file mode 100644 index ae6e72c..0000000 --- a/nbvtf/librgbcx.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include - -#define RGBCX_IMPLEMENTATION -#include "rgbcx.h" - -extern "C" -{ - void rgbcx__init(void) - { - rgbcx::init(); - } - - void rgbcx__encode_bc1( uint32_t level, void* pDst, const uint8_t* pPixels, int allow_3color, int use_transparent_texels_for_black ) - { - rgbcx::encode_bc1( level, pDst, pPixels, allow_3color, use_transparent_texels_for_black ); - } - - void rgbcx__encode_bc3( uint32_t level, void* pDst, const uint8_t* pPixels ) - { - rgbcx::encode_bc3( level, pDst, pPixels ); - } -} diff --git a/nbvtf/librgbcx.cpp b/nbvtf/librgbcx.cpp new file mode 100644 index 0000000..ae6e72c --- /dev/null +++ b/nbvtf/librgbcx.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +#define RGBCX_IMPLEMENTATION +#include "rgbcx.h" + +extern "C" +{ + void rgbcx__init(void) + { + rgbcx::init(); + } + + void rgbcx__encode_bc1( uint32_t level, void* pDst, const uint8_t* pPixels, int allow_3color, int use_transparent_texels_for_black ) + { + rgbcx::encode_bc1( level, pDst, pPixels, allow_3color, use_transparent_texels_for_black ); + } + + void rgbcx__encode_bc3( uint32_t level, void* pDst, const uint8_t* pPixels ) + { + rgbcx::encode_bc3( level, pDst, pPixels ); + } +} diff --git a/nbvtf/nbvtf.c b/nbvtf/nbvtf.c new file mode 100644 index 0000000..2485679 --- /dev/null +++ b/nbvtf/nbvtf.c @@ -0,0 +1,2 @@ +#define NBVTF_AS_SO +#include "nbvtf.h" diff --git a/nbvtf/nbvtf.h b/nbvtf/nbvtf.h index 111ba9e..6be4714 100644 --- a/nbvtf/nbvtf.h +++ b/nbvtf/nbvtf.h @@ -235,6 +235,76 @@ typedef struct vtfheader #pragma pack(pop) +#pragma pack(push, 1) +struct DDS_PIXELFORMAT +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; +}; + +struct DDS_HEADER { + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + struct DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; +}; + +#pragma pack(pop) + +uint32_t swap_endian(uint32_t val) +{ + return (val << 24) | ((val << 8) & 0x00ff0000) | + ((val >> 8) & 0x0000ff00) | (val >> 24); +} + +#define DDSD_CAPS 0x1 +#define DDSD_HEIGHT 0x2 +#define DDSD_WIDTH 0x4 +#define DDSD_PITCH 0x8 +#define DDSD_PIXELFORMAT 0x1000 +#define DDSD_MIPMAPCOUNT 0x20000 +#define DDSD_LINEARSIZE 0x80000 +#define DDSD_DEPTH 0x800000 + +#define DDPF_ALPHAPIXELS 0x1 +#define DDPF_ALPHA 0x2 +#define DDPF_FOURCC 0x4 +#define DDPF_RGB 0x40 +#define DDPF_YUV 0x200 +#define DDPF_LUMINANCE 0x20000 + +#define DDSCAPS_COMPLEX 0x8 +#define DDSCAPS_MIPMAP 0x400000 +#define DDSCAPS_TEXTURE 0x1000 + +#define BLOCK_SIZE_DXT1 8 +#define BLOCK_SIZE_DXT5 16 + +#define BBP_RGB888 24 +#define BBP_RGBA8888 32 + +#define DDS_HEADER_SIZE 124 +#define DDS_HEADER_PFSIZE 32 +#define DDS_MAGICNUM 0x20534444; + +#define DDS_FLIP_VERTICALLY_ON_WRITE + typedef struct mipimg { uint32_t w; @@ -573,6 +643,54 @@ void nbvtf_write_img_data( uint8_t *src, int w, int h, } } + + +#ifdef NBVTF_AS_SO +__attribute__((visibility("default"))) +#endif +int nbvtf_write_dds_dxt1( uint8_t *reference, int w, int h, int qual, const char *dest ) +{ + if( !nbvtf_power2x(w,h) ) + { + NBVTF_ERR( "nbvtf_write:err image dimentions were not power of two (%d %d)\n", w, h ); + return 0; + } + + struct DDS_HEADER header = {0}; + header.dwSize = DDS_HEADER_SIZE; + header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + header.dwHeight = h; + header.dwWidth = w; + header.dwPitchOrLinearSize = nbvtf__max(1, ((w + 3) / 4)) * BLOCK_SIZE_DXT1; + header.ddspf.dwSize = DDS_HEADER_PFSIZE; + header.ddspf.dwFlags |= DDPF_FOURCC; + header.ddspf.dwFourCC = ((uint32_t)'D'<<0) | + ((uint32_t)'X'<<8) | + ((uint32_t)'T'<<16) | + ((uint32_t)'1'<<24); + + header.dwFlags |= DDSD_LINEARSIZE; + header.dwMipMapCount = 0; + header.dwCaps = DDSCAPS_TEXTURE; + + // Magic number + uint32_t magic = DDS_MAGICNUM; + + FILE *file = fopen( dest, "wb" ); + fwrite( &magic, sizeof(uint32_t), 1, file ); + fwrite( &header, DDS_HEADER_SIZE, 1, file ); + + uint32_t size_highres = nbvtf_sizeimg( w, h, k_EImageFormat_DXT1 ); + uint8_t *working_buffer = malloc( size_highres ); + + nbvtf_compress_dxt( reference, w, h, 0, qual, working_buffer ); + fwrite( working_buffer, size_highres, 1, file ); + + free( working_buffer ); + fclose( file ); + return 1; +} + #ifdef NBVTF_AS_SO __attribute__((visibility("default"))) #endif