From ea8476b5ce234e3098116bf08c17b0def5115bfd Mon Sep 17 00:00:00 2001 From: hgn Date: Mon, 2 May 2022 04:53:00 +0100 Subject: [PATCH] texture loading --- __init__.py | 115 ++++++++++----- cxr/cxr_valve_bin.h | 13 +- nbvtf/librgbcx.cpp | 10 ++ nbvtf/librgbcx.h | 2 + nbvtf/nbvtf.h | 337 +++++++++++++++++++++++++++++++++++++++----- nbvtf/vtf_cmd.c | 1 + 6 files changed, 405 insertions(+), 73 deletions(-) diff --git a/__init__.py b/__init__.py index 8d9254e..05aefbc 100644 --- a/__init__.py +++ b/__init__.py @@ -48,10 +48,10 @@ cxr_ui_draw_handler = None # Batches cxr_view_lines = None cxr_view_mesh = None -cxr_mdl_mesh = None cxr_jobs_batch = None cxr_jobs_inf = [] cxr_error_inf = None +cxr_test_mdl = None cxr_asset_lib = \ { @@ -93,9 +93,11 @@ uniform mat4 viewProjectionMatrix; in vec3 aPos; in vec3 aNormal; +in vec2 aUv; out vec3 lPos; out vec3 lNormal; +out vec2 lUv; void main() { @@ -105,14 +107,17 @@ void main() gl_Position = viewProjectionMatrix * pWorldPos; lNormal = normalize(mat3(transpose(inverse(modelMatrix))) * aNormal); lPos = worldPos; + lUv = aUv; } """,""" uniform vec4 colour; uniform vec3 testLightDir; +uniform sampler2D uBasetexture; in vec3 lNormal; in vec3 lPos; +in vec2 lUv; out vec4 FragColor; @@ -146,11 +151,18 @@ vec3 LinearToGamma( vec3 f3linear ) return pow( f3linear, vec3(1.0 / 2.2) ); } +vec3 GammaToLinear( vec3 f3gamma ) +{ + return pow( f3gamma, vec3(2.2) ); +} + void main() { vec3 tangentSpaceNormal = vec3( 0.0, 0.0, 1.0 ); vec4 normalTexel = vec4(1.0,1.0,1.0,1.0); - vec4 baseColor = colour; + vec3 colorInput = GammaToLinear( texture( uBasetexture, lUv ).rgb ); + + vec4 baseColor = vec4( colorInput * colour.rgb, 1.0 ); //normalTexel = tex2D( BumpmapSampler, i.detailOrBumpTexCoord ); //tangentSpaceNormal = 2.0 * normalTexel - 1.0; @@ -229,7 +241,7 @@ def cxr_ui(_,context): def cxr_draw(): global cxr_view_shader, cxr_view_mesh, cxr_view_lines, cxr_mdl_shader,\ - cxr_mdl_mesh + cxr_mdl_mesh, cxr_test_mdl cxr_view_shader.bind() @@ -245,29 +257,33 @@ def cxr_draw(): if cxr_view_mesh != None: gpu.state.depth_test_set('LESS_EQUAL') gpu.state.blend_set('ADDITIVE') - + cxr_view_mesh.draw( cxr_view_shader ) - if cxr_mdl_mesh != None: - gpu.state.depth_mask_set(True) - gpu.state.depth_test_set('LESS_EQUAL') - gpu.state.face_culling_set('FRONT') - gpu.state.blend_set('NONE') - cxr_mdl_shader.bind() - cxr_mdl_shader.uniform_float('colour',(0.5,0.5,0.5,1.0)) - cxr_mdl_shader.uniform_float("viewProjectionMatrix", \ - bpy.context.region_data.perspective_matrix) + # Models + gpu.state.depth_mask_set(True) + gpu.state.depth_test_set('LESS_EQUAL') + gpu.state.face_culling_set('FRONT') + gpu.state.blend_set('NONE') + cxr_mdl_shader.bind() + cxr_mdl_shader.uniform_float("viewProjectionMatrix", \ + bpy.context.region_data.perspective_matrix) + + if cxr_test_mdl != None: + cxr_mdl_shader.uniform_float('colour',(1.0,1.0,1.0,1.0)) + + #temp light dir testmdl = bpy.context.scene.objects['target'] light = bpy.context.scene.objects['point'] relative = light.location - testmdl.location relative.normalize() - cxr_mdl_shader.uniform_float("modelMatrix", testmdl.matrix_world) cxr_mdl_shader.uniform_float("testLightDir", relative) - - cxr_mdl_mesh.draw( cxr_mdl_shader ) + for part in cxr_test_mdl: + cxr_mdl_shader.uniform_sampler("uBasetexture", part[0]['basetexture']) + part[1].draw( cxr_mdl_shader ) def cxr_jobs_update_graph(jobs): global cxr_jobs_batch, cxr_ui_shader, cxr_jobs_inf @@ -627,7 +643,8 @@ libcxr_lightpatch_bsp = extern( "cxr_lightpatch_bsp", [c_char_p], None ) # Binary file formats and FS libcxr_fs_set_gameinfo = extern( "cxr_fs_set_gameinfo", [c_char_p], c_int32 ) libcxr_fs_exit = extern( "cxr_fs_exit", [], None ) -libcxr_fs_get = extern( "cxr_fs_get", [c_char_p, c_int32], c_char_p ) +libcxr_fs_get = extern( "cxr_fs_get", [c_char_p, c_int32], c_void_p ) +libcxr_fs_free = extern( "cxr_fs_free", [c_void_p], None ) libcxr_fs_find = extern( "cxr_fs_find", [c_char_p, POINTER(fs_locator)],\ c_int32 ) @@ -648,7 +665,7 @@ libcxr_funcs = [ libcxr_decompose, libcxr_free_world, libcxr_begin_vmf, \ libcxr_vdf_kv, libcxr_lightpatch_bsp, libcxr_write_test_data,\ libcxr_world_preview, libcxr_free_tri_mesh, \ libcxr_fs_set_gameinfo, libcxr_fs_exit, libcxr_fs_get, \ - libcxr_fs_find,\ + libcxr_fs_find, libcxr_fs_free, \ libcxr_valve_load_model, libcxr_valve_free_model,\ libcxr_valve_load_material, libcxr_valve_free_material ] @@ -717,8 +734,15 @@ libnbvtf_convert = extern( "nbvtf_convert", \ [c_char_p,c_int32,c_int32,c_int32,c_int32,c_int32,c_uint32,c_char_p], \ c_int32 ) +libnbvtf_read = extern( "nbvtf_read", \ + [c_void_p,POINTER(c_int32),POINTER(c_int32), c_int32], \ + POINTER(c_uint8) ) + +libnbvtf_free = extern( "nbvtf_free", [POINTER(c_uint8)], None ) + libnbvtf_init = extern( "nbvtf_init", [], None ) -libnbvtf_funcs = [ libnbvtf_convert, libnbvtf_init ] +libnbvtf_funcs = [ libnbvtf_convert, libnbvtf_init, libnbvtf_read, \ + libnbvtf_free ] # Loading # -------------------------- @@ -1770,7 +1794,7 @@ class CXR_INIT_FS_OPERATOR(bpy.types.Operator): return {'FINISHED'} -def cxr_load_texture( path ): +def cxr_load_texture( path, is_normal ): global cxr_asset_lib if path in cxr_asset_lib['textures']: @@ -1778,9 +1802,36 @@ def cxr_load_texture( path ): print( F"cxr_load_texture( '{path}' )" ) - # TODO + pvtf = libcxr_fs_get.call( path.encode('utf-8'), 0 ) + + if not pvtf: + print( "vtf failed to load" ) + cxr_asset_lib['textures'][path] = None + return None + + x = c_int32(0) + y = c_int32(0) + + img_data = libnbvtf_read.call( pvtf, pointer(x), pointer(y), \ + c_int32(is_normal) ) + + x = x.value + y = y.value + + if not img_data: + print( "vtf failed to decode" ) + libcxr_fs_free.call( pvtf ) + cxr_asset_lib['textures'][path] = None + return None + + img_buf = gpu.types.Buffer('FLOAT', [x*y*4], [_/255.0 for _ in img_data[:x*y*4]]) - tex = cxr_asset_lib['textures'][path] = None + tex = cxr_asset_lib['textures'][path] = \ + gpu.types.GPUTexture( size=(x,y), layers=0, is_cubemap=False,\ + format='RGBA8', data=img_buf ) + + libnbvtf_free.call( img_data ) + libcxr_fs_free.call( pvtf ) return tex def cxr_load_material( path ): @@ -1792,15 +1843,19 @@ def cxr_load_material( path ): print( F"cxr_load_material( '{path}' )" ) pvmt = libcxr_valve_load_material.call( path.encode( 'utf-8') ) - vmt = pvmt[0] + + if not pvmt: + cxr_asset_lib['materials'][path] = None + return None + vmt = pvmt[0] mat = cxr_asset_lib['materials'][path] = {} if vmt.basetexture: - mat['basetexture'] = cxr_load_texture( vmt.basetexture.decode('utf-8') ) + mat['basetexture'] = cxr_load_texture( vmt.basetexture.decode('utf-8'), 0) if vmt.bumpmap: - mat['bumpmap'] = cxr_load_texture( vmt.bumpmap.decode('utf-8') ) + mat['bumpmap'] = cxr_load_texture( vmt.bumpmap.decode('utf-8'), 1) libcxr_valve_free_material.call( pvmt ) @@ -1870,15 +1925,9 @@ class CXR_LOAD_MODEL_OPERATOR(bpy.types.Operator): bl_label="Load model" def execute(_,context): - global cxr_mdl_mesh, cxr_mdl_shader, cxr_asset_lib - - test_mdl = cxr_load_model_full( bpy.context.scene.cxr_data.dev_mdl ) + global cxr_test_mdl, cxr_mdl_shader, cxr_asset_lib - if test_mdl != None: - # just draw first batch part for now - cxr_mdl_mesh = test_mdl[0][1] - else: - cxr_mdl_mesh = None + cxr_test_mdl = cxr_load_model_full( bpy.context.scene.cxr_data.dev_mdl ) scene_redraw() return {'FINISHED'} diff --git a/cxr/cxr_valve_bin.h b/cxr/cxr_valve_bin.h index b11f71f..8d0bf9c 100644 --- a/cxr/cxr_valve_bin.h +++ b/cxr/cxr_valve_bin.h @@ -40,6 +40,7 @@ typedef struct valve_material valve_material; CXR_API i32 cxr_fs_set_gameinfo( const char *path ); /* Setup system */ CXR_API void cxr_fs_exit(void); /* Clean up */ CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer ); /* Get a file */ +CXR_API void cxr_fs_free( void *data ); CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator ); CXR_API valve_model *valve_load_model( const char *relpath ); @@ -997,6 +998,11 @@ CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator ) return 0; } +CXR_API void cxr_fs_free( void *data ) +{ + free( data ); +} + CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer ) { valve_file_system *fs = &fs_global; @@ -1711,9 +1717,12 @@ static char *valve_texture_path( const char *path ) if( !path ) return NULL; - char *buf = cxr_str_clone( path, 4 ); - + char *buf = + malloc( strlen( path ) + strlen(".vtf") + strlen("materials/") +1 ); + strcpy( buf, "materials/" ); + strcat( buf, path ); strcat( buf, ".vtf" ); + cxr_unixpath( buf ); cxr_lowercase( buf ); diff --git a/nbvtf/librgbcx.cpp b/nbvtf/librgbcx.cpp index ae6e72c..958b99e 100644 --- a/nbvtf/librgbcx.cpp +++ b/nbvtf/librgbcx.cpp @@ -21,4 +21,14 @@ extern "C" { rgbcx::encode_bc3( level, pDst, pPixels ); } + + int rgbcx__unpack_bc1( const void *pSrc, uint8_t *pDst ) + { + return rgbcx::unpack_bc1( pSrc, pDst ); + } + + int rgbcx__unpack_bc3( const void *pSrc, uint8_t *pDst ) + { + return rgbcx::unpack_bc3( pSrc, pDst ); + } } diff --git a/nbvtf/librgbcx.h b/nbvtf/librgbcx.h index 31ded0c..04a0c12 100644 --- a/nbvtf/librgbcx.h +++ b/nbvtf/librgbcx.h @@ -12,6 +12,8 @@ void rgbcx__init(void); void rgbcx__encode_bc1( uint32_t level, void* pDst, const uint8_t* pPixels, int allow_3color, int use_transparent_texels_for_black ); void rgbcx__encode_bc3( uint32_t level, void* pDst, const uint8_t* pPixels ); +int rgbcx__unpack_bc1( const void *pSrc, uint8_t *pDst ); +int rgbcx__unpack_bc3( const void *pSrc, uint8_t *pDst ); #ifdef __cplusplus } #endif diff --git a/nbvtf/nbvtf.h b/nbvtf/nbvtf.h index aa82cbf..5db305b 100644 --- a/nbvtf/nbvtf.h +++ b/nbvtf/nbvtf.h @@ -57,10 +57,12 @@ extern "C" { #define NBVTF_SHOW_STDERR #define STB_IMAGE_IMPLEMENTATION + #define STB_IMAGE_WRITE_IMPLEMENTATION #endif #define STBI_NO_THREAD_LOCALS #include "stb/stb_image.h" +#include "stb/stb_image_write.h" #ifdef USE_LIBRGBCX // #define RGBCX_NO_ALGORITHM @@ -88,40 +90,39 @@ extern "C" { typedef enum EImageFormat { - // Name // Supported? + /* Format Export Import */ k_EImageFormat_NONE = -1, - k_EImageFormat_RGBA8888 = 0, // YES - k_EImageFormat_ABGR8888, - k_EImageFormat_RGB888, // YES - k_EImageFormat_BGR888, - k_EImageFormat_RGB565, - k_EImageFormat_I8, - k_EImageFormat_IA88, - k_EImageFormat_P8, - k_EImageFormat_A8, - k_EImageFormat_RGB888_BLUESCREEN, - k_EImageFormat_BGR888_BLUESCREEN, - k_EImageFormat_ARGB8888, - k_EImageFormat_BGRA8888, - k_EImageFormat_DXT1, // YES - k_EImageFormat_DXT3, - k_EImageFormat_DXT5, // YES - k_EImageFormat_BGRX8888, - k_EImageFormat_BGR565, - k_EImageFormat_BGRX5551, - k_EImageFormat_BGRA4444, - k_EImageFormat_DXT1_ONEBITALPHA, - k_EImageFormat_BGRA5551, - k_EImageFormat_UV88, - k_EImageFormat_UVWQ8888, - k_EImageFormat_RGBA16161616F, - k_EImageFormat_RGBA16161616, - k_EImageFormat_UVLX8888 + k_EImageFormat_RGBA8888 = 0, // - yes + k_EImageFormat_ABGR8888, // yes yes + k_EImageFormat_RGB888, // - yes + k_EImageFormat_BGR888, // yes yes + k_EImageFormat_RGB565, // - planned + k_EImageFormat_I8, // - planned + k_EImageFormat_IA88, // - planned + k_EImageFormat_P8, // - - + k_EImageFormat_A8, // - - + k_EImageFormat_RGB888_BLUESCREEN,// - - + k_EImageFormat_BGR888_BLUESCREEN,// - - + k_EImageFormat_ARGB8888, // - yes + k_EImageFormat_BGRA8888, // - yes + k_EImageFormat_DXT1, // yes yes + k_EImageFormat_DXT3, // - - + k_EImageFormat_DXT5, // yes yes + k_EImageFormat_BGRX8888, // - - + k_EImageFormat_BGR565, // - planned + k_EImageFormat_BGRX5551, // - - + k_EImageFormat_BGRA4444, // - - + k_EImageFormat_DXT1_ONEBITALPHA, // - planned + k_EImageFormat_BGRA5551, // - - + k_EImageFormat_UV88, // - - + k_EImageFormat_UVWQ8888, // - - + k_EImageFormat_RGBA16161616F, // - - + k_EImageFormat_RGBA16161616, // - - + k_EImageFormat_UVLX8888 // - - } EImageFormat_t; const char *vtf_format_strings[] = { - // Name // Supported? "RGBA8888", "ABGR8888", "RGB888", @@ -564,6 +565,29 @@ void nbvtf_dxt_block( uint8_t *dest, uint8_t *src, int alpha, int qual ) #endif } +void nbvtf_dxt_block_unpack( uint8_t *src, uint8_t *dest, int alpha ) +{ +#ifdef USE_LIBRGBCX + if( alpha ) + { + rgbcx__unpack_bc3( src, dest ); + } + else + { + rgbcx__unpack_bc1( src, dest ); + } +#endif + +#if USE_STB_DXT + stb_decompress_dxt_block( src, dest, alpha ); +#endif + +#ifndef HAS_DXT_COMPRESSOR + for( int i=0; i> 2; + blocks_y = ((uint32_t)h) >> 2; + + int padx = w % 4 != 0? 1: 0; + int pady = h % 4 != 0? 1: 0; + + int block_size = alpha? 16: 8; + + uint8_t *dxt_block = src; + uint8_t working_block[ 4*4*4 ]; + + // Compress rows + for( int y = 0; y < blocks_y; y ++ ) + { + for( int x = 0; x < blocks_x; x ++ ) + { + nbvtf_dxt_block_unpack( dxt_block, working_block, alpha ); + + uint8_t *dest_begin = dest + (y*w*4 + x*4)*4; + for( int i = 0; i < 4; i ++ ) + memcpy( dest_begin + w*4*i, working_block + i*4*4, 4*4 ); + + dxt_block += block_size; + } + + if( padx ) + dxt_block += block_size; + } + + if( pady ) + for( int x = 0; x < blocks_x; x ++ ) + dxt_block += block_size; + + if( padx && pady ) + { + } +} +static void nbvtf_swizzle_to( uint8_t *src, int n, int nc, uint8_t *dest ) { for( int i = 0; i < n; i ++ ) { @@ -634,7 +700,7 @@ void nbvtf_swizzle_to( uint8_t *src, int n, int nc, uint8_t *dest ) } } -void nbvtf_write_img_data( uint8_t *src, int w, int h, +static void nbvtf_write_img_data( uint8_t *src, int w, int h, EImageFormat_t format, int qual, uint8_t *wb, FILE *file ) { switch( format ) @@ -661,7 +727,119 @@ void nbvtf_write_img_data( uint8_t *src, int w, int h, } } +static size_t nbvtf_img_size( int w, int h, EImageFormat_t format ) +{ + int block_count = nbvtf__max(1, ((w + 3) / 4)) * + nbvtf__max(1, ((h + 3) / 4)); + switch( format ) + { + case k_EImageFormat_RGBA8888: + case k_EImageFormat_ABGR8888: + case k_EImageFormat_ARGB8888: + case k_EImageFormat_BGRA8888: + return 4*w*h; + + case k_EImageFormat_RGB888: + case k_EImageFormat_BGR888: + return 3*w*h; + + case k_EImageFormat_RGB565: + case k_EImageFormat_IA88: + return 2*w*h; + + case k_EImageFormat_I8: + return w*h; + + case k_EImageFormat_DXT1: + return block_count * BLOCK_SIZE_DXT1; + + case k_EImageFormat_DXT5: + return block_count * BLOCK_SIZE_DXT5; + + default: + break; + } +} + +static void nbvtf_read_img_data( uint8_t *src, int w, int h, + EImageFormat_t format, uint8_t *dst ) +{ + switch( format ) + { + case k_EImageFormat_RGBA8888: + for( int i=0; iwidth; + *h = header->height; + + uint8_t *rgba = malloc( header->width * header->height * 4 ); + + if( !rgba ) + return NULL; + + size_t memory = 0; + int x = header->width, + y = header->height; + + int mip_iter = 0; + + while( nbvtf_lower( &x, &y ) ) + { + if( mip_iter == header->mipmapCount ) + break; + + mip_iter ++; + memory += nbvtf_img_size( x, y, header->highResImageFormat ); + } + + if( header->lowResImageWidth == 0 || header->lowResImageHeight == 0 || + header->lowResImageFormat == 0xffffffff ) + { + /* no thumbnail? */ + } + else + memory += nbvtf_img_size( header->lowResImageWidth, + header->lowResImageHeight, + header->lowResImageFormat ); + + /* Decode high res image into rgba */ + nbvtf_read_img_data( ((uint8_t *)header) + header->headerSize + memory, + header->width, header->height, + header->highResImageFormat, + rgba ); + + if( normal ) + { + nbvtf_correct_normal( rgba, rgba, header->width, header->height ); + stbi_write_png( "/tmp/cxr_hello_n.png", header->width, header->height, + 4, rgba, header->width*4 ); + } + else + stbi_write_png( "/tmp/cxr_hello.png", header->width, header->height, + 4, rgba, header->width*4 ); + + + return rgba; +} + +#ifdef NBVTF_AS_SO +__attribute__((visibility("default"))) +#endif +uint8_t *nbvtf_raw_data( vtfheader_t *header, int32_t *w, int32_t *h, int32_t *format ) +{ + *w = header->width; + *h = header->height; + *format = header->highResImageFormat; + + return ((uint8_t *)header) + header->headerSize; +} + +#ifdef NBVTF_AS_SO +__attribute__((visibility("default"))) +#endif +void nbvtf_free( uint8_t *data ) +{ + free(data); +} + #ifdef NBVTF_AS_SO __attribute__((visibility("default"))) #endif diff --git a/nbvtf/vtf_cmd.c b/nbvtf/vtf_cmd.c index d501279..37bf0aa 100644 --- a/nbvtf/vtf_cmd.c +++ b/nbvtf/vtf_cmd.c @@ -5,6 +5,7 @@ #define STB_IMAGE_IMPLEMENTATION #define NBVTF_SHOW_STDERR +#define NBVTF_AS_SO #include "nbvtf.h" // Find file path extension, returns NULL if no ext (0x00) -- 2.25.1