From 9a92cec0e25c758d12c535bb737be598f053b56d Mon Sep 17 00:00:00 2001 From: hgn Date: Wed, 13 Apr 2022 12:43:12 +0100 Subject: [PATCH] UI and interface --- __init__.py | 1784 ++++++++++++++++++++++++++----------------------- config.py | 254 +++++++ cxr/cxr.h | 33 +- cxr/test.c | 7 +- nbvtf/nbvtf.h | 20 +- 5 files changed, 1239 insertions(+), 859 deletions(-) create mode 100644 config.py diff --git a/__init__.py b/__init__.py index fc20f05..0a5ddcf 100644 --- a/__init__.py +++ b/__init__.py @@ -15,17 +15,176 @@ bl_info = { print( "Convexer reload" ) #from mathutils import * -import bpy, gpu, math, os, time, mathutils +import bpy, gpu, math, os, time, mathutils, blf from ctypes import * from gpu_extras.batch import batch_for_shader from bpy.app.handlers import persistent -# Globals and tweaks -vmt_param_dynamic_class = None +# GPU and viewport drawing +# ------------------------------------------------------------------------------ + +# Handlers +cxr_view_draw_handler = None +cxr_ui_draw_handler = None + +# Batches +cxr_view_lines = None +cxr_view_mesh = None +cxr_jobs_batch = None +cxr_jobs_inf = [] + +# Shaders +cxr_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') +cxr_ui_shader = gpu.types.GPUShader(""" +uniform mat4 ModelViewProjectionMatrix; +uniform float scale; + +in vec2 aPos; +in vec4 aColour; + +out vec4 colour; + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4(aPos.x*scale,aPos.y, 0.0, 1.0); + colour = aColour; +} +""",""" +in vec4 colour; +out vec4 FragColor; + +void main() +{ + FragColor = colour; +} +""") + +# Render functions +# +def cxr_ui(_,context): + global cxr_jobs_batch, cxr_ui_shader, cxr_jobs_inf + + w = gpu.state.viewport_get()[2] + cxr_ui_shader.bind() + cxr_ui_shader.uniform_float( "scale", w ) + + if cxr_jobs_batch != None: + gpu.state.blend_set('ALPHA') + cxr_jobs_batch.draw(cxr_ui_shader) + + blf.position(0,2,50,0) + blf.size(0,50,48) + blf.color(0,1.0,1.0,1.0,1.0) + blf.draw(0,"Compiling") + + for ji in cxr_jobs_inf: + blf.position(0,ji[0]*w,35,0) + blf.size(0,50,20) + blf.draw(0,ji[1]) + + if CXR_PREVIEW_OPERATOR.LASTERR != None: + blf.position(0,2,80,0) + blf.size(0,50,48) + blf.color(0,1.0,0.2,0.2,0.9) + blf.draw(0,"This is a stoopid error\nWIthiuawdnaw") + + # Something is off with TIMER, + # this forces the viewport to redraw before we can continue with our + # compilation stuff. + + CXR_COMPILER_CHAIN.WAIT_REDRAW = False + +def cxr_draw(): + global cxr_view_shader, cxr_view_mesh, cxr_view_lines + + cxr_view_shader.bind() + + gpu.state.depth_mask_set(False) + gpu.state.line_width_set(1.5) + gpu.state.face_culling_set('BACK') + gpu.state.depth_test_set('NONE') + gpu.state.blend_set('ALPHA') + + if cxr_view_lines != None: + cxr_view_lines.draw( cxr_view_shader ) + + gpu.state.depth_test_set('LESS_EQUAL') + gpu.state.blend_set('ADDITIVE') + if cxr_view_mesh != None: + cxr_view_mesh.draw( cxr_view_shader ) + +def cxr_jobs_update_graph(jobs): + global cxr_jobs_batch, cxr_ui_shader, cxr_jobs_inf + + cxr_jobs_inf = [] + + total_width = 0 + verts = [] + colours = [] + indices = [] + + for sys in jobs: + total_width += sys['w'] + + sf = 1.0/total_width + cur = 0.0 + ci = 0 + + for sys in jobs: + w = sys['w'] + h = 30.0 + colour = sys['colour'] + colourwait = (colour[0],colour[1],colour[2],0.4) + colourrun = (colour[0]*1.5,colour[1]*1.5,colour[2]*1.5,0.5) + colourdone = (colour[0],colour[1],colour[2],1.0) + + jobs = sys['jobs'] + sfsub = (1.0/(len(jobs)))*w + i = 0 + + for j in jobs: + if j == None: colour = colourdone + else: colour = colourwait + + px = (cur + (i)*sfsub) * sf + px1 = (cur + (i+1.0)*sfsub) * sf - 0.003 + i += 1 + + verts += [(px,0), (px, h), (px1, 0.0), (px1,h)] + colours += [colour,colour,colour,colour] + indices += [(ci+0,ci+2,ci+3),(ci+0,ci+3,ci+1)] + ci += 4 + + cxr_jobs_inf += [((sf*cur), sys['title'])] + cur += w + + cxr_jobs_batch = batch_for_shader( + cxr_ui_shader, 'TRIS', + { "aPos": verts, "aColour": colours }, + indices = indices + ) + +# view_layer.update() doesnt seem to work, +# tag_redraw() seems to have broken +# therefore, change a property +def scene_redraw(): + ob = bpy.context.scene.objects[0] + ob.hide_render = ob.hide_render + + # the 'real' way to refresh the scene + for area in bpy.context.window.screen.areas: + if area.type == 'view_3d': + area.tag_redraw() -# libcxr interface (TODO: We can probably automate this) -# ====================================================== +# Shared libraries +# ------------------------------------------------------------------------------ +# dlclose for reloading modules manually +libc_dlclose = None +libc_dlclose = cdll.LoadLibrary(None).dlclose +libc_dlclose.argtypes = [c_void_p] + +# wrapper for ctypes binding class extern(): def __init__(_,name,argtypes,restype): _.name = name @@ -40,16 +199,13 @@ class extern(): if _.restype != None: _.call.restype = _.restype -libc_dlclose = None -libc_dlclose = cdll.LoadLibrary(None).dlclose -libc_dlclose.argtypes = [c_void_p] +# libcxr (convexer) +# ------------------------------------------------------------------------------ -# Callback ctypes wrapper... libcxr = None -c_libcxr_log_callback = None -c_libcxr_line_callback = None # Structure definitions +# class cxr_edge(Structure): _fields_ = [("i0",c_int32), ("i1",c_int32), @@ -103,72 +259,144 @@ class cxr_vmf_context(Structure): ("entity_count",c_int32), ("face_count",c_int32)] -# Public API -libcxr_decompose = extern( "cxr_decompose", \ - [POINTER(cxr_static_mesh), POINTER(c_int32)], c_void_p ) +# Convert blenders mesh format into CXR's static format (they are very similar) +# +def mesh_cxr_format(obj): + orig_state = None -libcxr_free_world = extern( "cxr_free_world", [c_void_p], None ) -libcxr_write_test_data = extern( "cxr_write_test_data", \ - [POINTER(cxr_static_mesh)], None ) -libcxr_world_preview = extern( "cxr_world_preview", [c_void_p], \ - POINTER(cxr_tri_mesh)) -libcxr_free_tri_mesh = extern( "cxr_free_tri_mesh", [c_void_p], None ) + if bpy.context.active_object != None: + orig_state = obj.mode + if orig_state != 'OBJECT': + bpy.ops.object.mode_set(mode='OBJECT') -# VMF -libcxr_begin_vmf = extern( "cxr_begin_vmf", \ - [POINTER(cxr_vmf_context), c_void_p], None ) + dgraph = bpy.context.evaluated_depsgraph_get() + data = obj.evaluated_get(dgraph).data + + _,mtx_rot,_ = obj.matrix_world.decompose() -libcxr_vmf_begin_entities = extern( "cxr_vmf_begin_entities", \ - [POINTER(cxr_vmf_context), c_void_p], None ) + mesh = cxr_static_mesh() -libcxr_push_world_vmf = extern("cxr_push_world_vmf", \ - [c_void_p,POINTER(cxr_vmf_context),c_void_p], None ) + vertex_data = ((c_double*3)*len(data.vertices))() + for i, vert in enumerate(data.vertices): + v = obj.matrix_world @ vert.co + vertex_data[i][0] = c_double(v[0]) + vertex_data[i][1] = c_double(v[1]) + vertex_data[i][2] = c_double(v[2]) -libcxr_end_vmf = extern( "cxr_end_vmf", \ - [POINTER(cxr_vmf_context),c_void_p], None ) + loop_data = (cxr_static_loop*len(data.loops))() + polygon_data = (cxr_polygon*len(data.polygons))() -# VDF -libcxr_vdf_open = extern( "cxr_vdf_open", [c_char_p], c_void_p ) -libcxr_vdf_close = extern( "cxr_vdf_close", [c_void_p], None ) -libcxr_vdf_put = extern( "cxr_vdf_put", [c_void_p,c_char_p], None ) -libcxr_vdf_node = extern( "cxr_vdf_node", [c_void_p,c_char_p], None ) -libcxr_vdf_edon = extern( "cxr_vdf_edon", [c_void_p], None ) -libcxr_vdf_kv = extern( "cxr_vdf_kv", [c_void_p,c_char_p,c_char_p], None ) + for i, poly in enumerate(data.polygons): + loop_start = poly.loop_start + loop_end = poly.loop_start + poly.loop_total + for loop_index in range(loop_start, loop_end): + loop = data.loops[loop_index] + loop_data[loop_index].index = loop.vertex_index + loop_data[loop_index].edge_index = loop.edge_index -# Other -libcxr_lightpatch_bsp = extern( "cxr_lightpatch_bsp", [c_char_p], None ) + if data.uv_layers: + uv = data.uv_layers.active.data[loop_index].uv + loop_data[loop_index].uv[0] = c_double(uv[0]) + loop_data[loop_index].uv[1] = c_double(uv[1]) + else: + loop_data[loop_index].uv[0] = c_double(0.0) + loop_data[loop_index].uv[1] = c_double(0.0) + center = obj.matrix_world @ poly.center + normal = mtx_rot @ poly.normal -libcxr_funcs = [ libcxr_decompose, libcxr_free_world, libcxr_begin_vmf, \ - libcxr_vmf_begin_entities, libcxr_push_world_vmf, \ - libcxr_end_vmf, libcxr_vdf_open, libcxr_vdf_close, \ - libcxr_vdf_put, libcxr_vdf_node, libcxr_vdf_edon, - libcxr_vdf_kv, libcxr_lightpatch_bsp, libcxr_write_test_data,\ - libcxr_world_preview, libcxr_free_tri_mesh ] + polygon_data[i].loop_start = poly.loop_start + polygon_data[i].loop_total = poly.loop_total + polygon_data[i].normal[0] = normal[0] + polygon_data[i].normal[1] = normal[1] + polygon_data[i].normal[2] = normal[2] + polygon_data[i].center[0] = center[0] + polygon_data[i].center[1] = center[1] + polygon_data[i].center[2] = center[2] + polygon_data[i].material_id = poly.material_index -# libnbvtf interface -# ================== -libnbvtf = None + edge_data = (cxr_edge*len(data.edges))() -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 ) + for i, edge in enumerate(data.edges): + edge_data[i].i0 = edge.vertices[0] + edge_data[i].i1 = edge.vertices[1] + edge_data[i].freestyle = edge.use_freestyle_mark -libnbvtf_funcs = [ libnbvtf_convert ] + material_data = (cxr_material*len(obj.material_slots))() -# NBVTF constants -# =============== + for i, ms in enumerate(obj.material_slots): + inf = material_info(ms.material) + material_data[i].res[0] = inf['res'][0] + material_data[i].res[1] = inf['res'][1] + material_data[i].name = inf['name'].encode('utf-8') + + mesh.edges = cast(edge_data, POINTER(cxr_edge)) + mesh.vertices = cast(vertex_data, POINTER(c_double*3)) + mesh.loops = cast(loop_data,POINTER(cxr_static_loop)) + mesh.polys = cast(polygon_data, POINTER(cxr_polygon)) + mesh.materials = cast(material_data, POINTER(cxr_material)) + + mesh.poly_count = len(data.polygons) + mesh.vertex_count = len(data.vertices) + mesh.edge_count = len(data.edges) + mesh.loop_count = len(data.loops) + mesh.material_count = len(obj.material_slots) -NBVTF_IMAGE_FORMAT_RGBA8888 = 0 -NBVTF_IMAGE_FORMAT_RGB888 = 2 -NBVTF_IMAGE_FORMAT_DXT1 = 13 -NBVTF_IMAGE_FORMAT_DXT5 = 15 -NBVTF_TEXTUREFLAGS_CLAMPS = 0x00000004 -NBVTF_TEXTUREFLAGS_CLAMPT = 0x00000008 -NBVTF_TEXTUREFLAGS_NORMAL = 0x00000080 -NBVTF_TEXTUREFLAGS_NOMIP = 0x00000100 -NBVTF_TEXTUREFLAGS_NOLOD = 0x00000200 + if orig_state != None: + bpy.ops.object.mode_set(mode=orig_state) + + return mesh + +# Callback ctypes indirection things.. not really sure. +c_libcxr_log_callback = None +c_libcxr_line_callback = None + +# Public API +# ------------------------------------------------------------- +libcxr_decompose = extern( "cxr_decompose", + [POINTER(cxr_static_mesh), POINTER(c_int32)], + c_void_p +) +libcxr_free_world = extern( "cxr_free_world", + [c_void_p], + None +) +libcxr_write_test_data = extern( "cxr_write_test_data", + [POINTER(cxr_static_mesh)], + None +) +libcxr_world_preview = extern( "cxr_world_preview", + [c_void_p], + POINTER(cxr_tri_mesh) +) +libcxr_free_tri_mesh = extern( "cxr_free_tri_mesh", + [c_void_p], + None +) +libcxr_begin_vmf = extern( "cxr_begin_vmf", + [POINTER(cxr_vmf_context), c_void_p], + None +) +libcxr_vmf_begin_entities = extern( "cxr_vmf_begin_entities", + [POINTER(cxr_vmf_context), c_void_p], + None +) +libcxr_push_world_vmf = extern("cxr_push_world_vmf", + [c_void_p,POINTER(cxr_vmf_context),c_void_p], + None +) +libcxr_end_vmf = extern( "cxr_end_vmf", + [POINTER(cxr_vmf_context),c_void_p], + None +) + +# VDF + with open wrapper +libcxr_vdf_open = extern( "cxr_vdf_open", [c_char_p], c_void_p ) +libcxr_vdf_close = extern( "cxr_vdf_close", [c_void_p], None ) +libcxr_vdf_put = extern( "cxr_vdf_put", [c_void_p,c_char_p], None ) +libcxr_vdf_node = extern( "cxr_vdf_node", [c_void_p,c_char_p], None ) +libcxr_vdf_edon = extern( "cxr_vdf_edon", [c_void_p], None ) +libcxr_vdf_kv = extern( "cxr_vdf_kv", [c_void_p,c_char_p,c_char_p], None ) -# Wrapper for vdf functions to allow: with o = vdf_structure ... class vdf_structure(): def __init__(_,path): _.path = path @@ -181,7 +409,6 @@ class vdf_structure(): def __exit__(_,type,value,traceback): if _.fp != None: libcxr_vdf_close.call(_.fp) - def put(_,s): libcxr_vdf_put.call(_.fp, s.encode('utf-8') ) def node(_,name): @@ -191,382 +418,120 @@ class vdf_structure(): def kv(_,k,v): libcxr_vdf_kv.call(_.fp, k.encode('utf-8'), v.encode('utf-8')) -class cxr_object_context(): - def __init__(_,scale,offset_z): - _.scale=scale - _.offset_z=offset_z +# Other +libcxr_lightpatch_bsp = extern( "cxr_lightpatch_bsp", [c_char_p], None ) -debug_gpu_lines = None -debug_gpu_mesh = None -debug_gpu_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') -debug_draw_handler = None +libcxr_funcs = [ libcxr_decompose, libcxr_free_world, libcxr_begin_vmf, \ + libcxr_vmf_begin_entities, libcxr_push_world_vmf, \ + libcxr_end_vmf, libcxr_vdf_open, libcxr_vdf_close, \ + libcxr_vdf_put, libcxr_vdf_node, libcxr_vdf_edon, + libcxr_vdf_kv, libcxr_lightpatch_bsp, libcxr_write_test_data,\ + libcxr_world_preview, libcxr_free_tri_mesh ] +# Callbacks def libcxr_log_callback(logStr): print( F"{logStr.decode('utf-8')}",end='' ) -debug_lines_positions = None -debug_lines_colours = None +cxr_line_positions = None +cxr_line_colours = None -def libcxr_reset_debug_lines(): - global debug_lines_positions - global debug_lines_colours +def cxr_reset_lines(): + global cxr_line_positions, cxr_line_colours - debug_lines_positions = [] - debug_lines_colours = [] + cxr_line_positions = [] + cxr_line_colours = [] -def libcxr_batch_debug_lines(): - global debug_lines_positions - global debug_lines_colours - global debug_gpu_lines - global debug_gpu_shader +def cxr_batch_lines(): + global cxr_line_positions, cxr_line_colours, cxr_view_shader, cxr_view_lines - debug_gpu_lines = batch_for_shader(\ - debug_gpu_shader, 'LINES',\ - { "pos": debug_lines_positions, "color": debug_lines_colours }) + cxr_view_lines = batch_for_shader(\ + cxr_view_shader, 'LINES',\ + { "pos": cxr_line_positions, "color": cxr_line_colours }) -@persistent -def cxr_on_load(dummy): - libcxr_reset_debug_lines() - libcxr_batch_debug_lines() +def libcxr_line_callback( p0,p1,colour ): + global cxr_line_colours, cxr_line_positions -@persistent -def cxr_dgraph_update(scene,dgraph): - return - print( F"Hallo {time.time()}" ) - -def libcxr_line_callback(p0,p1,colour): - global debug_lines_positions - global debug_lines_colours - debug_lines_positions += [(p0[0],p0[1],p0[2])] - debug_lines_positions += [(p1[0],p1[1],p1[2])] - debug_lines_colours += [(colour[0],colour[1],colour[2],colour[3])] - debug_lines_colours += [(colour[0],colour[1],colour[2],colour[3])] + cxr_line_positions += [(p0[0],p0[1],p0[2])] + cxr_line_positions += [(p1[0],p1[1],p1[2])] + cxr_line_colours += [(colour[0],colour[1],colour[2],colour[3])] + cxr_line_colours += [(colour[0],colour[1],colour[2],colour[3])] -def cxr_draw(): - global debug_gpu_lines - global debug_gpu_shader - global debug_gpu_mesh +# libnbvtf +# ------------------------------------------------------------------------------ - debug_gpu_shader.bind() - - gpu.state.depth_mask_set(False) - gpu.state.line_width_set(1.5) - gpu.state.face_culling_set('BACK') +libnbvtf = None - gpu.state.depth_test_set('NONE') - gpu.state.blend_set('ALPHA') - if debug_gpu_lines != None: - debug_gpu_lines.draw(debug_gpu_shader) +# Constants +NBVTF_IMAGE_FORMAT_RGBA8888 = 0 +NBVTF_IMAGE_FORMAT_RGB888 = 2 +NBVTF_IMAGE_FORMAT_DXT1 = 13 +NBVTF_IMAGE_FORMAT_DXT5 = 15 +NBVTF_TEXTUREFLAGS_CLAMPS = 0x00000004 +NBVTF_TEXTUREFLAGS_CLAMPT = 0x00000008 +NBVTF_TEXTUREFLAGS_NORMAL = 0x00000080 +NBVTF_TEXTUREFLAGS_NOMIP = 0x00000100 +NBVTF_TEXTUREFLAGS_NOLOD = 0x00000200 - gpu.state.depth_test_set('LESS_EQUAL') - gpu.state.blend_set('ADDITIVE') - if debug_gpu_mesh != None: - debug_gpu_mesh.draw(debug_gpu_shader) +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 ) -class CXR_RELOAD(bpy.types.Operator): - bl_idname="convexer.reload" - bl_label="Reload convexer" +libnbvtf_init = extern( "nbvtf_init", [], None ) +libnbvtf_funcs = [ libnbvtf_convert, libnbvtf_init ] - def execute(_,context): - global libcxr, libnbvtf, libcxr_funcs, libnbvtf_funcs +# Loading +# -------------------------- - # Load vtf library - libnbvtf = cdll.LoadLibrary( os.path.dirname(__file__)+'/libnbvtf.so') +def shared_reload(): + global libcxr, libnbvtf, libcxr_funcs, libnbvtf_funcs - if libcxr != None: - _handle = libcxr._handle - + # Unload libraries if existing + def _reload( lib, path ): + if lib != None: + _handle = lib._handle for i in range(10): libc_dlclose( _handle ) - del libcxr + lib = None + del lib + return cdll.LoadLibrary( F'{os.path.dirname(__file__)}/{path}.so' ) - libcxr = None - libcxr = cdll.LoadLibrary( os.path.dirname(__file__)+'/libcxr.so') - - build_time = c_char_p.in_dll(libcxr,'cxr_build_time') - print( F"libcxr build time: {build_time.value}" ) + libnbvtf = _reload( libnbvtf, "libnbvtf" ) + libcxr = _reload( libcxr, "libcxr" ) - for fd in libnbvtf_funcs: - fd.loadfrom( libnbvtf ) + for fd in libnbvtf_funcs: + fd.loadfrom( libnbvtf ) + libnbvtf_init.call() - for fd in libcxr_funcs: - fd.loadfrom( libcxr ) + for fd in libcxr_funcs: + fd.loadfrom( libcxr ) - # Callbacks - global c_libcxr_log_callback, c_libcxr_line_callback + # Callbacks + global c_libcxr_log_callback, c_libcxr_line_callback - LOG_FUNCTION_TYPE = CFUNCTYPE(None,c_char_p) - c_libcxr_log_callback = LOG_FUNCTION_TYPE(libcxr_log_callback) - libcxr.cxr_set_log_function(cast(c_libcxr_log_callback,c_void_p)) + LOG_FUNCTION_TYPE = CFUNCTYPE(None,c_char_p) + c_libcxr_log_callback = LOG_FUNCTION_TYPE(libcxr_log_callback) - LINE_FUNCTION_TYPE = CFUNCTYPE(None,\ - POINTER(c_double),POINTER(c_double),POINTER(c_double)) - c_libcxr_line_callback = LINE_FUNCTION_TYPE(libcxr_line_callback) - libcxr.cxr_set_line_function(cast(c_libcxr_line_callback,c_void_p)) + LINE_FUNCTION_TYPE = CFUNCTYPE(None,\ + POINTER(c_double), POINTER(c_double), POINTER(c_double)) + c_libcxr_line_callback = LINE_FUNCTION_TYPE(libcxr_line_callback) - return {'FINISHED'} + libcxr.cxr_set_log_function(cast(c_libcxr_log_callback,c_void_p)) + libcxr.cxr_set_line_function(cast(c_libcxr_line_callback,c_void_p)) -def libcxr_use(): - global libcxr + build_time = c_char_p.in_dll(libcxr,'cxr_build_time') + print( F"libcxr build time: {build_time.value}" ) - if libcxr == None: - bpy.ops.convexer.reload() +shared_reload() -def to_aeiou( v ): - ret = "" - if v == 0: - return "z" - dig = [] - while v: - dig.append( int( v % 5 ) ) - v //= 5 - for d in dig[::-1]: - ret += [ 'a','e','i','o','u' ][d] - return ret +# Configuration +# ------------------------------------------------------------------------------ -def asset_uid(asset): - if isinstance(asset,str): - return asset - name = to_aeiou(asset.cxr_data.asset_id) - if bpy.context.scene.cxr_data.include_names: - name += asset.name.replace('.','_') - return name - -# -> / -def asset_name(asset): - return F"{bpy.context.scene.cxr_data.project_name}/{asset_uid(asset)}" - -# -> // -def asset_path(subdir, asset): - return F"{subdir}/{asset_name(asset_uid(asset))}" - -# -> /// -def asset_full_path(sdir,asset): - return F"{bpy.context.scene.cxr_data.subdir}/"+\ - F"{asset_path(sdir,asset_uid(asset))}" - -# view_layer.update() doesnt seem to work, -# tag_redraw() seems to have broken -# therefore, change a property -def scene_redraw(): - ob = bpy.context.scene.objects[0] - ob.hide_render = ob.hide_render - - # the 'real' way to refresh the scene - #for area in bpy.context.window.screen.areas: - # if area.type == 'view_3d': - # area.tag_redraw() - -# The default shader is the first entry +# Standard entity functions, think of like base.fgd # -cxr_shaders = { - "LightMappedGeneric": - { - "name": "Light Mapped", - "id": 0 - }, - "VertexLitGeneric": - { - "name": "Vertex Lit", - "id": 1 - }, - "UnlitGeneric": - { - "name": "Unlit", - "id": 2 - }, - "Builtin": - { - "name": "Builtin", - "id": 3 - } -} - -def material_tex_image(v): - return {\ - "ShaderNodeTexImage": - { - "image": F"${v}" - } - } - -cxr_graph_mapping = { - "ShaderNodeBsdfPrincipled": - { - "Base Color": - { - "ShaderNodeMixRGB": - { - "Color1": material_tex_image("basetexture"), - "Color2": material_tex_image("decaltexture") - }, - "ShaderNodeTexImage": - { - "image":"$basetexture" - }, - "default": - [("VertexLitGeneric","$color2"),\ - ("UnlitGeneric","$color2"),\ - ("LightMappedGeneric","$color")] - }, - "Normal": - { - "ShaderNodeNormalMap": - { - "Color": material_tex_image("bumpmap") - } - } - } -} - -cxr_shader_params = { - "Textures": - { - "type": "ui", - "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"), - - "$basetexture": - { - "name": "Base Texture", - "type": "intrinsic", - "default": None - }, - "$decaltexture": - { - "name": "Decal Texture", - "type": "intrinsic", - "default": None, - - "$decalblendmode": - { - "name": "Blend Mode", - "type": "enum", - "items": [ - ('0',"AlphaOver","Default",'',0), - ('1',"Multiply","",'',1), - ('2',"Modulate","",'',2), - ('3',"Additive","",'',3) - ], - "default": 0, - "always": True - } - }, - "$bumpmap": - { - "name": "Normal Map", - "type": "intrinsic", - "flags": NBVTF_TEXTUREFLAGS_NORMAL, - "default": None - } - }, - "$color": - { - "name": "Color", - "type": "intrinsic", - "default": None, - "exponent": 2.2 - }, - "$color2": - { - "name": "Color2", - "type": "intrinsic", - "default": None, - "exponent": 2.2 - }, - "Lighting": - { - "type": "ui", - "shaders": ("VertexLitGeneric", "LightMappedGeneric"), - - "$phong": - { - "name": "Phong", - "type": "bool", - "default": False, - - "$phongexponent": - { - "name": "Exponent", - "type": "float", - "default": 5.0 - }, - "$phongboost": - { - "name": "Boost", - "type": "float", - "default": 1.0 - }, - "$phongfresnelranges": - { - "name": "Fresnel Ranges", - "type": "vector", - "default":(1.0,1.0,1.0) - } - }, - "$envmap": - { - "name": "Cubemap", - "type": "string", - "default": "", - - "$envmaptint": - { - "name": "Tint", - "type": "vector", - "subtype": 'COLOR', - "default": (1.0,1.0,1.0) - }, - "$envmaplightscale": - { - "name": "Light Scale", - "type": "float", - "default": 0.0 - }, - "$envmaplightscaleminmax": - { - "name": "Min/Max", - "type": "vector", - "default": (0.0,1.0) - } - } - }, - "Transparency": - { - "type": "ui", - "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"), - - "$translucent": - { - "name": "Translucent", - "type": "bool", - "default": False - }, - "$alphatest": - { - "name": "Alpha Test", - "type": "bool", - "default": False, - - "$alphatestreference": - { - "name": "Step", - "type": "float", - "default": 0.5 - } - }, - "$nocull": - { - "name": "No Cull", - "type": "bool", - "default": False - } - } -} +def cxr_get_origin(obj,context): + return obj.location * context['scale'] + mathutils.Vector(context['offset']) -def ent_get_origin(obj,context): - return obj.location * context.scale - -def ent_get_angles(obj,context): +def cxr_get_angles(obj,context): euler = [ a*57.295779513 for a in obj.rotation_euler ] angle = [0,0,0] angle[0] = euler[1] @@ -574,18 +539,16 @@ def ent_get_angles(obj,context): angle[2] = euler[0] return angle -def ent_baseclass(classes, other): +def cxr_baseclass(classes, other): base = other.copy() for x in classes: base.update(x.copy()) return base -ent_origin = { "origin": ent_get_origin } -ent_angles = { "angles": ent_get_angles } -ent_transform = ent_baseclass( [ent_origin], ent_angles ) - +# EEVEE Light component converter -> Source 1 +# def ent_lights(obj,context): - kvs = ent_baseclass([ent_origin],\ + kvs = cxr_baseclass([ent_origin],\ { "_distance": (0.0 if obj.data.cxr_data.realtime else -1.0), "_light": [int(pow(obj.data.color[i],1.0/2.2)*255.0) for i in range(3)] +\ @@ -625,45 +588,76 @@ def ent_lights(obj,context): return kvs def ent_cubemap(obj,context): - return ent_baseclass([ent_origin],\ - {"cubemapsize": obj.data.cxr_data.size}) + return cxr_baseclass([ent_origin], {"cubemapsize": obj.data.cxr_data.size}) -cxr_entities = { - "info_player_counterterrorist": - { - "gizmo": [], - "allow": ('EMPTY',), - "keyvalues": ent_baseclass([ent_transform],\ - { - "priority": {"type": "int", "default": 0 }, - "enabled": {"type": "int", "default": 1 }, - }) - }, - "info_player_terrorist": - { - "gizmo": [], - "allow": ('EMPTY',), - "keyvalues": ent_baseclass([ent_transform],\ - { - "priority": {"type": "int", "default": 0 }, - "enabled": {"type": "int", "default": 1 }, - }) - }, - "light": { "keyvalues": ent_lights }, - "light_spot": { "keyvalues": ent_lights }, - # SUN - "env_cubemap": { "keyvalues": ent_cubemap }, - - # Brush entites - "func_buyzone": - { - "allow": ('MESH',), - "keyvalues": - { - "TeamNum": {"type": "int", "default": 0 } - } - } -} +ent_origin = { "origin": cxr_get_origin } +ent_angles = { "angles": cxr_get_angles } +ent_transform = cxr_baseclass( [ent_origin], ent_angles ) + +#include the user config +exec(open(F'{os.path.dirname(__file__)}/config.py').read()) + +# Blender state callbacks +# ------------------------------------------------------------------------------ + +@persistent +def cxr_on_load(dummy): + global cxr_view_lines, cxr_view_mesh + + cxr_view_lines = None + cxr_view_mesh = None + +@persistent +def cxr_dgraph_update(scene,dgraph): + return + print( F"Hallo {time.time()}" ) + +# Convexer compilation functions +# ------------------------------------------------------------------------------ + +# Asset path management + +def asset_uid(asset): + if isinstance(asset,str): + return asset + + # Create a unique ID string + base = "ABCDEFGHIJKLMNOPQRSTUV" + v = asset.cxr_data.asset_id + name = "" + + if v == 0: + name = "A" + else: + dig = [] + + while v: + dig.append( int( v % len(base) ) ) + v //= len(base) + + for d in dig[::-1]: + name += base[d] + + if bpy.context.scene.cxr_data.include_names: + name += asset.name.replace('.','_') + + return name + +# -> / +def asset_name(asset): + return F"{bpy.context.scene.cxr_data.project_name}/{asset_uid(asset)}" + +# -> // +def asset_path(subdir, asset): + return F"{subdir}/{asset_name(asset_uid(asset))}" + +# -> /// +def asset_full_path(sdir,asset): + return F"{bpy.context.scene.cxr_data.subdir}/"+\ + F"{asset_path(sdir,asset_uid(asset))}" + +# Entity functions / infos +# ------------------------ def cxr_intrinsic_classname(obj): if obj.type == 'LIGHT': @@ -731,6 +725,8 @@ def cxr_entity_keyvalues(obj,context,classname): return result +# Extract material information from shader graph data +# def material_info(mat): info = {} info['res'] = (512,512) @@ -744,11 +740,7 @@ def material_info(mat): info['name'] = mat.name return info - if not hasattr(material_info,'references'): - material_info.references = set() - - # Custom material - material_info.references.add(mat) + # Custom materials info['name'] = asset_name(mat) # Using the cxr_graph_mapping as a reference, go through the shader @@ -813,269 +805,350 @@ def material_info(mat): return info -def mesh_cxr_format(obj): - orig_state = obj.mode - if orig_state != 'OBJECT': - bpy.ops.object.mode_set(mode='OBJECT') +# Prepares Scene into dictionary format +# +def cxr_scene_collect(): + context = bpy.context + + # Make sure all of our asset types have a unique ID + def _uid_prepare(objtype): + used_ids = [0] + to_generate = [] + id_max = 0 + for o in objtype: + vs = o.cxr_data + if vs.asset_id in used_ids: + to_generate+=[vs] + else: + id_max = max(id_max,vs.asset_id) + used_ids+=[vs.asset_id] + for vs in to_generate: + id_max += 1 + vs.asset_id = id_max + _uid_prepare(bpy.data.materials) + _uid_prepare(bpy.data.images) + _uid_prepare(bpy.data.collections) + + sceneinfo = { + "entities": [], # Everything with a classname + "geo": [], # All meshes without a classname + "heros": [] # Collections prefixed with mdl_ + } - dgraph = bpy.context.evaluated_depsgraph_get() - data = obj.evaluated_get(dgraph).data - - _,mtx_rot,_ = obj.matrix_world.decompose() + def _collect(collection,transform): + nonlocal sceneinfo + + if collection.name.startswith('.'): return + if collection.hide_render: return + + if collection.name.startswith('mdl_'): + sceneinfo['heros'] += [{ + "collection": collection, + "transform": transform + }] + return + + for obj in collection.objects: + if obj.hide_get(): continue + + classname = cxr_classname( obj ) + + if classname != None: + sceneinfo['entities'] += [{ + "object": obj, + "classname": classname, + "transform": transform + }] + elif obj.type == 'MESH': + sceneinfo['geo'] += [{ + "object": obj, + "transform": transform + }] + + for c in collection.children: + cxr_scene_collect( c, transform ) + + transform_main = { + "scale": context.scene.cxr_data.scale_factor, + "offset": (0,0,0) + } - mesh = cxr_static_mesh() + transform_sky = { + "scale": context.scene.cxr_data.skybox_scale_factor, + "offset": (0,0,context.scene.cxr_data.skybox_offset ) + } - vertex_data = ((c_double*3)*len(data.vertices))() - for i, vert in enumerate(data.vertices): - v = obj.matrix_world @ vert.co - vertex_data[i][0] = c_double(v[0]) - vertex_data[i][1] = c_double(v[1]) - vertex_data[i][2] = c_double(v[2]) + if 'main' in bpy.data.collections: + _collect( bpy.data.collections['main'], transform_main ) - loop_data = (cxr_static_loop*len(data.loops))() - polygon_data = (cxr_polygon*len(data.polygons))() + if 'skybox' in bpy.data.collections: + _collect( bpy.data.collections['skybox'], transform_sky ) - for i, poly in enumerate(data.polygons): - loop_start = poly.loop_start - loop_end = poly.loop_start + poly.loop_total - for loop_index in range(loop_start, loop_end): - loop = data.loops[loop_index] - loop_data[loop_index].index = loop.vertex_index - loop_data[loop_index].edge_index = loop.edge_index + return sceneinfo - if data.uv_layers: - uv = data.uv_layers.active.data[loop_index].uv - loop_data[loop_index].uv[0] = c_double(uv[0]) - loop_data[loop_index].uv[1] = c_double(uv[1]) - else: - loop_data[loop_index].uv[0] = c_double(0.0) - loop_data[loop_index].uv[1] = c_double(0.0) - center = obj.matrix_world @ poly.center - normal = mtx_rot @ poly.normal +# Write VMF out to file (JOB HANDLER) +# +def cxr_export_vmf(sceneinfo): + cxr_reset_lines() + + # Setup output and state + filepath = bpy.data.filepath + directory = os.path.dirname(filepath) + settings = bpy.context.scene.cxr_data + + asset_dir = F"{directory}/bin" + material_dir = F"{settings.subdir}/materials/{settings.project_name}" + model_dir = F"{settings.subdir}/models/{settings.project_name}" - polygon_data[i].loop_start = poly.loop_start - polygon_data[i].loop_total = poly.loop_total - polygon_data[i].normal[0] = normal[0] - polygon_data[i].normal[1] = normal[1] - polygon_data[i].normal[2] = normal[2] - polygon_data[i].center[0] = center[0] - polygon_data[i].center[1] = center[1] - polygon_data[i].center[2] = center[2] - polygon_data[i].material_id = poly.material_index + os.makedirs( asset_dir, exist_ok=True ) + os.makedirs( material_dir, exist_ok=True ) + os.makedirs( model_dir, exist_ok=True ) + + # States + cxr_reset_lines() + output_vmf = F"{directory}/{settings.project_name}.vmf" - edge_data = (cxr_edge*len(data.edges))() + with vdf_structure(output_vmf) as m: + print( F"Write: {output_vmf}" ) - for i, edge in enumerate(data.edges): - edge_data[i].i0 = edge.vertices[0] - edge_data[i].i1 = edge.vertices[1] - edge_data[i].freestyle = edge.use_freestyle_mark + vmfinfo = cxr_vmf_context() + vmfinfo.mapversion = 4 + + #TODO: These need to be in options... + vmfinfo.skyname = b"sky_csgo_night02b" + vmfinfo.detailvbsp = b"detail.vbsp" + vmfinfo.detailmaterial = b"detail/detailsprites" + vmfinfo.lightmap_scale = 12 + + vmfinfo.brush_count = 0 + vmfinfo.entity_count = 0 + vmfinfo.face_count = 0 + + libcxr_begin_vmf.call( pointer(vmfinfo), m.fp ) - material_data = (cxr_material*len(obj.material_slots))() + def _buildsolid( cmd ): + nonlocal m - for i, ms in enumerate(obj.material_slots): - inf = material_info(ms.material) - material_data[i].res[0] = inf['res'][0] - material_data[i].res[1] = inf['res'][1] - material_data[i].vmt_path = inf['name'].encode('utf-8') - - mesh.edges = cast(edge_data, POINTER(cxr_edge)) - mesh.vertices = cast(vertex_data, POINTER(c_double*3)) - mesh.loops = cast(loop_data,POINTER(cxr_static_loop)) - mesh.polys = cast(polygon_data, POINTER(cxr_polygon)) - mesh.materials = cast(material_data, POINTER(cxr_material)) - - mesh.poly_count = len(data.polygons) - mesh.vertex_count = len(data.vertices) - mesh.edge_count = len(data.edges) - mesh.loop_count = len(data.loops) - mesh.material_count = len(obj.material_slots) + baked = mesh_cxr_format( cmd['object'] ) + world = libcxr_decompose.call( baked, None ) + + if world == None: + return False - bpy.ops.object.mode_set(mode=orig_state) - return mesh + vmfinfo.scale = cmd['transform']['scale'] -class CXR_WRITE_VMF(bpy.types.Operator): - bl_idname="convexer.write_vmf" - bl_label="Write VMF" + offset = cmd['transform']['offset'] + vmfinfo.offset[0] = offset[0] + vmfinfo.offset[1] = offset[1] + vmfinfo.offset[2] = offset[2] - def execute(_,context): - libcxr_use() - libcxr_reset_debug_lines() - - # Setup output and state - filepath = bpy.data.filepath - directory = os.path.dirname(filepath) - settings = context.scene.cxr_data - - asset_dir = F"{directory}/bin" - material_dir = F"{settings.subdir}/materials/{settings.project_name}" - model_dir = F"{settings.subdir}/models/{settings.project_name}" + libcxr_push_world_vmf.call( world, pointer(vmfinfo), m.fp ) + libcxr_free_world.call( world ) + + return True + + # World geometry + for brush in sceneinfo['geo']: + if not _buildsolid( brush ): + cxr_batch_lines() + scene_redraw() + return False - os.makedirs( asset_dir, exist_ok=True ) - os.makedirs( material_dir, exist_ok=True ) - os.makedirs( model_dir, exist_ok=True ) + libcxr_vmf_begin_entities.call(pointer(vmfinfo), m.fp) - # States - libcxr_reset_debug_lines() - material_info.references = set() - output_vmf = F"{directory}/{settings.project_name}.vmf" - - with vdf_structure(output_vmf) as m: - print( F"Write: {output_vmf}" ) - - vmfinfo = cxr_vmf_context() - vmfinfo.mapversion = 4 - vmfinfo.skyname = b"sky_csgo_night02b" - vmfinfo.detailvbsp = b"detail.vbsp" - vmfinfo.detailmaterial = b"detail/detailsprites" - vmfinfo.lightmap_scale = 12 - vmfinfo.brush_count = 0 - vmfinfo.entity_count = 0 - vmfinfo.face_count = 0 - - libcxr_begin_vmf.call( pointer(vmfinfo), m.fp ) - - # Make sure all of our asset types have a unique ID - def _uid_prepare(objtype): - used_ids = [0] - to_generate = [] - id_max = 0 - for o in objtype: - vs = o.cxr_data - if vs.asset_id in used_ids: - to_generate+=[vs] - else: - id_max = max(id_max,vs.asset_id) - used_ids+=[vs.asset_id] - for vs in to_generate: - id_max += 1 - vs.asset_id = id_max + # Entities + for ent in sceneinfo['entities']: + obj = ent['object'] + ctx = ent['transform'] + cls = ent['classname'] + + m.node( 'entity' ) + m.kv( 'classname', cls ) + + kvs = cxr_entity_keyvalues( obj, ctx, cls ) + + for kv in kvs: + if isinstance(kv[2], list): + m.kv( kv[0], ' '.join([str(_) for _ in kv[2]]) ) + else: m.kv( kv[0], str(kv[2]) ) - _uid_prepare(bpy.data.materials) - _uid_prepare(bpy.data.images) - _uid_prepare(bpy.data.collections) - - # Export Brushes and displacement - def _collect(collection,transform): - if collection.name.startswith('.'): - return - - if collection.hide_render: - return + if obj.type == 'MESH': + if not _buildsolid( ent ): + cxr_batch_lines() + scene_redraw() + return False - if collection.name.startswith('mdl_'): - _collect.heros += [(collection,transform)] - return + m.edon() - for obj in collection.objects: - if obj.hide_get(): continue + print( "Done" ) + return True - classname = cxr_classname( obj ) +# COmpile image using NBVTF and hash it (JOB HANDLER) +# +def compile_image(img): + if img==None: + return None - if classname != None: - _collect.entities += [( obj,transform,classname )] - elif obj.type == 'MESH': - _collect.geo += [(obj,transform)] + name = asset_name(img) + src_path = bpy.path.abspath(img.filepath) - for c in collection.children: - _collect( c, transform ) - - _collect.a_models = set() - _collect.entities = [] - _collect.geo = [] - _collect.heros = [] + dims = img.cxr_data.export_res + fmt = { + 'RGBA': NBVTF_IMAGE_FORMAT_RGBA8888, + 'DXT1': NBVTF_IMAGE_FORMAT_DXT1, + 'DXT5': NBVTF_IMAGE_FORMAT_DXT5, + 'RGB': NBVTF_IMAGE_FORMAT_RGB888 + }[ img.cxr_data.fmt ] - transform_main = cxr_object_context( \ - context.scene.cxr_data.scale_factor, 0.0 ) + mipmap = img.cxr_data.mipmap + lod = img.cxr_data.lod + clamp = img.cxr_data.clamp + flags = img.cxr_data.flags - transform_sky = cxr_object_context( \ - context.scene.cxr_data.skybox_scale_factor, \ - context.scene.cxr_data.skybox_offset ) - - if 'main' in bpy.data.collections: - _collect( bpy.data.collections['main'], transform_main ) + q=bpy.context.scene.cxr_data.image_quality - if 'skybox' in bpy.data.collections: - _collect( bpy.data.collections['skybox'], transform_sky ) - - def _buildsolid( obj, ctx ): - nonlocal m + userflag_hash = F"{mipmap}.{lod}.{clamp}.{flags}" + file_hash = F"{name}.{os.path.getmtime(src_path)}" + comphash = F"{file_hash}.{dims[0]}.{dims[1]}.{fmt}.{userflag_hash}.{q}" - baked = mesh_cxr_format( brush[0] ) - world = libcxr_decompose.call( baked, None ) + if img.cxr_data.last_hash != comphash: + print( F"Texture update: {img.filepath}" ) + + src = src_path.encode('utf-8') + dst = (asset_full_path('materials',img)+'.vtf').encode('utf-8') + + flags_full = flags + + # texture setting flags + if not lod: flags_full |= NBVTF_TEXTUREFLAGS_NOLOD + if clamp: + flags_full |= NBVTF_TEXTUREFLAGS_CLAMPS + flags_full |= NBVTF_TEXTUREFLAGS_CLAMPT + + if libnbvtf_convert.call(src,dims[0],dims[1],mipmap,fmt,q,flags_full,dst): + img.cxr_data.last_hash = comphash + + return name + +# +# Compile a material to VMT format. This is quick to run, doesnt need to be a +# job handler. +# +def compile_material(mat): + print( F"Compile {asset_full_path('materials',mat)}.vmt" ) + + info = material_info(mat) + properties = mat.cxr_data + + props = [] + + # Walk the property tree + def _mlayer( layer ): + nonlocal properties, props + + for decl in layer: + if isinstance(layer[decl],dict): # $property definition + pdef = layer[decl] + ptype = pdef['type'] + + subdefines = False + default = None + prop = None + + if 'shaders' in pdef and properties.shader not in pdef['shaders']: + continue + + # Group expansion (does it have subdefinitions?) + for ch in pdef: + if isinstance(pdef[ch],dict): + subdefines = True + break - if world == None: - return False + expandview = False + + if ptype == 'ui': + expandview = True + else: + if ptype == 'intrinsic': + if decl in info: + prop = info[decl] + else: + prop = getattr(properties,decl) + default = pdef['default'] + + if not isinstance(prop,str) and \ + not isinstance(prop,bpy.types.Image) and \ + hasattr(prop,'__getitem__'): + prop = tuple([p for p in prop]) - vmfinfo.scale = brush[1].scale - vmfinfo.offset[0] = 0.0 - vmfinfo.offset[1] = 0.0 - vmfinfo.offset[2] = brush[1].offset_z + if prop != default: + # write prop + props += [(decl,pdef,prop)] + + if subdefines: + expandview = True - libcxr_push_world_vmf.call( world, pointer(vmfinfo), m.fp ) - libcxr_free_world.call( world ) + if expandview: _mlayer(pdef) - return True + _mlayer( cxr_shader_params ) - # World geometry - for brush in _collect.geo: - if not _buildsolid( brush[0], brush[1] ): - libcxr_batch_debug_lines() - scene_redraw() - return {'CANCELLED'} + # Write the vmt + with vdf_structure( F"{asset_full_path('materials',mat)}.vmt" ) as vmt: + vmt.node( properties.shader ) + vmt.put( "// Convexer export\n" ) - m.edon() - - # Entities - for entity in _collect.entities: - obj = entity[0] - ctx = entity[1] - cls = entity[2] - m.node( 'entity' ) - m.kv( 'classname', cls ) - - kvs = cxr_entity_keyvalues( obj, ctx, cls ) - - for kv in kvs: - if isinstance(kv[2], list): - m.kv( kv[0], ' '.join([str(_) for _ in kv[2]]) ) - else: m.kv( kv[0], str(kv[2]) ) - - if not _buildsolid( obj, ctx ): - libcxr_batch_debug_lines() - scene_redraw() - return {'CANCELLED'} + for pair in props: + decl = pair[0] + pdef = pair[1] + prop = pair[2] - m.edon() - - print( "[CONVEXER] Compile materials / textures" ) + def _numeric(v): + nonlocal pdef + if 'exponent' in pdef: return str(pow( v, pdef['exponent'] )) + else: return str(v) - for mat in material_info.references: - compile_material(mat) + if isinstance(prop,bpy.types.Image): + vmt.kv( decl, asset_name(prop)) + elif isinstance(prop,bool): + vmt.kv( decl, '1' if prop else '0' ) + elif isinstance(prop,str): + vmt.kv( decl, prop ) + elif isinstance(prop,float) or isinstance(prop,int): + vmt.kv( decl, _numeric(prop) ) + elif isinstance(prop,tuple): + vmt.kv( decl, F"[{' '.join([_numeric(_) for _ in prop])}]" ) + else: + vmt.put( F"// (cxr) unkown shader value type'{type(prop)}'" ) - print( "[CONVEXER] Compiling models" ) + vmt.edon() + return props - libcxr_batch_debug_lines() - scene_redraw() +# Convexer operators +# ------------------------------------------------------------------------------ +# Force reload of shared libraries +# +class CXR_RELOAD(bpy.types.Operator): + bl_idname="convexer.reload" + bl_label="Reload" + def execute(_,context): + shared_reload() return {'FINISHED'} +# Used for exporting data to use with ASAN builds +# class CXR_DEV_OPERATOR(bpy.types.Operator): bl_idname="convexer.dev_test" bl_label="Export development data" def execute(_,context): - libcxr_use() - # Prepare input data mesh_src = mesh_cxr_format(context.active_object) - - libcxr_reset_debug_lines() libcxr_write_test_data.call( pointer(mesh_src) ) - libcxr_batch_debug_lines() - - scene_redraw() return {'FINISHED'} +# UI: Preview how the brushes will looks in 3D view +# class CXR_PREVIEW_OPERATOR(bpy.types.Operator): bl_idname="convexer.preview" bl_label="Preview Brushes" @@ -1087,27 +1160,26 @@ class CXR_PREVIEW_OPERATOR(bpy.types.Operator): return {'FINISHED'} def modal(_,context,event): - global debug_gpu_mesh + global cxr_view_mesh static = _.__class__ if event.type == 'ESC': - libcxr_reset_debug_lines() - libcxr_batch_debug_lines() - debug_gpu_mesh = None - scene_redraw() - + cxr_reset_lines() + cxr_batch_lines() + cxr_view_mesh = None static.RUNNING = False + + scene_redraw() return {'FINISHED'} return {'PASS_THROUGH'} def invoke(_,context,event): - global debug_gpu_shader, debug_gpu_mesh + global cxr_view_shader, cxr_view_mesh static = _.__class__ static.LASTERR = None - libcxr_use() - libcxr_reset_debug_lines() + cxr_reset_lines() mesh_src = mesh_cxr_format(context.active_object) @@ -1115,8 +1187,8 @@ class CXR_PREVIEW_OPERATOR(bpy.types.Operator): world = libcxr_decompose.call( mesh_src, pointer(err) ) if world == None: - debug_gpu_mesh = None - libcxr_batch_debug_lines() + cxr_view_mesh = None + cxr_batch_lines() scene_redraw() static.LASTERR = ["There is no error", \ @@ -1134,6 +1206,8 @@ class CXR_PREVIEW_OPERATOR(bpy.types.Operator): context.window_manager.modal_handler_add(_) return {'RUNNING_MODAL'} + # Generate preview using cxr + # ptrpreview = libcxr_world_preview.call( world ) preview = ptrpreview[0] @@ -1147,24 +1221,187 @@ class CXR_PREVIEW_OPERATOR(bpy.types.Operator): indices = [ (indices[i*3+0],indices[i*3+1],indices[i*3+2]) \ for i in range(int(preview.indices_count/3)) ] - debug_gpu_mesh = batch_for_shader( - debug_gpu_shader, 'TRIS', + cxr_view_mesh = batch_for_shader( + cxr_view_shader, 'TRIS', { "pos": vertices, "color": colours }, indices = indices, ) libcxr_free_tri_mesh.call( ptrpreview ) libcxr_free_world.call( world ) - libcxr_batch_debug_lines() + cxr_batch_lines() scene_redraw() + # Allow user to spam the operator if static.RUNNING: return {'CANCELLED'} + if not static.RUNNING: static.RUNNING = True context.window_manager.modal_handler_add(_) return {'RUNNING_MODAL'} +# Search for VMF compiler executables in subdirectory +# +class CXR_DETECT_COMPILERS(bpy.types.Operator): + bl_idname="convexer.detect_compilers" + bl_label="Find compilers" + + def execute(self,context): + scene = context.scene + settings = scene.cxr_data + subdir = settings.subdir + + for exename in ['studiomdl','vbsp','vvis','vrad']: + searchpath = os.path.normpath(F'{subdir}/../bin/{exename}.exe') + if os.path.exists(searchpath): + settings[F'exe_{exename}'] = searchpath + + return {'FINISHED'} + +# Main compile function +# +class CXR_COMPILER_CHAIN(bpy.types.Operator): + bl_idname="convexer.chain" + bl_label="Compile Chain" + + # 'static' + USER_EXIT = False + SUBPROC = None + TIMER = None + TIMER_LAST = 0.0 + WAIT_REDRAW = False + + JOBINFO = None + JOBID = 0 + + def cancel(_,context): + static = _.__class__ + wm = context.window_manager + + if static.SUBPROC != None: + static.SUBPROC.terminate() + static.SUBPROC = None + + if static.TIMER != None: + wm.event_timer_remove( static.TIMER ) + static.TIMER = None + + scene_redraw() + return {'FINISHED'} + + def modal(_,context,ev): + static = _.__class__ + + if ev.type == 'TIMER': + if static.WAIT_REDRAW: + return {'PASS_THROUGH'} + static.WAIT_REDRAW = True + + if static.USER_EXIT: + print( "Chain USER_EXIT" ) + return _.cancel(context) + + if static.SUBPROC != None: + # Deal with async modes + pass + + # Compile syncronous thing + for sys in static.JOBINFO: + for i,target in enumerate(sys['jobs']): + if target != None: + print( F"Start job: {static.JOBID} @{time.time()}" ) + + if not sys['exec'](target): + print( "Job failed" ) + return _.cancel(context) + + sys['jobs'][i] = None + static.JOBID += 1 + + cxr_jobs_update_graph( static.JOBINFO ) + scene_redraw() + return {'PASS_THROUGH'} + + # All completed + print( "All jobs completed!" ) + global cxr_jobs_batch + cxr_jobs_batch = None + + scene_redraw() + return _.cancel(context) + + return {'PASS_THROUGH'} + + def invoke(_,context,event): + static = _.__class__ + wm = context.window_manager + + if static.TIMER == None: + print("Launching compiler toolchain") + + # Run static compilation units now (collect, vmt..) + sceneinfo = cxr_scene_collect() + image_jobs = [] + + # Collect materials + a_materials = set() + for brush in sceneinfo['geo']: + for ms in brush['object'].material_slots: + a_materials.add( ms.material ) + + # Collect images + for mat in a_materials: + for pair in compile_material(mat): + decl = pair[0] + pdef = pair[1] + prop = pair[2] + + if isinstance(prop,bpy.types.Image): + flags = 0 + if 'flags' in pdef: flags = pdef['flags'] + if prop not in image_jobs: + image_jobs += [prop] + prop.cxr_data.flags = flags + + # Convexer jobs + static.JOBID = 0 + static.JOBINFO = [] + + static.JOBINFO += [{ + "title": "Convexer", + "w": 40, + "colour": (1.0,0.3,0.1,1.0), + "exec": cxr_export_vmf, + "jobs": [sceneinfo] + }] + + if len(image_jobs) > 0: + static.JOBINFO += [{ + "title": "Textures", + "w": 40, + "colour": (0.1,1.0,0.3,1.0), + "exec": compile_image, + "jobs": image_jobs + }] + + static.USER_EXIT=False + static.TIMER=wm.event_timer_add(0.1,window=context.window) + wm.modal_handler_add(_) + + cxr_jobs_update_graph( static.JOBINFO ) + scene_redraw() + return {'RUNNING_MODAL'} + + print("Chain exiting...") + static.USER_EXIT=True + return {'RUNNING_MODAL'} + +# Convexer panels +# ------------------------------------------------------------------------------ + +# Helper buttons for 3d toolbox view +# class CXR_VIEW3D( bpy.types.Panel ): bl_idname = "VIEW3D_PT_convexer" bl_label = "Convexer" @@ -1186,6 +1423,8 @@ class CXR_VIEW3D( bpy.types.Panel ): box = layout.box() box.label(text=CXR_PREVIEW_OPERATOR.LASTERR, icon='ERROR') +# Main scene properties interface, where all the settings go +# class CXR_INTERFACE(bpy.types.Panel): bl_label="Convexer" bl_idname="SCENE_PT_convexer" @@ -1197,7 +1436,6 @@ class CXR_INTERFACE(bpy.types.Panel): _.layout.operator("convexer.reload") _.layout.operator("convexer.dev_test") _.layout.operator("convexer.preview") - _.layout.operator("convexer.write_vmf") settings = context.scene.cxr_data @@ -1205,6 +1443,7 @@ class CXR_INTERFACE(bpy.types.Panel): _.layout.prop(settings, "scale_factor") _.layout.prop(settings, "lightmap_scale") _.layout.prop(settings, "light_scale" ) + _.layout.prop(settings, "image_quality" ) box = _.layout.box() @@ -1217,140 +1456,12 @@ class CXR_INTERFACE(bpy.types.Panel): box.prop(settings, "exe_vbsp") box.prop(settings, "exe_vvis") box.prop(settings, "exe_vrad") + + text = "Compile" if CXR_COMPILER_CHAIN.TIMER == None else "Cancel" + row = box.row() + row.scale_y = 3 + row.operator("convexer.chain", text=text) -# COmpile image using NBVTF and hash it -def compile_image(img,flags): - if img==None: - return None - - name = asset_name(img) - src_path = bpy.path.abspath(img.filepath) - - dims = img.cxr_data.export_res - fmt = { - 'RGBA': NBVTF_IMAGE_FORMAT_RGBA8888, - 'DXT1': NBVTF_IMAGE_FORMAT_DXT1, - 'DXT5': NBVTF_IMAGE_FORMAT_DXT5, - 'RGB': NBVTF_IMAGE_FORMAT_RGB888 - }[ img.cxr_data.fmt ] - - mipmap = img.cxr_data.mipmap - lod = img.cxr_data.lod - clamp = img.cxr_data.clamp - - userflag_hash = F"{mipmap}.{lod}.{clamp}" - file_hash = F"{name}.{os.path.getmtime(src_path)}" - comphash = F"{file_hash}.{dims[0]}.{dims[1]}.{fmt}.{userflag_hash}" - - if img.cxr_data.last_hash != comphash: - print( F"Texture update: {img.filepath}" ) - - src = src_path.encode('utf-8') - dst = (asset_full_path('materials',img)+'.vtf').encode('utf-8') - - flags_full = flags - - # texture setting flags - if not lod: flags_full |= NBVTF_TEXTUREFLAGS_NOLOD - if clamp: - flags_full |= NBVTF_TEXTUREFLAGS_CLAMPS - flags_full |= NBVTF_TEXTUREFLAGS_CLAMPT - - if libnbvtf_convert.call(src,dims[0],dims[1],mipmap,fmt,0,flags_full,dst): - img.cxr_data.last_hash = comphash - - return name - -def compile_material(mat): - print( F"Compile {asset_full_path('materials',mat)}.vmt" ) - - info = material_info(mat) - properties = mat.cxr_data - - props = [] - - def _mlayer( layer ): - nonlocal properties, props - - for decl in layer: - if isinstance(layer[decl],dict): # $property definition - pdef = layer[decl] - ptype = pdef['type'] - - subdefines = False - default = None - prop = None - - if 'shaders' in pdef and properties.shader not in pdef['shaders']: - continue - - # Group expansion (does it have subdefinitions?) - for ch in pdef: - if isinstance(pdef[ch],dict): - subdefines = True - break - - expandview = False - - if ptype == 'ui': - expandview = True - else: - if ptype == 'intrinsic': - if decl in info: - prop = info[decl] - else: - prop = getattr(properties,decl) - default = pdef['default'] - - if not isinstance(prop,str) and \ - not isinstance(prop,bpy.types.Image) and \ - hasattr(prop,'__getitem__'): - prop = tuple([p for p in prop]) - - if prop != default: - # write prop - props += [(decl,pdef,prop)] - - if subdefines: - expandview = True - - if expandview: _mlayer(pdef) - - _mlayer( cxr_shader_params ) - - with vdf_structure( F"{asset_full_path('materials',mat)}.vmt" ) as vmt: - vmt.node( properties.shader ) - vmt.put( "// Convexer export\n" ) - - for pair in props: - decl = pair[0] - pdef = pair[1] - prop = pair[2] - - def _numeric(v): - nonlocal pdef - - if 'exponent' in pdef: return str(pow( v, pdef['exponent'] )) - else: return str(v) - - if isinstance(prop,bpy.types.Image): - flags = 0 - if 'flags' in pdef: - flags = pdef['flags'] - vmt.kv( decl,compile_image(prop,flags)) - - elif isinstance(prop,bool): - vmt.kv( decl, '1' if prop else '0' ) - elif isinstance(prop,str): - vmt.kv( decl, prop ) - elif isinstance(prop,float) or isinstance(prop,int): - vmt.kv( decl, _numeric(prop) ) - elif isinstance(prop,tuple): - vmt.kv( decl, F"[{' '.join([_numeric(_) for _ in prop])}]" ) - else: - vmt.put( F"// (cxr) unkown shader value type'{type(prop)}'" ) - - vmt.edon() class CXR_MATERIAL_PANEL(bpy.types.Panel): bl_label="VMT Properties" @@ -1546,6 +1657,9 @@ class CXR_LIGHT_PANEL(bpy.types.Panel): elif active_object.type == 'LIGHT_PROBE': layout.prop( properties, "size" ) +# Settings groups +# ------------------------------------------------------------------------------ + class CXR_IMAGE_SETTINGS(bpy.types.PropertyGroup): export_res: bpy.props.IntVectorProperty( name="", @@ -1572,6 +1686,7 @@ class CXR_IMAGE_SETTINGS(bpy.types.PropertyGroup): mipmap: bpy.props.BoolProperty(name="MIP",default=True) lod: bpy.props.BoolProperty(name="LOD",default=True) clamp: bpy.props.BoolProperty(name="CLAMP",default=False) + flags: bpy.props.IntProperty(name="flags",default=0) class CXR_LIGHT_SETTINGS(bpy.types.PropertyGroup): realtime: bpy.props.BoolProperty(name="Realtime Light", default=True) @@ -1640,32 +1755,21 @@ class CXR_SCENE_SETTINGS(bpy.types.PropertyGroup): default=True) lightmap_scale: bpy.props.IntProperty(name="Global Lightmap Scale",\ default=12) + image_quality: bpy.props.IntProperty(name="Texture Quality (0-18)",\ + default=8, min=0, max=18 ) -class CXR_DETECT_COMPILERS(bpy.types.Operator): - bl_idname="convexer.detect_compilers" - bl_label="Find compilers" - - def execute(self,context): - scene = context.scene - settings = scene.cxr_data - subdir = settings.subdir - - for exename in ['studiomdl','vbsp','vvis','vrad']: - searchpath = os.path.normpath(F'{subdir}/../bin/{exename}.exe') - if os.path.exists(searchpath): - settings[F'exe_{exename}'] = searchpath - - return {'FINISHED'} classes = [ CXR_RELOAD, CXR_DEV_OPERATOR, CXR_INTERFACE, \ - CXR_WRITE_VMF, CXR_MATERIAL_PANEL, CXR_IMAGE_SETTINGS,\ + CXR_MATERIAL_PANEL, CXR_IMAGE_SETTINGS,\ CXR_MODEL_SETTINGS, CXR_ENTITY_SETTINGS, CXR_CUBEMAP_SETTINGS,\ CXR_LIGHT_SETTINGS, CXR_SCENE_SETTINGS, CXR_DETECT_COMPILERS,\ CXR_ENTITY_PANEL, CXR_LIGHT_PANEL, CXR_PREVIEW_OPERATOR,\ - CXR_VIEW3D ] + CXR_VIEW3D, CXR_COMPILER_CHAIN ] + +vmt_param_dynamic_class = None def register(): - global debug_draw_handler, vmt_param_dynamic_class + global cxr_view_draw_handler, vmt_param_dynamic_class, cxr_ui_handler for c in classes: bpy.utils.register_class(c) @@ -1760,14 +1864,17 @@ def register(): # CXR Scene settings # GPU / callbacks - debug_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\ + cxr_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\ cxr_draw,(),'WINDOW','POST_VIEW') + cxr_ui_handler = bpy.types.SpaceView3D.draw_handler_add(\ + cxr_ui,(None,None),'WINDOW','POST_PIXEL') + bpy.app.handlers.load_post.append(cxr_on_load) bpy.app.handlers.depsgraph_update_post.append(cxr_dgraph_update) def unregister(): - global debug_draw_handler, vmt_param_dynamic_class + global cxr_view_draw_handler, vmt_param_dynamic_class, cxr_ui_handler bpy.utils.unregister_class( vmt_param_dynamic_class ) for c in classes: @@ -1776,4 +1883,5 @@ def unregister(): bpy.app.handlers.depsgraph_update_post.remove(cxr_dgraph_update) bpy.app.handlers.load_post.remove(cxr_on_load) - bpy.types.SpaceView3D.draw_handler_remove(debug_draw_handler,'WINDOW') + bpy.types.SpaceView3D.draw_handler_remove(cxr_view_draw_handler,'WINDOW') + bpy.types.SpaceView3D.draw_handler_remove(cxr_ui_handler,'WINDOW') diff --git a/config.py b/config.py new file mode 100644 index 0000000..b886758 --- /dev/null +++ b/config.py @@ -0,0 +1,254 @@ +# Convexer default configuration .py +# +# config dict purpose +# ------------------------------------------------------------------------------ +# cxr_shaders Defines shader names +# cxr_graph_mapping Contains a graph to traverse Blender shader nodes +# cxr_shader_params Material shader properties and groups +# cxr_entities Entity info + +# Shader names +# +cxr_shaders = \ +{ + "LightMappedGeneric": { "name": "Light Mapped" }, + "VertexLitGeneric": { "name": "Vertex Lit" }, + "UnlitGeneric": { "name": "Unlit" }, + "Builtin": { "name": "Builtin" } +} + +# Shader graph mapping +# ------------------------------------------------------------------------------ + +def material_tex_image(v): + return { + "ShaderNodeTexImage": + { + "image": F"${v}" + } + } + +cxr_graph_mapping = \ +{ + "ShaderNodeBsdfPrincipled": + { + "Base Color": + { + "ShaderNodeMixRGB": + { + "Color1": material_tex_image("basetexture"), + "Color2": material_tex_image("decaltexture") + }, + "ShaderNodeTexImage": + { + "image":"$basetexture" + }, + "default": + [("VertexLitGeneric","$color2"),\ + ("UnlitGeneric","$color2"),\ + ("LightMappedGeneric","$color")] + }, + "Normal": + { + "ShaderNodeNormalMap": + { + "Color": material_tex_image("bumpmap") + } + } + } +} + +# Shader keyvalues / material properties +# ------------------------------------------------------------------------------ + +cxr_shader_params = \ +{ + "Textures": + { + "type": "ui", + "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"), + + "$basetexture": + { + "name": "Base Texture", + "type": "intrinsic", + "default": None + }, + "$decaltexture": + { + "name": "Decal Texture", + "type": "intrinsic", + "default": None, + + "$decalblendmode": + { + "name": "Blend Mode", + "type": "enum", + "items": [ + ('0',"AlphaOver","Default",'',0), + ('1',"Multiply","",'',1), + ('2',"Modulate","",'',2), + ('3',"Additive","",'',3) + ], + "default": 0, + "always": True + } + }, + "$bumpmap": + { + "name": "Normal Map", + "type": "intrinsic", + "flags": NBVTF_TEXTUREFLAGS_NORMAL, # OpenGL (correct) normal maps. + "default": None + } + }, + "$color": + { + "name": "Color", + "type": "intrinsic", + "default": None, + "exponent": 2.2 + }, + "$color2": + { + "name": "Color2", + "type": "intrinsic", + "default": None, + "exponent": 2.2 + }, + "Lighting": + { + "type": "ui", + "shaders": ("VertexLitGeneric", "LightMappedGeneric"), + + "$phong": + { + "name": "Phong", + "type": "bool", + "default": False, + + "$phongexponent": + { + "name": "Exponent", + "type": "float", + "default": 5.0 + }, + "$phongboost": + { + "name": "Boost", + "type": "float", + "default": 1.0 + }, + "$phongfresnelranges": + { + "name": "Fresnel Ranges", + "type": "vector", + "default":(1.0,1.0,1.0) + } + }, + "$envmap": + { + "name": "Cubemap", + "type": "string", + "default": "", + + "$envmaptint": + { + "name": "Tint", + "type": "vector", + "subtype": 'COLOR', + "default": (1.0,1.0,1.0) + }, + "$envmaplightscale": + { + "name": "Light Scale", + "type": "float", + "default": 0.0 + }, + "$envmaplightscaleminmax": + { + "name": "Min/Max", + "type": "vector", + "default": (0.0,1.0) + } + } + }, + "Transparency": + { + "type": "ui", + "shaders": ("UnlitGeneric","VertexLitGeneric","LightMappedGeneric"), + + "$translucent": + { + "name": "Translucent", + "type": "bool", + "default": False + }, + "$alphatest": + { + "name": "Alpha Test", + "type": "bool", + "default": False, + + "$alphatestreference": + { + "name": "Step", + "type": "float", + "default": 0.5 + } + }, + "$nocull": + { + "name": "No Cull", + "type": "bool", + "default": False + } + } +} + +# Entity KV definitions +# Note: currently blender doesnt support enum or bool types for IDProperties, +# so unfortunately the panel UI is a little bit bad to work with. +# ------------------------------------------------------------------------------ + +cxr_entities = \ +{ + # Builtin/intrinsic entities, you probably dont want to modify these + "light": { "keyvalues": ent_lights }, + "light_spot": { "keyvalues": ent_lights }, + "env_cubemap": { "keyvalues": ent_cubemap }, + + # CSGO.fgd + # -------- + "info_player_counterterrorist": + { + "gizmo": [], + "allow": ('EMPTY',), + "keyvalues": cxr_baseclass([ent_transform],\ + { + "priority": {"type": "int", "default": 0 }, + "enabled": {"type": "int", "default": 1 }, + }) + }, + "info_player_terrorist": + { + "gizmo": [], + "allow": ('EMPTY',), + "keyvalues": cxr_baseclass([ent_transform],\ + { + "priority": {"type": "int", "default": 0 }, + "enabled": {"type": "int", "default": 1 }, + }) + }, + + # Brush entites + # ------------- + "func_buyzone": + { + "allow": ('MESH',), + "keyvalues": + { + "TeamNum": {"type": "int", "default": 0 } + } + } +} diff --git a/cxr/cxr.h b/cxr/cxr.h index bb904c4..518d197 100644 --- a/cxr/cxr.h +++ b/cxr/cxr.h @@ -45,7 +45,7 @@ IMPLEMENTATION */ -#define CXR_API +#define CXR_API #define CXR_EPSILON 0.001 #define CXR_PLANE_SIMILARITY_MAX 0.998 #define CXR_BIG_NUMBER 1e300 @@ -170,7 +170,7 @@ struct cxr_static_mesh struct cxr_material { i32 res[2]; - const char *name; + char *name; } *materials; @@ -489,7 +489,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_static_loop test_loops[] = {\n" ); + fprintf( fp, "cxr_static_loop test_loops[] = {\n" ); for( int i=0; iloop_count; i ++ ) { fprintf( fp, " {%d, %d},\n", @@ -498,7 +498,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_polygon test_polys[] = {\n" ); + fprintf( fp, "cxr_polygon test_polys[] = {\n" ); for( int i=0; i poly_count; i++ ) { fprintf( fp, " {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n", @@ -513,7 +513,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_edge test_edges[] = {\n" ); + fprintf( fp, "cxr_edge test_edges[] = {\n" ); for( int i=0; iedge_count; i++ ) { fprintf( fp, " {%d, %d, %d},\n", @@ -524,7 +524,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_static_mesh test_mesh = {\n" ); + fprintf( fp, "cxr_static_mesh test_mesh = {\n" ); fprintf( fp, " .vertices = test_verts,\n" ); fprintf( fp, " .loops = test_loops,\n" ); fprintf( fp, " .edges = test_edges,\n" ); @@ -2050,7 +2050,14 @@ CXR_API void cxr_free_world( cxr_world *world ) cxr_ab_free( &world->abverts ); cxr_ab_free( &world->absolids ); - free( world->materials ); + + if( world->materials ) + { + for( int i=0; imaterial_count; i++ ) + free( world->materials[i].name ); + + free( world->materials ); + } free( world ); } @@ -2176,6 +2183,13 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode ) size_t dsize = sizeof(cxr_material) * src->material_count; world->materials = malloc( dsize ); memcpy( world->materials, src->materials, dsize ); + + for( int i=0; imaterial_count; i++ ) + { + world->materials[i].name = malloc(strlen(src->materials[i].name) +1); + strcpy( world->materials[i].name, src->materials[i].name ); + } + world->material_count = src->material_count; } else world->materials = NULL; @@ -2726,7 +2740,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, * TODO(harry): Error checking is needed here for bad input data */ - int dispedge[16]; + int dispedge[17]; v2f corner_uvs[4]; int dispedge_count; int disp_count = 0; @@ -3011,7 +3025,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, tx = (double)k/(double)(5-1); v3_lerp( lside0, lside1, tx, lref ); v3_muls( verts[grid[index]], ctx->scale, vworld ); - v3_add( ctx->offset, vworld, ctx->offset ); + v3_add( ctx->offset, vworld, vworld ); v3_sub( vworld, lref, vdelta ); v3_copy( vdelta, normals[index] ); @@ -3031,7 +3045,6 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, world_corners[side[0]] ); cxr_vdf_kv( output, "material", matptr->name ); - cxr_vdf_kaxis( output, "uaxis", texinfo_shared.uaxis, texinfo_shared.offset[0], diff --git a/cxr/test.c b/cxr/test.c index 4529b98..b8d91d8 100644 --- a/cxr/test.c +++ b/cxr/test.c @@ -1,4 +1,7 @@ -#include "convexer.c" +#define CXR_IMPLEMENTATION +#define CXR_VALVE_MAP_FILE +#define CXR_DEBUG 1 +#include "cxr.h" #include "solid.h" int main(int arc, const char *argv[]) @@ -20,7 +23,7 @@ int main(int arc, const char *argv[]) .skyname = "vertigoblue" }; - cxr_world *world = cxr_decompose( &test_mesh ); + cxr_world *world = cxr_decompose( &test_mesh, NULL ); if( world ) { cxr_push_world_vmf( world, &ctx, vdo ); diff --git a/nbvtf/nbvtf.h b/nbvtf/nbvtf.h index c0071c9..e6318c0 100644 --- a/nbvtf/nbvtf.h +++ b/nbvtf/nbvtf.h @@ -33,7 +33,7 @@ // w, h - MAXIMUM image dimentions of final product. Set as 0 to be automatic // // version history: -// v1.03 - Added quality switch +// v1.03 - Added quality switch, moved init (major api revision) // v1.02 - Improved box filtering, small bug fixes // v1.01 - switch to OpenGL normal format for input // v1.00 - (hgn) first release @@ -450,17 +450,19 @@ uint32_t nbvtf_sizeimg( int w, int h, EImageFormat_t format ) return 0; } -void nbvtf_dxt_block( uint8_t *dest, uint8_t *src, int alpha, int qual ) +#ifdef NBVTF_AS_SO +__attribute__((visibility("default"))) +#endif +void nbvtf_init(void) { #ifdef USE_LIBRGBCX - // TODO: move this somewehre else - static int init = 0; - if( !init ) - { - rgbcx__init(); - init = 1; - } + rgbcx__init(); +#endif +} +void nbvtf_dxt_block( uint8_t *dest, uint8_t *src, int alpha, int qual ) +{ +#ifdef USE_LIBRGBCX if( alpha ) { rgbcx__encode_bc3( qual, dest, src ); -- 2.25.1