From fb7459dd595d8d7e135c8e39f8d40111f7d60b65 Mon Sep 17 00:00:00 2001 From: NicoMico Date: Mon, 18 Nov 2024 14:36:16 +0800 Subject: [PATCH] upgrade to Blender 4.2 LTS --- blender/README.md | 28 ++++++----- blender/README_EN.md | 15 ------ blender/__init__.py | 11 ++--- blender/rightclick_menu/mesh_functions.py | 20 -------- blender/rightclick_menu/mesh_operator.py | 39 ++------------- blender/unity/migoto_import.py | 59 +++++++---------------- blender/unreal/buffer_import.py | 1 - 7 files changed, 42 insertions(+), 131 deletions(-) delete mode 100644 blender/README_EN.md diff --git a/blender/README.md b/blender/README.md index 197a3c0..2678ea5 100644 --- a/blender/README.md +++ b/blender/README.md @@ -1,16 +1,20 @@ -# DBMT的Blender插件 -DirectX Buffer Mod Tool的Blender配套插件。 +# DBMT's Blender Plugin +The DirectX Buffer Mod Tool plugin for Blender. -# 安装步骤 -- 打包BlenderPlugins目录为zip文件。 -- 下载并安装Blender3.6 LTS版本。 -- 在 首选项=>插件 中安装打包的.zip文件 -- 打开DBMT面板,选择DBMT-GUI.exe所在路径完成插件初始化。 +# Installation Steps +- Pack the BlenderPlugins directory into a zip file. +- Download and install Blender version 4.2 LTS. +- In Preferences => Add-ons, install the packed .zip file. +- Open the DBMT panel, select the path where DBMT-GUI.exe is located to complete the plugin initialization. -# 开发环境 +# Development Environment - Visual Studio Code -- Python 3.9 -- Blender 3.6 LTS -- pip install fake-bpy-module-3.6 -- pip install numpy +- Python 3.12.7 or higher +- Blender 4.2 LTS +- `pip install fake-bpy-module-4.2` +- `pip install numpy` +# Notice +- fake-bpy-module-4.2 need at least Python 3.12 to work. +- VSCode is the best practice IDE in blender plugin development. +- Ctrl + Shift + P to start debug Blender. diff --git a/blender/README_EN.md b/blender/README_EN.md deleted file mode 100644 index 35ea795..0000000 --- a/blender/README_EN.md +++ /dev/null @@ -1,15 +0,0 @@ -# DBMT's Blender Plugin -The DirectX Buffer Mod Tool plugin for Blender. - -# Installation Steps -- Pack the BlenderPlugins directory into a zip file. -- Download and install Blender version 3.6 LTS. -- In Preferences => Add-ons, install the packed .zip file. -- Open the DBMT panel, select the path where DBMT-GUI.exe is located to complete the plugin initialization. - -# Development Environment -- Visual Studio Code -- Python 3.9 -- Blender 3.6 LTS -- `pip install fake-bpy-module-3.6` -- `pip install numpy` \ No newline at end of file diff --git a/blender/__init__.py b/blender/__init__.py index 24f2bb1..90d883f 100644 --- a/blender/__init__.py +++ b/blender/__init__.py @@ -18,14 +18,13 @@ # 插件基础信息 bl_info = { "name": "DBMT-Blender-Plugin", - "description": "DBMT的Blender插件 目前只支持3.6LTS版本", - "blender": (3, 6, 0), - "version": (1, 0, 2, 0), + "description": "DBMT的Blender插件", + "blender": (4, 2, 3), + "version": (1, 0, 2, 1), "location": "View3D", "category": "Generic" } - # 需要注册和取消注册的所有类 register_classes = ( # migoto @@ -58,9 +57,7 @@ MMTDeleteLoose, MMTResetRotation, MigotoRightClickMenu, - MMTCancelAutoSmooth, MMTShowIndexedVertices, - MMTSetAutoSmooth89, SplitMeshByCommonVertexGroup, RecalculateTANGENTWithVectorNormalizedNormal, RecalculateCOLORWithVectorNormalizedNormal, @@ -115,3 +112,5 @@ def unregister(): del bpy.types.Scene.mmt_mmd_animation_mod_play_speed +if __name__ == "__main__": + register() \ No newline at end of file diff --git a/blender/rightclick_menu/mesh_functions.py b/blender/rightclick_menu/mesh_functions.py index 21840de..99eb5a5 100644 --- a/blender/rightclick_menu/mesh_functions.py +++ b/blender/rightclick_menu/mesh_functions.py @@ -293,26 +293,6 @@ def mmt_reset_rotation(self, context): return {'FINISHED'} -def mmt_cancel_auto_smooth(self, context): - for obj in bpy.context.selected_objects: - if obj.type == "MESH": - # 取消勾选"Auto Smooth" - # TODO 4.1中移除了use_auto_smooth - obj.data.use_auto_smooth = False - return {'FINISHED'} - - -def mmt_set_auto_smooth_89(self, context): - for obj in bpy.context.selected_objects: - if obj.type == "MESH": - # 取消勾选"Auto Smooth" - # TODO 4.1中移除了use_auto_smooth - obj.data.use_auto_smooth = True - # TODO 4.1中移除了auto_smooth_angle - obj.data.auto_smooth_angle = math.radians(89) - return {'FINISHED'} - - def split_mesh_by_common_vertex_group(self, context): # Code copied and modified from @Kail_Nethunter, very useful in some special meets. # https://blenderartists.org/t/split-a-mesh-by-vertex-groups/438990/11 diff --git a/blender/rightclick_menu/mesh_operator.py b/blender/rightclick_menu/mesh_operator.py index 8a66d43..e4a7784 100644 --- a/blender/rightclick_menu/mesh_operator.py +++ b/blender/rightclick_menu/mesh_operator.py @@ -1,12 +1,12 @@ # Nico: 此文件定义右键菜单的功能类。 -# 所有操作都要加上bl_options = {'UNDO'},这样可以支持Ctrl + Z撤销。 +# 4.2版本开始,bl_options里再加任何东西都会出现 +# RuntimeError: Error: validating class:: 'REGISTER' not found in ('SEARCH_ON_KEY_PRESS') from .mesh_functions import * class RemoveUnusedVertexGroupOperator(bpy.types.Operator): bl_idname = "object.remove_unused_vertex_group" bl_label = "移除未使用的空顶点组" - bl_options = {'UNDO'} def execute(self, context): return remove_unused_vertex_group(self, context) @@ -15,7 +15,6 @@ def execute(self, context): class MergeVertexGroupsWithSameNumber(bpy.types.Operator): bl_idname = "object.merge_vertex_group_with_same_number" bl_label = "合并具有相同数字前缀名称的顶点组" - bl_options = {'UNDO'} def execute(self, context): return merge_vertex_group_with_same_number(self, context) @@ -24,7 +23,6 @@ def execute(self, context): class FillVertexGroupGaps(bpy.types.Operator): bl_idname = "object.fill_vertex_group_gaps" bl_label = "填充数字顶点组的间隙" - bl_options = {'UNDO'} def execute(self, context): return fill_vertex_group_gaps(self, context) @@ -33,7 +31,6 @@ def execute(self, context): class AddBoneFromVertexGroup(bpy.types.Operator): bl_idname = "object.add_bone_from_vertex_group" bl_label = "根据顶点组自动生成骨骼" - bl_options = {'UNDO'} def execute(self, context): return add_bone_from_vertex_group(self, context) @@ -42,7 +39,6 @@ def execute(self, context): class RemoveNotNumberVertexGroup(bpy.types.Operator): bl_idname = "object.remove_not_number_vertex_group" bl_label = "移除非数字名称的顶点组" - bl_options = {'UNDO'} def execute(self, context): return remove_not_number_vertex_group(self, context) @@ -51,7 +47,6 @@ def execute(self, context): class ConvertToFragmentOperator(bpy.types.Operator): bl_idname = "object.convert_to_fragment" bl_label = "转换为一个3Dmigoto碎片用于合并" - bl_options = {'UNDO'} def execute(self, context): return convert_to_fragment(self, context) @@ -60,7 +55,6 @@ def execute(self, context): class MMTDeleteLoose(bpy.types.Operator): bl_idname = "object.mmt_delete_loose" bl_label = "删除物体的松散点" - bl_options = {'UNDO'} def execute(self, context): return delete_loose(self, context) @@ -69,34 +63,15 @@ def execute(self, context): class MMTResetRotation(bpy.types.Operator): bl_idname = "object.mmt_reset_rotation" bl_label = "重置x,y,z的旋转角度为0 (UE Model)" - bl_options = {'UNDO'} def execute(self, context): return mmt_reset_rotation(self, context) -class MMTCancelAutoSmooth(bpy.types.Operator): - bl_idname = "object.mmt_cancel_auto_smooth" - bl_label = "取消自动平滑 (UE Model)" - bl_options = {'UNDO'} - - def execute(self, context): - return mmt_cancel_auto_smooth(self, context) - - -class MMTSetAutoSmooth89(bpy.types.Operator): - bl_idname = "object.mmt_set_auto_smooth_89" - bl_label = "设置Normal的自动平滑为89° (Unity)" - bl_options = {'UNDO'} - - def execute(self, context): - return mmt_set_auto_smooth_89(self, context) - class MMTShowIndexedVertices(bpy.types.Operator): bl_idname = "object.mmt_show_indexed_vertices" bl_label = "展示Indexed Vertices和Indexes Number" - bl_options = {'UNDO'} def execute(self, context): return show_indexed_vertices(self, context) @@ -105,7 +80,6 @@ def execute(self, context): class SplitMeshByCommonVertexGroup(bpy.types.Operator): bl_idname = "object.split_mesh_by_common_vertex_group" bl_label = "根据相同的顶点组分割物体" - bl_options = {'UNDO'} def execute(self, context): return split_mesh_by_common_vertex_group(self, context) @@ -114,7 +88,6 @@ def execute(self, context): class RecalculateTANGENTWithVectorNormalizedNormal(bpy.types.Operator): bl_idname = "object.recalculate_tangent_arithmetic_average_normal" bl_label = "使用向量相加归一化算法重计算TANGENT" - bl_options = {'UNDO'} def execute(self, context): for obj in bpy.context.selected_objects: @@ -130,7 +103,6 @@ def execute(self, context): class RecalculateCOLORWithVectorNormalizedNormal(bpy.types.Operator): bl_idname = "object.recalculate_color_arithmetic_average_normal" bl_label = "使用算术平均归一化算法重计算COLOR" - bl_options = {'UNDO'} def execute(self, context): for obj in bpy.context.selected_objects: @@ -147,7 +119,6 @@ def execute(self, context): class MigotoRightClickMenu(bpy.types.Menu): bl_idname = "VIEW3D_MT_object_3Dmigoto" bl_label = "3Dmigoto" - bl_options = {'UNDO'} def draw(self, context): layout = self.layout @@ -160,8 +131,6 @@ def draw(self, context): layout.operator(ConvertToFragmentOperator.bl_idname) layout.operator(MMTDeleteLoose.bl_idname) layout.operator(MMTResetRotation.bl_idname) - layout.operator(MMTCancelAutoSmooth.bl_idname) - layout.operator(MMTSetAutoSmooth89.bl_idname) layout.operator(MMTShowIndexedVertices.bl_idname) layout.operator(SplitMeshByCommonVertexGroup.bl_idname) layout.operator(RecalculateTANGENTWithVectorNormalizedNormal.bl_idname) @@ -173,14 +142,12 @@ def menu_func_migoto_right_click(self, context): self.layout.menu(MigotoRightClickMenu.bl_idname) - # -------------------------- # Right click menu for collection. class DBMT_MarkCollection_Switch(bpy.types.Operator): # bl_idname必须小写,中间可以下划线分割,不然报错无法加载 bl_idname = "object.mark_collection_switch" bl_label = "Mark Collection Switch" - bl_options = {'UNDO'} def execute(self, context): # 示例:打印当前选中的集合名称 @@ -188,11 +155,11 @@ def execute(self, context): context.collection.color_tag = "COLOR_03" return {'FINISHED'} + class DBMT_MarkCollection_Toggle(bpy.types.Operator): # bl_idname必须小写,中间可以下划线分割,不然报错无法加载 bl_idname = "object.mark_collection_toggle" bl_label = "Mark Collection Toggle" - bl_options = {'UNDO'} def execute(self, context): # 示例:打印当前选中的集合名称 diff --git a/blender/unity/migoto_import.py b/blender/unity/migoto_import.py index 800943f..8d746a7 100644 --- a/blender/unity/migoto_import.py +++ b/blender/unity/migoto_import.py @@ -34,8 +34,8 @@ def import_vertex_groups(mesh, obj, blend_indices, blend_weights): def import_uv_layers(mesh, obj, texcoords, flip_texcoord_v): for (texcoord, data) in sorted(texcoords.items()): ''' - TODO 这里他说的TEXCOORD可以有四个分量的原因是什么?是不是因为自定义数据写到TEXCOORD里的原因? - 如果是因为自定义数据,则自定义数据应该由其它模块处理 + Nico: 在我们的游戏Mod设计中,TEXCOORD只能有两个分量 + 如果出现两个以上,则是自定义数据存储到TEXCOORD使用,所以这里我们只考虑两个分量的清空。 TEXCOORDS can have up to four components, but UVs can only have two dimensions. Not positive of the best way to handle this in general, @@ -67,10 +67,7 @@ def import_uv_layers(mesh, obj, texcoords, flip_texcoord_v): obj['3DMigoto:' + uv_name] = {'flip_v': True} else: flip_uv = lambda uv: uv - - # TODO 这种全写在一行里的太难理解了 uvs = [[d[cmap[c]] for c in components] for d in data] - for l in mesh.loops: blender_uvs.data[l.index].uv = flip_uv(uvs[l.vertex_index]) @@ -104,10 +101,6 @@ def import_vertices(mesh, vb: VertexBuffer): raise Fatal('Positions are 4D') # Nico: Blender暂时不支持4D索引,加了也没用,直接不行就报错,转人工处理。 positions = [(x[0], x[1], x[2]) for x in data] - - # https://docs.blender.org/api/3.6/bpy_extras.io_utils.html#bpy_extras.io_utils.unpack_list - # 只列出了这个函数,文档未给出任何说明 - # TODO 这里为什么是'co' mesh.vertices.foreach_set('co', unpack_list(positions)) elif elem.name.startswith('COLOR'): if len(data[0]) <= 3 or 4 == 4: @@ -132,11 +125,10 @@ def import_vertices(mesh, vb: VertexBuffer): elif elem.name == 'NORMAL': # TODO SnB的NORMAl有四个分量,但是它这里只导入了三个,所以导致最终导出的时候丢失了部分信息。 # 也有可能SnB本身就是NORMAL和TANGENT的排列? + use_normals = True normals = [(x[0], x[1], x[2]) for x in data] - mesh.create_normals_split() - for l in mesh.loops: - l.normal[:] = normals[l.vertex_index] + elif elem.name in ('TANGENT', 'BINORMAL'): # Nico: 不需要导入TANGENT,因为导出时会重新计算。 pass @@ -156,7 +148,7 @@ def import_vertices(mesh, vb: VertexBuffer): print('NOTICE: Storing unhandled semantic %s %s as vertex layer' % (elem.name, elem.Format)) vertex_layers[elem.name] = data - return (blend_indices, blend_weights, texcoords, vertex_layers, use_normals) + return (blend_indices, blend_weights, texcoords, vertex_layers, use_normals,normals) def find_texture(texture_prefix, texture_suffix, directory): @@ -244,7 +236,6 @@ def create_material_with_texture(obj, mesh_name:str, directory:str): def import_3dmigoto_raw_buffers(operator, context, fmt_path:str, vb_path:str, ib_path:str, flip_texcoord_v=True, **kwargs): - # get import prefix mesh_name = os.path.basename(fmt_path) if mesh_name.endswith(".fmt"): @@ -254,7 +245,7 @@ def import_3dmigoto_raw_buffers(operator, context, fmt_path:str, vb_path:str, ib mesh = bpy.data.meshes.new(mesh_name) obj = bpy.data.objects.new(mesh.name, mesh) - # TODO every game need different coordinate. + # Nico: 虽然每个游戏导入时的坐标不一致,导致模型朝向都不同,但是不在这里修改,而是在后面根据具体的游戏进行扶正 obj.matrix_world = axis_conversion(from_forward='-Z', from_up='Y').to_4x4() # check if .ib .vb file is empty, skip empty import. @@ -275,14 +266,23 @@ def import_3dmigoto_raw_buffers(operator, context, fmt_path:str, vb_path:str, ib obj['3DMigoto:FirstVertex'] = vb.first obj['3DMigoto:IBFormat'] = ib.format obj['3DMigoto:FirstIndex'] = ib.first + # Nico: 设置默认不重计算TANGNET和COLOR obj["3DMigoto:RecalculateTANGENT"] = False obj["3DMigoto:RecalculateCOLOR"] = False + # get current gamename + current_game = get_current_game_from_main_json(in_path=main_json_path) + + # Nico: GI,HSR,ZZZ必须重计算TANGENT, 直接在导入时设置。 + # 不重计算时薄的双面模型会直接露出里面黑色的部分,以及轮廓线也会有问题。 + if current_game in ["GI","HSR","ZZZ"]: + obj["3DMigoto:RecalculateTANGENT"] = True + # post process for import data. import_faces_from_ib(mesh, ib) - (blend_indices, blend_weights, texcoords, vertex_layers, use_normals) = import_vertices(mesh, vb) + (blend_indices, blend_weights, texcoords, vertex_layers, use_normals, normals) = import_vertices(mesh, vb) import_uv_layers(mesh, obj, texcoords, flip_texcoord_v) @@ -292,34 +292,14 @@ def import_3dmigoto_raw_buffers(operator, context, fmt_path:str, vb_path:str, ib mesh.validate(verbose=False, clean_customdata=False) mesh.update() - - # Must be done after validate step: + # Nico: 这个方法还必须得在mesh.validate和mesh.update之后调用 if use_normals: - # Taken from import_obj/import_fbx - clnors = array('f', [0.0] * (len(mesh.loops) * 3)) - mesh.loops.foreach_get("normal", clnors) - mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) - mesh.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) - - - elif hasattr(mesh, 'calc_normals'): - mesh.calc_normals() - + mesh.normals_split_custom_set_from_vertices(normals) # 设置导入时的顶点数和索引数,用于插件右键对比是否和原本顶点数量一致 obj['3DMigoto:OriginalVertexNumber'] = len(mesh.vertices) obj['3DMigoto:OriginalIndicesNumber'] = len(mesh.loops) - # get current gamename - current_game = get_current_game_from_main_json(in_path=main_json_path) - - # unity games need auto smooth 89° - if current_game in ["GI","HI3","HSR","ZZZ","Unity-CPU-PreSkinning","Naraka","Mecha","LiarsBar"]: - # Set auto_smooth 89° for unity game's perfect shadow. - obj.data.use_auto_smooth = True - # TODO Blender 4.1 remove auto_smooth_angle function. - obj.data.auto_smooth_angle = math.radians(89) - # auto texture create_material_with_texture(obj, mesh_name=mesh_name,directory= os.path.dirname(fmt_path)) @@ -343,7 +323,6 @@ class Import3DMigotoRaw(bpy.types.Operator, ImportHelper): """Import raw 3DMigoto vertex and index buffers""" bl_idname = "import_mesh.migoto_raw_buffers_mmt" bl_label = "导入3Dmigoto的原始Buffer文件" - bl_options = {'UNDO'} # new architecture only need .fmt file to locate. filename_ext = '.fmt' @@ -432,7 +411,6 @@ def execute(self, context): class MMTImportAllVbModel(bpy.types.Operator): bl_idname = "mmt.import_all" bl_label = "Import all .ib .vb model from current OutputFolder" - bl_options = {'UNDO'} def execute(self, context): import_drawib_folder_path_list = get_import_drawib_folder_path_list() @@ -482,7 +460,6 @@ def execute(self, context): class DBMTImportAllVbModelMerged(bpy.types.Operator): bl_idname = "mmt.import_all_merged" bl_label = "Import all .ib .vb model from current OutputFolder,but merged sturcture." - bl_options = {'UNDO'} def execute(self, context): import_drawib_folder_path_list = get_import_drawib_folder_path_list() diff --git a/blender/unreal/buffer_import.py b/blender/unreal/buffer_import.py index af4c786..d2838e7 100644 --- a/blender/unreal/buffer_import.py +++ b/blender/unreal/buffer_import.py @@ -141,7 +141,6 @@ def read_buffer_and_combine_obj(operator,format_json_path:str): class Import_DBMT_Buffer(bpy.types.Operator, ImportHelper): bl_idname = "import_mesh.dbmt_buffer" bl_label = "导入DBMT原始Buffer文件" - bl_options = {'UNDO'} filename_ext = '.json' filter_glob : StringProperty(default='*.json', options={'HIDDEN'}) # type: ignore