+ def execute(_,context):
+ # Prepare input data
+ mesh_src = mesh_cxr_format(context.active_object)
+ libcxr_write_test_data.call( pointer(mesh_src) )
+ 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"
+
+ RUNNING = False
+
+ def execute(_,context):
+ global cxr_view_mesh
+ global cxr_view_shader, cxr_view_mesh, cxr_error_inf
+
+ cxr_reset_all()
+
+ static = _.__class__
+
+ mesh_src = mesh_cxr_format(context.active_object)
+ world = cxr_decompose_globalerr( mesh_src )
+
+ if world == None:
+ return {'FINISHED'}
+
+ # Generate preview using cxr
+ #
+ ptrpreview = libcxr_world_preview.call( world )
+ preview = ptrpreview[0]
+
+ vertices = preview.vertices[:preview.vertex_count]
+ vertices = [(_[0],_[1],_[2]) for _ in vertices]
+
+ colours = preview.colours[:preview.vertex_count]
+ colours = [(_[0],_[1],_[2],_[3]) for _ in colours]
+
+ indices = preview.indices[:preview.indices_count]
+ indices = [ (indices[i*3+0],indices[i*3+1],indices[i*3+2]) \
+ for i in range(int(preview.indices_count/3)) ]
+
+ 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 )
+ cxr_batch_lines()
+ scene_redraw()
+
+ return {'FINISHED'}
+
+# 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'}
+
+def cxr_compiler_path( compiler ):
+ settings = bpy.context.scene.cxr_data
+ subdir = settings.subdir
+ path = os.path.normpath(F'{subdir}/../bin/{compiler}.exe')
+
+ if os.path.exists( path ): return path
+ else: return None
+
+# Compatibility layer
+#
+def cxr_temp_file( fn ):
+ if CXR_GNU_LINUX == 1:
+ return F"/tmp/fn"
+ else:
+ filepath = bpy.data.filepath
+ directory = os.path.dirname(filepath)
+ return F"{directory}/{fn}.txt"
+
+def cxr_winepath( path ):
+ if CXR_GNU_LINUX == 1:
+ return 'z:'+path.replace('/','\\')
+ else:
+ return path
+
+# 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
+ FILE = None
+ LOG = []
+
+ JOBINFO = None
+ JOBID = 0
+ JOBSYS = None
+
+ def cancel(_,context):
+ #global cxr_jobs_batch
+ 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
+
+ static.FILE.close()
+
+ #cxr_jobs_batch = None
+ scene_redraw()
+ return {'FINISHED'}
+
+ def modal(_,context,ev):
+ static = _.__class__
+
+ if ev.type == 'TIMER':
+ global cxr_jobs_batch, cxr_error_inf
+
+ if static.WAIT_REDRAW:
+ scene_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
+ status = static.SUBPROC.poll()
+
+ # Cannot redirect STDOUT through here without causing
+ # undefined behaviour due to the Blender Python specification.
+ #
+ # Have to write it out to a file and read it back in.
+ #
+
+ with open(cxr_temp_file("convexer_compile_log.txt"),"r") as log:
+ static.LOG = log.readlines()
+ if status == None:
+ return {'PASS_THROUGH'}
+ else:
+ #for l in static.SUBPROC.stdout:
+ # print( F'-> {l.decode("utf-8")}',end='' )
+ static.SUBPROC = None
+
+ if status != 0:
+ print(F'Compiler () error: {status}')
+
+ jobn = static.JOBSYS['jobs'][static.JOBID]
+ cxr_error_inf = ( F"{static.JOBSYS['title']} error {status}", jobn )
+
+ return _.cancel(context)
+
+ static.JOBSYS['jobs'][static.JOBID] = None
+ cxr_jobs_update_graph( static.JOBINFO )
+ scene_redraw()
+ return {'PASS_THROUGH'}
+
+ # Compile syncronous thing
+ for sys in static.JOBINFO:
+ for i,target in enumerate(sys['jobs']):
+ if target != None:
+
+ if callable(sys['exec']):
+ print( F"Run (sync): {static.JOBID} @{time.time()}" )
+
+ if not sys['exec'](*target):
+ print( "Job failed" )
+ return _.cancel(context)
+
+ sys['jobs'][i] = None
+ static.JOBID += 1
+ else:
+ # Run external executable (wine)
+ static.SUBPROC = subprocess.Popen( target,
+ stdout=static.FILE,\
+ stderr=subprocess.PIPE,\
+ cwd=sys['cwd'])
+ static.JOBSYS = sys
+ static.JOBID = i
+
+ cxr_jobs_update_graph( static.JOBINFO )
+ scene_redraw()
+ return {'PASS_THROUGH'}
+
+ # All completed
+ print( "All jobs completed!" )
+ #cxr_jobs_batch = None
+ #scene_redraw()
+ return _.cancel(context)
+
+ return {'PASS_THROUGH'}