def open_collada_wizard(self): def cancel_import(): self.wizard.request_destroy() def confirm_import(): self.do_import = True self.wizard.request_destroy() self.wizard = SWindow( title='Collada Import Options', modal=True, sizing_rule=1)(SVerticalBox()( ue.create_detail_view(self.ImportOptions), auto_height=True, padding=10)( SHorizontalBox()(SButton( text='Cancel', on_clicked=cancel_import, h_align=EHorizontalAlignment.HAlign_Center))(SButton( text='Import', on_clicked=confirm_import, h_align=EHorizontalAlignment.HAlign_Center)), auto_height=True, padding=4, ), ) self.wizard.add_modal()
def show(): #Data saveDir = r"\\vietsap002\projects\R6\04_WIP\tools\perforce\proxyCheck" rootDir = ue.get_content_dir() matchType = compile(r'\.uasset\Z') allstaticmeshes = ue.get_assets_by_class("StaticMesh") uassetfolders = ((root,files) for root, subdirs, files in os.walk(rootDir) if any(matchType.search(file) for file in files)) filterstaticmeshfolder = ((folder, list( staticmesh for staticmesh in allstaticmeshes if any(match("^{}".format(staticmesh.get_name()), ufile) for ufile in ufiles ))) for folder, ufiles in uassetfolders) staticmeshfolder = ((folder, files) for folder, files in filterstaticmeshfolder if files) #UI window = SWindow().resize(512,1024).set_title("Export Static Mesh") vertical = SVerticalBox() #style = ButtonStyle(Normal=SlateBrush(TintColor=SlateColor(SpecifiedColor=FLinearColor(1, 0, 0)))) #picker = SFilePathPicker(browse_title='Export To', browse_button_style=style, on_path_picked=path_picked) button = SButton().set_content(STextBlock().set_text("Export All").set_v_align(2) vertical.add_slot(button, v_align=0, h_align=0) vertical.add_slot(STextBlock().set_text("Export Folders:"), v_align=0, h_align=0) vertical.add_slot(picker, v_align=0, h_align=0) vertical.add_slot(STextBlock().set_text("Static Mesh Folders:"), v_align=0, h_align=0) for folder, ufiles in staticmeshfolder: button = SButton().set_content(STextBlock().set_text(os.path.relpath(folder, rootDir))).set_v_align(2) button.bind_on_clicked(partial(export, ufiles, saveDir)) #vertical.add_slot(button, v_align=2, h_align=2) vertical.add_slot(button, v_align=0, h_align=0) window.set_content(vertical) #window.set_modal(True) show()
def split_hips(self, animation, bone='Hips'): self.choosen_skeleton = None # first ask for which skeleton to use: self.window = SWindow(title='Choose your new Skeleton', modal=True, sizing_rule=1)( SObjectPropertyEntryBox(allowed_class=Skeleton, on_object_changed=self.set_skeleton) ) self.window.add_modal() if not self.choosen_skeleton: raise DialogException('Please specify a Skeleton for retargeting') factory = AnimSequenceFactory() factory.TargetSkeleton = self.choosen_skeleton base_path = animation.get_path_name() package_name = ue.get_path(base_path) object_name = ue.get_base_filename(base_path) new_anim = factory.factory_create_new(package_name + '/' + object_name + '_rooted') new_anim.NumFrames = animation.NumFrames new_anim.SequenceLength = animation.SequenceLength # first step is generatin the 'root' track # we need to do it before anything else, as the 'root' track must be the 0 one for index, name in enumerate(animation.AnimationTrackNames): if name == bone: data = animation.get_raw_animation_track(index) # extract root motion root_motion = [(position - data.pos_keys[0]) for position in data.pos_keys] # create a new track (the root motion one) root_data = FRawAnimSequenceTrack() root_data.pos_keys = root_motion # ensure empty rotations ! root_data.rot_keys = [FQuat()] # add the track new_anim.add_new_raw_track('root', root_data) break else: raise DialogException('Unable to find bone {0}'.format(bone)) # now append the original tracks, but removes the position keys # from the original root bone for index, name in enumerate(animation.AnimationTrackNames): data = animation.get_raw_animation_track(index) if name == bone: # remove root motion from original track data.pos_keys = [data.pos_keys[0]] new_anim.add_new_raw_track(name, data) else: new_anim.add_new_raw_track(name, data) new_anim.save_package()
def show(): rootDir = ue.get_content_dir() matchType = compile(r'\.uasset\Z') folders = (root for root, subdirs, files in os.walk(rootDir) if any(matchType.search(file) for file in files)) window = SWindow().resize(512,1024).set_title("Export Static Mesh") vertical = SVerticalBox() vertical.add_slot(STextBlock().set_text("Export Folders:"), v_align=2, h_align=0) for folder in folders: button = SButton().set_content(STextBlock().set_text(os.path.relpath(folder, rootDir))).set_v_align(2) #vertical.add_slot(button, v_align=2, h_align=2) vertical.add_slot(button, v_align=0, h_align=0) window.set_content(vertical) #window.set_modal(True)
def open_collada_wizard(self): def cancel_import(): self.wizard.request_destroy() def confirm_import(): self.do_import = True self.wizard.request_destroy() self.wizard = SWindow(title='Collada Import Options', modal=True, sizing_rule=1)( SVerticalBox() ( ue.create_detail_view(self.ImportOptions), auto_height=True, padding = 10 ) ( SHorizontalBox() ( SButton(text='Cancel', on_clicked=cancel_import, h_align = EHorizontalAlignment.HAlign_Center) ) ( SButton(text='Import', on_clicked=confirm_import, h_align = EHorizontalAlignment.HAlign_Center) ), auto_height=True, padding = 4, ), ) self.wizard.add_modal()
SWindow(client_size=(1024, 576), title='DynamicComboBox')\ ( SBorder(color_and_opacity=FLinearColor(0, 1, 0, 1), border_background_color=SlateColor(SpecifiedColor=FLinearColor(1, 0, 0, 1))) ( SBox(h_align=EHorizontalAlignment.HAlign_Center, v_align=EVerticalAlignment.VAlign_Center) ( SBorder(color_and_opacity=FLinearColor(0, 1, 0, 1), border_background_color=SlateColor(SpecifiedColor=FLinearColor(0, 1, 0, 1))) ( SVerticalBox() ( STextBlock(text='Hello 1', tool_tip_text='Test Tool Tip') ) ( STextBlock(text='Hello 2') ) ( dynamic_combo_box.get_widget() ) ( SHorizontalBox() ( SEditableTextBox(text=lambda: '', on_text_committed=dynamic_combo_box.append), fill_width=0.8 ) ( SButton(text='Ok'), fill_width=0.2 ) ) ) ) ) )
import unreal_engine as ue from unreal_engine import SFilePathPicker, SWindow, FLinearColor from unreal_engine.structs import ButtonStyle, SlateBrush, SlateColor # a style is required for the file picker style = ButtonStyle(Normal=SlateBrush(TintColor=SlateColor( SpecifiedColor=FLinearColor(1, 0, 0)))) window = SWindow(client_size=(576, 576), title='Hello', modal=True) def path_picked(path): print(path) window.request_destroy() picker = SFilePathPicker(browse_title='Hello', browse_button_style=style, on_path_picked=path_picked) window.set_content(picker) window.add_modal()
def __init__(self): self.window = SWindow(client_size=(680, 530), title='Resource List Check', sizing_rule=0) self.hori_0 = SHorizontalBox() self.hori_0_0 = SHorizontalBox() self.hori_1_0 = SHorizontalBox() self.hori_2 = SHorizontalBox() self.hori_2_0 = SHorizontalBox() #self.hori_3_0 = SHorizontalBox() self.vert_0 = SVerticalBox() #self.vert_2 = SVerticalBox() self.main_vertical = SVerticalBox() self.scroll = SScrollBox(orientation=EOrientation.Orient_Vertical) self.chk_grid = SGridPanel() top_vertical = (0, 0, 0, 0) mid_vertical = (0, -400, 0, -390) bot_vertical = (0, 390, 0, 0) #zero_padding = (0,0,0,0) self.newindex = [] self.newint = [] self.txt_01 = STextBlock(text='엑셀 테이블 경로 : ') self.etb = SEditableTextBox().assign('table_path') self.hori_0.add_slot(self.txt_01, fill_width=0.1) self.hori_0.add_slot(self.etb, fill_width=0.4) self.hori_0.add_slot(SButton(text='...', fill_width=0.1, on_clicked=lambda: self.fn_sp()), auto_width=True) self.hori_0.add_slot(SButton(text='@', fill_width=0.1, on_clicked=lambda: self.fn_test()), auto_width=True) self.hori_0_0.add_slot(SBorder()(self.hori_0), padding=top_vertical, v_align=EVerticalAlignment.VAlign_Top) self.hori_1_0.add_slot(SBorder()(self.vert_0), padding=mid_vertical, v_align=EVerticalAlignment.VAlign_Fill) self.hori_2.add_slot( SButton(text='선택리스트 저장-TXT', h_align=2, v_align=2, fill_width=0.1, on_clicked=lambda: fn_sel())) self.hori_2.add_slot( SButton(text='저장리스트 비교-TXT', h_align=2, v_align=2, fill_width=0.1, on_clicked=lambda: fn_readtext())) self.hori_2.add_slot(STextBlock(text=' 매칭 리스트 정렬 '), auto_width=True, v_align=EVerticalAlignment.VAlign_Center) self.hori_2.add_slot(sort_bool, auto_width=True) self.hori_2.add_slot(STextBlock(text=' 리스트에 없는 에셋만 표시 '), auto_width=True, v_align=EVerticalAlignment.VAlign_Center) self.hori_2.add_slot(checkbox_bool, auto_width=True) self.hori_2_0.add_slot(SBorder()(self.hori_2), padding=bot_vertical, v_align=EVerticalAlignment.VAlign_Fill) # 초기설정파일 셋팅 if fn_OpenDefaultSetting() != None: table_path.set_text(fn_OpenDefaultSetting()) #테이블 리스트 함수 - 설정 경로 바탕으로 리스트를 작성 합니다 if self.fn_resettable() != None: self.fn_resettable() else: ue.log('설정 파일이 없습니다') self.main_vertical.add_slot(self.hori_0_0) self.main_vertical.add_slot(SBorder()(self.scroll), padding=mid_vertical, v_align=EVerticalAlignment.VAlign_Fill) self.main_vertical.add_slot(self.hori_2_0) self.window.set_content(SBorder()(self.main_vertical))
class SetWindow: def __init__(self): self.window = SWindow(client_size=(680, 530), title='Resource List Check', sizing_rule=0) self.hori_0 = SHorizontalBox() self.hori_0_0 = SHorizontalBox() self.hori_1_0 = SHorizontalBox() self.hori_2 = SHorizontalBox() self.hori_2_0 = SHorizontalBox() #self.hori_3_0 = SHorizontalBox() self.vert_0 = SVerticalBox() #self.vert_2 = SVerticalBox() self.main_vertical = SVerticalBox() self.scroll = SScrollBox(orientation=EOrientation.Orient_Vertical) self.chk_grid = SGridPanel() top_vertical = (0, 0, 0, 0) mid_vertical = (0, -400, 0, -390) bot_vertical = (0, 390, 0, 0) #zero_padding = (0,0,0,0) self.newindex = [] self.newint = [] self.txt_01 = STextBlock(text='엑셀 테이블 경로 : ') self.etb = SEditableTextBox().assign('table_path') self.hori_0.add_slot(self.txt_01, fill_width=0.1) self.hori_0.add_slot(self.etb, fill_width=0.4) self.hori_0.add_slot(SButton(text='...', fill_width=0.1, on_clicked=lambda: self.fn_sp()), auto_width=True) self.hori_0.add_slot(SButton(text='@', fill_width=0.1, on_clicked=lambda: self.fn_test()), auto_width=True) self.hori_0_0.add_slot(SBorder()(self.hori_0), padding=top_vertical, v_align=EVerticalAlignment.VAlign_Top) self.hori_1_0.add_slot(SBorder()(self.vert_0), padding=mid_vertical, v_align=EVerticalAlignment.VAlign_Fill) self.hori_2.add_slot( SButton(text='선택리스트 저장-TXT', h_align=2, v_align=2, fill_width=0.1, on_clicked=lambda: fn_sel())) self.hori_2.add_slot( SButton(text='저장리스트 비교-TXT', h_align=2, v_align=2, fill_width=0.1, on_clicked=lambda: fn_readtext())) self.hori_2.add_slot(STextBlock(text=' 매칭 리스트 정렬 '), auto_width=True, v_align=EVerticalAlignment.VAlign_Center) self.hori_2.add_slot(sort_bool, auto_width=True) self.hori_2.add_slot(STextBlock(text=' 리스트에 없는 에셋만 표시 '), auto_width=True, v_align=EVerticalAlignment.VAlign_Center) self.hori_2.add_slot(checkbox_bool, auto_width=True) self.hori_2_0.add_slot(SBorder()(self.hori_2), padding=bot_vertical, v_align=EVerticalAlignment.VAlign_Fill) # 초기설정파일 셋팅 if fn_OpenDefaultSetting() != None: table_path.set_text(fn_OpenDefaultSetting()) #테이블 리스트 함수 - 설정 경로 바탕으로 리스트를 작성 합니다 if self.fn_resettable() != None: self.fn_resettable() else: ue.log('설정 파일이 없습니다') self.main_vertical.add_slot(self.hori_0_0) self.main_vertical.add_slot(SBorder()(self.scroll), padding=mid_vertical, v_align=EVerticalAlignment.VAlign_Fill) self.main_vertical.add_slot(self.hori_2_0) self.window.set_content(SBorder()(self.main_vertical)) # 테이블 버튼 리스트 생성 def fn_resettable(self): table_padding = (10, 5, 10, 5) btn_padding = (4, 4, 4, 4) # 기본 리스트 설정 self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=ctext.tableHeader_01)), column=0, row=0) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=ctext.listHeader_01)), column=1, row=0) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=ctext.listHeader_02)), column=2, row=0) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=ctext.listHeader_03)), column=3, row=0) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=ctext.listHeader_04)), column=4, row=0) # 테이블에서 엑셀 경로 가져오기 listxx = fn_Dafultxlsx(table_path.get_text()) if listxx == None: return None #ue.log(listxx) # 리스트 생성 for i in range(0, len(listxx)): bntlists.append(SetButtontlist(i)) i = 1 x = 0 for ttable in listxx: d = i name = ttable[0] #'엑셀파일 이름' sname = ttable[1] #'시트 이름' cname = ttable[2] #'컬럼 이름' uesasset = ttable[3] #'리소스 경로' self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)( SEditableTextBox(text=str(i))), column=0, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=name)), column=1, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=sname)), column=2, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=cname)), column=3, row=i) # 버튼 리스트 ADD self.chk_grid.add_slot(SBorder(padding=btn_padding, v_align=2)(bntlists[i - 1].set_bnt), column=4, row=i) i += 1 self.scroll.add_slot(self.chk_grid, auto_width=True, auto_hight=True) def fn_test(self): ue.log('설정테스트') # 테이블 경로 - 가져오기 def fn_sp(self): try: st = fn_SaveDefaultSetting() table_path.set_text(st) if self.fn_resettable() != None: self.fn_resettable() except: ue.log('엑셀 기본 경로 설정을 취소 하였습니다') # 엑셀 테이블 읽기 위한 필요한 정보 (삭제 해야함 def fn_tt(self, path, tint): datacolumn = fn_Dafultxlsx(path) #ue.log(len(datacolumn)) #ue.log(tint) filepath = path + '/' + datacolumn[tint][0] + '.xlsx' sheetname = datacolumn[tint][1] findcolumn = datacolumn[tint][2] addstring = datacolumn[tint][3] fn_Loadxlsx(filepath, sheetname, findcolumn, addstring)
class RootMotionFixer: def add_root_to_skeleton(self, mesh, bone='root'): base_path = mesh.get_path_name() new_path = ue.create_modal_save_asset_dialog('Choose destination path', ue.get_path(base_path), ue.get_base_filename(base_path) + '_rooted') if not new_path: raise DialogException('Please specify a new path for the Skeletal Mesh copy') package_name = ue.object_path_to_package_name(new_path) object_name = ue.get_base_filename(new_path) # the last True allows for overwrites new_mesh = mesh.duplicate(package_name, object_name, True) # generate a new skeleton new_skel = self.build_new_skeleton(mesh.Skeleton, object_name + '_Skeleton', bone) # save the new skeleton in the same package directory of the new skeletal mesh new_skel.save_package(package_name) # assign the new skeleton to the new mesh new_mesh.skeletal_mesh_set_skeleton(new_skel) new_skel.save_package() self.fix_bones_influences(new_mesh, mesh.Skeleton) new_mesh.save_package() def build_new_skeleton(self, skeleton, name, root): new_skel = Skeleton(name) new_skel.skeleton_add_bone(root, -1, FTransform()) for index in range(0, skeleton.skeleton_bones_get_num()): bone_name = skeleton.skeleton_get_bone_name(index) bone_parent = skeleton.skeleton_get_parent_index(index) bone_transform = skeleton.skeleton_get_ref_bone_pose(index) if bone_parent == -1: bone_parent_name = root else: bone_parent_name = skeleton.skeleton_get_bone_name(bone_parent) new_bone_parent = new_skel.skeleton_find_bone_index(bone_parent_name) new_skel.skeleton_add_bone(bone_name, new_bone_parent, bone_transform) return new_skel def get_updated_bone_index(self, old_skeleton, new_skeleton, old_bone_map, new_bone_map, index): # get the skeleton bone_id from the map true_bone_id = old_bone_map[index] # get the bone name bone_name = old_skeleton.skeleton_get_bone_name(true_bone_id) # get the new index new_bone_id = new_skeleton.skeleton_find_bone_index(bone_name) # check if a new mapping is available if new_bone_id in new_bone_map: return new_bone_map.index(new_bone_id) new_bone_map.append(new_bone_id) return len(new_bone_map)-1 def fix_bones_influences(self, mesh, old_skeleton): active_bones = [] for section in range(0, mesh.skeletal_mesh_sections_num()): vertices = mesh.skeletal_mesh_get_soft_vertices(0, section) ue.log_warning(len(vertices)) new_vertices = [] old_bone_map = mesh.skeletal_mesh_get_bone_map(0, section) new_bone_map = [] for vertex in vertices: bone_ids = list(vertex.influence_bones) for index, bone_id in enumerate(bone_ids): if vertex.influence_weights[index] > 0: bone_ids[index] = self.get_updated_bone_index(old_skeleton, mesh.Skeleton, old_bone_map, new_bone_map, bone_id) if new_bone_map[bone_ids[index]] not in active_bones: active_bones.append(new_bone_map[bone_ids[index]]) vertex.influence_bones = bone_ids new_vertices.append(vertex) # assign new vertices mesh.skeletal_mesh_set_soft_vertices(new_vertices, 0, section) # add the new bone mapping mesh.skeletal_mesh_set_bone_map(new_bone_map, 0, section) # specify which bones are active and required (ensure root is added to required bones) mesh.skeletal_mesh_set_active_bone_indices(active_bones) # mark all the bones as required (eventually you can be more selective) mesh.skeletal_mesh_set_required_bones(list(range(0, mesh.Skeleton.skeleton_bones_get_num()))) def set_skeleton(self, asset_data): self.choosen_skeleton = asset_data.get_asset() self.window.request_destroy() def split_hips(self, animation, bone='Hips'): self.choosen_skeleton = None # first ask for which skeleton to use: self.window = SWindow(title='Choose your new Skeleton', modal=True, sizing_rule=1)( SObjectPropertyEntryBox(allowed_class=Skeleton, on_object_changed=self.set_skeleton) ) self.window.add_modal() if not self.choosen_skeleton: raise DialogException('Please specify a Skeleton for retargeting') factory = AnimSequenceFactory() factory.TargetSkeleton = self.choosen_skeleton base_path = animation.get_path_name() package_name = ue.get_path(base_path) object_name = ue.get_base_filename(base_path) new_anim = factory.factory_create_new(package_name + '/' + object_name + '_rooted') new_anim.NumFrames = animation.NumFrames new_anim.SequenceLength = animation.SequenceLength for index, name in enumerate(animation.AnimationTrackNames): data = animation.get_raw_animation_track(index) if name == bone: # extract root motion root_motion = [position - data.pos_keys[0] for position in data.pos_keys] # remove root motion from original track data.pos_keys = [data.pos_keys[0]] new_anim.add_new_raw_track(name, data) # create a new track (the root motion one) root_data = FRawAnimSequenceTrack() root_data.pos_keys = root_motion # ensure empty rotations ! root_data.rot_keys = [FQuat()] # add the track new_anim.add_new_raw_track('root', root_data) else: new_anim.add_new_raw_track(name, data) new_anim.save_package()
class ColladaFactory(PyFactory): ImportOptions = ColladaImportOptions() def __init__(self): # inform the editor that this class is able to import assets self.bEditorImport = True # register the .dae extension as supported self.Formats = ['dae;Collada'] # set the UClass this UFactory will generate self.SupportedClass = StaticMesh def open_collada_wizard(self): def cancel_import(): self.wizard.request_destroy() def confirm_import(): self.do_import = True self.wizard.request_destroy() self.wizard = SWindow(title='Collada Import Options', modal=True, sizing_rule=1)( SVerticalBox() ( ue.create_detail_view(self.ImportOptions), auto_height=True, padding = 10 ) ( SHorizontalBox() ( SButton(text='Cancel', on_clicked=cancel_import, h_align = EHorizontalAlignment.HAlign_Center) ) ( SButton(text='Import', on_clicked=confirm_import, h_align = EHorizontalAlignment.HAlign_Center) ), auto_height=True, padding = 4, ), ) self.wizard.add_modal() # this functions starts with an uppercase letter, so it will be visible to the UE system # not required obviously, but it will be a good example def FixMeshData(self): # move from collada system (y on top) to ue4 one (z on top, forward decreases over viewer) for i in range(0, len(self.vertices), 3): xv, yv, zv = self.vertices[i], self.vertices[i+1], self.vertices[i+2] # invert forward vec = FVector(zv * -1, xv, yv) * self.ImportOptions.DefaultRotation self.vertices[i] = vec.x self.vertices[i+1] = vec.y self.vertices[i+2] = vec.z xn, yn, zn = self.normals[i], self.normals[i+1], self.normals[i+2] nor = FVector(zn * -1, xn, yn) * self.ImportOptions.DefaultRotation # invert forward self.normals[i] = nor.x self.normals[i+1] = nor.y self.normals[i+2] = nor.z # fix uvs from 0 on bottom to 0 on top for i, uv in enumerate(self.uvs): if i % 2 != 0: self.uvs[i] = 1 - uv def PyFactoryCreateFile(self, uclass: Class, parent: Object, name: str, filename: str) -> Object: # load the collada file dae = Collada(filename) ue.log_warning(dae) self.do_import = False self.open_collada_wizard() if not self.do_import: return None # create a new UStaticMesh with the specified name and parent static_mesh = StaticMesh(name, parent) # prepare a new model with the specified build settings source_model = StaticMeshSourceModel(BuildSettings=MeshBuildSettings(bRecomputeNormals=False, bRecomputeTangents=True, bUseMikkTSpace=True, bBuildAdjacencyBuffer=True, bRemoveDegenerates=True)) # extract vertices, uvs and normals from the da file (numpy.ravel will flatten the arrays to simple array of floats) triset = dae.geometries[0].primitives[0] self.vertices = numpy.ravel(triset.vertex[triset.vertex_index]) # take the first uv channel (there could be multiple channels, like the one for lightmapping) self.uvs = numpy.ravel(triset.texcoordset[0][triset.texcoord_indexset[0]]) self.normals = numpy.ravel(triset.normal[triset.normal_index]) # fix mesh data self.FixMeshData() # create a new mesh, FRawMesh is an ptopmized wrapper exposed by the python plugin. read: no reflection involved mesh = FRawMesh() # assign vertices mesh.set_vertex_positions(self.vertices) # uvs are required mesh.set_wedge_tex_coords(self.uvs) # normals are optionals mesh.set_wedge_tangent_z(self.normals) # assign indices (not optimized, just return the list of triangles * 3...) mesh.set_wedge_indices(numpy.arange(0, len(triset) * 3)) # assign the FRawMesh to the LOD0 (the model we created before) mesh.save_to_static_mesh_source_model(source_model) # assign LOD0 to the SataticMesh and build it static_mesh.SourceModels = [source_model] static_mesh.static_mesh_build() static_mesh.static_mesh_create_body_setup() static_mesh.StaticMaterials = [StaticMaterial(MaterialInterface=self.ImportOptions.DefaultMaterial, MaterialSlotName='Main')] return static_mesh
def __init__(self, text, textlist, tablename): tablenametext = tablename self.window = SWindow(client_size=(1024, 600), title='리소스 검색 결과 창' + tablename) self.vertical = SVerticalBox() self.horizon = SHorizontalBox() self.scroll = SScrollBox(orientation=EOrientation.Orient_Vertical) self.chk_grid = SGridPanel() table_padding = (10, 5, 10, 5) btn_padding = (4, 4, 4, 4) savepath = '' savelist = [] self.chk_grid.add_slot(SBorder(padding=btn_padding, v_align=2)(SButton( text='현재 리스트 TXT 파일로 저장', on_clicked=lambda: SaveText(textlist, tablenametext))), column=0, row=0, column_span=2) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=text.tableHeader_01)), column=0, row=1) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=text.tableHeader_02)), column=1, row=1) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=text.tableHeader_05)), column=2, row=1) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=text.tableHeader_04)), column=3, row=1) self.chk_grid.add_slot( SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=text.tableHeader_03)), column=4, row=1) #두번째 row 부터 리스트를 작성 한다. i = 2 for ttable in textlist: #range(1, 120): d = i color_val = color_gray stringsplit = ttable.split(',') name = stringsplit[0] path = stringsplit[1] assetname = stringsplit[2] uesasset = stringsplit[3] if '없음' == uesasset: color_val = color_red self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=str(i - 1))), column=0, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock(text=name)), column=1, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)(STextBlock( text=uesasset, color_and_opacity=color_val)), column=2, row=i) self.chk_grid.add_slot(SBorder(padding=table_padding, h_align=2, v_align=2)( STextBlock(text=assetname)), column=3, row=i) self.chk_grid.add_slot(SBorder(padding=btn_padding, v_align=2)( SEditableTextBox(text=path)), column=4, row=i) i += 1 self.scroll.add_slot(self.chk_grid, auto_width=True, auto_hight=True) self.vertical.add_slot(SBorder()(self.scroll), auto_hight=True, padding=5) self.window.set_content(self.vertical)
from unreal_engine.classes import Blueprint def clicked(geometry, pointer_event): ue.log('Hello') ue.log(geometry.fields()) ue.log(pointer_event.fields()) return True def clicked2(): ue.log('Test') return True window = SWindow().resize(1024, 576).set_title('Hello World') horizontal = SHorizontalBox() button = SButton().set_content(STextBlock().set_text('Click ME !')) button.bind_on_mouse_button_down(clicked) button.bind_on_clicked(clicked2) box = SEditableTextBox() editor = SMultiLineEditableText() editor.set_text('Hello') grid = SGridPanel() grid.add_slot(button, 0, 0)
from unreal_engine import SWindow, SButton, SHorizontalBox, FARFilter # 한글 문자열 수집을 위해 필요함 import sys reload(sys) sys.setdefaultencoding('utf-8') #윈도우 하나만 사용 하자!!! // Error가 있음 #if window != None: # window.request_destroy() window = SWindow(client_size=(330, 32), title='Widget Blueprint To Text', sizing_rule=0)( SHorizontalBox()(SButton( text='전체 WBP 추출', on_clicked=lambda: WBPall()))( # 함수에 연결된 버튼 SButton(text='선택한 WBP 추출', on_clicked=lambda: WBPsel()))(SButton( text='WBP텍스트 지우기', on_clicked=lambda: Wtestxx()))) # 테스트설정 def Wtestxx(): selbp = ue.get_selected_assets() for aa in selbp: aa.save_package() uw = aa.WidgetTree allwid = uw.AllWidgets for wb in allwid: if wb.get_class().get_name() == 'TextBlock':
import unreal_engine as ue from unreal_engine import SWindow, SButton, STextBlock, SEditableTextBox, SMultiLineEditableText, SGridPanel, SHorizontalBox, SPythonEditorViewport from unreal_engine.classes import Blueprint def clicked(geometry, pointer_event): ue.log('Hello') ue.log(geometry.fields()) ue.log(pointer_event.fields()) return True def clicked2(): ue.log('Test') return True window = SWindow().resize(1024, 576).set_title('Hello World') horizontal = SHorizontalBox() button = SButton().set_content(STextBlock().set_text('Click ME !')) button.bind_on_mouse_button_down(clicked) button.bind_on_clicked(clicked2) box = SEditableTextBox() editor = SMultiLineEditableText() editor.set_text('Hello') grid = SGridPanel() grid.add_slot(button, 0, 0)
from unreal_engine import SWindow, SProgressBar, SButton, SVerticalBox import unreal_engine as ue progress_bar = SProgressBar() canceled = False def cancel_operation(): global canceled ue.log_warning('slow operation canceled') canceled = True SWindow(title='slow task', sizing_rule=1)(SVerticalBox()(progress_bar)(SButton( text='cancel', on_clicked=cancel_operation))) for i in range(0, 10000): progress_bar.set_percent(1 / 10000 * i) ue.log('slow task iteration: {0}'.format(i)) if canceled: break ue.slate_tick()
import unreal_engine as ue from unreal_engine import SFilePathPicker, SWindow, FLinearColor from unreal_engine.structs import ButtonStyle, SlateBrush, SlateColor # a style is required for the file picker style = ButtonStyle(Normal=SlateBrush(TintColor=SlateColor(SpecifiedColor=FLinearColor(1, 0, 0)))) window = SWindow(client_size=(576,576), title='Hello', modal=True) def path_picked(path): print(path) window.request_destroy() picker = SFilePathPicker(browse_title='Hello', browse_button_style=style, on_path_picked=path_picked) window.set_content(picker) window.add_modal()
import unreal_engine as ue from unreal_engine.classes import StaticMesh from unreal_engine import SWindow, SScrollBox, SVerticalBox, SHorizontalBox, SImage, STextBlock, FARFilter from unreal_engine.enums import EPixelFormat _filter = FARFilter() _filter.class_names = ['StaticMesh'] thumbnails = [] for static_mesh in ue.get_assets_by_filter(_filter): thumbnails.append((static_mesh.get_full_name(), static_mesh.get_thumbnail())) window = SWindow(client_size=(1024, 512), title='StaticMeshes thumbnails') scroll_box = SScrollBox() vertical = SVerticalBox() for name, thumbnail in thumbnails: texture = ue.create_transient_texture(thumbnail.get_image_width(), thumbnail.get_image_height()) texture.texture_set_data(thumbnail.get_uncompressed_image_data()) vertical.add_slot( SHorizontalBox() ( SImage().set_texture(texture), max_width=64 ) ( STextBlock(text=name), padding=4,
class ColladaFactory(PyFactory): ImportOptions = ColladaImportOptions() def __init__(self): # inform the editor that this class is able to import assets self.bEditorImport = True # register the .dae extension as supported self.Formats = ['dae;Collada'] # set the UClass this UFactory will generate self.SupportedClass = StaticMesh def open_collada_wizard(self): def cancel_import(): self.wizard.request_destroy() def confirm_import(): self.do_import = True self.wizard.request_destroy() self.wizard = SWindow( title='Collada Import Options', modal=True, sizing_rule=1)(SVerticalBox()( ue.create_detail_view(self.ImportOptions), auto_height=True, padding=10)( SHorizontalBox()(SButton( text='Cancel', on_clicked=cancel_import, h_align=EHorizontalAlignment.HAlign_Center))(SButton( text='Import', on_clicked=confirm_import, h_align=EHorizontalAlignment.HAlign_Center)), auto_height=True, padding=4, ), ) self.wizard.add_modal() # this functions starts with an uppercase letter, so it will be visible to the UE system # not required obviously, but it will be a good example def FixMeshData(self): # move from collada system (y on top) to ue4 one (z on top, forward decreases over viewer) for i in range(0, len(self.vertices), 3): xv, yv, zv = self.vertices[i], self.vertices[i + 1], self.vertices[i + 2] # invert forward vec = FVector(zv * -1, xv, yv) * self.ImportOptions.DefaultRotation self.vertices[i] = vec.x self.vertices[i + 1] = vec.y self.vertices[i + 2] = vec.z xn, yn, zn = self.normals[i], self.normals[i + 1], self.normals[i + 2] nor = FVector(zn * -1, xn, yn) * self.ImportOptions.DefaultRotation # invert forward self.normals[i] = nor.x self.normals[i + 1] = nor.y self.normals[i + 2] = nor.z # fix uvs from 0 on bottom to 0 on top for i, uv in enumerate(self.uvs): if i % 2 != 0: self.uvs[i] = 1 - uv def PyFactoryCreateFile(self, uclass: Class, parent: Object, name: str, filename: str) -> Object: # load the collada file dae = Collada(filename) ue.log_warning(dae) self.do_import = False self.open_collada_wizard() if not self.do_import: return None # create a new UStaticMesh with the specified name and parent static_mesh = StaticMesh(name, parent) # prepare a new model with the specified build settings source_model = StaticMeshSourceModel( BuildSettings=MeshBuildSettings(bRecomputeNormals=False, bRecomputeTangents=True, bUseMikkTSpace=True, bBuildAdjacencyBuffer=True, bRemoveDegenerates=True)) # extract vertices, uvs and normals from the da file (numpy.ravel will flatten the arrays to simple array of floats) triset = dae.geometries[0].primitives[0] self.vertices = numpy.ravel(triset.vertex[triset.vertex_index]) # take the first uv channel (there could be multiple channels, like the one for lightmapping) self.uvs = numpy.ravel( triset.texcoordset[0][triset.texcoord_indexset[0]]) self.normals = numpy.ravel(triset.normal[triset.normal_index]) # fix mesh data self.FixMeshData() # create a new mesh, FRawMesh is an ptopmized wrapper exposed by the python plugin. read: no reflection involved mesh = FRawMesh() # assign vertices mesh.set_vertex_positions(self.vertices) # uvs are required mesh.set_wedge_tex_coords(self.uvs) # normals are optionals mesh.set_wedge_tangent_z(self.normals) # assign indices (not optimized, just return the list of triangles * 3...) mesh.set_wedge_indices(numpy.arange(0, len(triset) * 3)) # assign the FRawMesh to the LOD0 (the model we created before) mesh.save_to_static_mesh_source_model(source_model) # assign LOD0 to the SataticMesh and build it static_mesh.SourceModels = [source_model] static_mesh.static_mesh_build() static_mesh.static_mesh_create_body_setup() static_mesh.StaticMaterials = [ StaticMaterial( MaterialInterface=self.ImportOptions.DefaultMaterial, MaterialSlotName='Main') ] return static_mesh