def import_vpd(vpd_file_path, scale=0.08, asset=None): asset_path, _ = _Utilities.resolve_path(asset) print(f'import_vpd({vpd_file_path},{scale},{asset_path})') try: bpy.ops.mmd_tools.import_vpd('INVOKE_DEFAULT', filepath=os.path.join( asset_path, vpd_file_path), scale=scale) except AttributeError as ex: if str( ex ) != 'Calling operator "bpy.ops.mmd_tools.import_vpd" error, could not be found': raise raise MessageException( _('Failed to invoke mmd_tools\nPlease install mmd_tools.') ) from ex except RuntimeError as ex: if str( ex ) != 'Operator bpy.ops.mmd_tools.import_vpd.poll() failed, context is incorrect': raise raise MessageException( _('Select an object.\nThe target object for pose import is not selected.' )) from ex
def un7zip(zip_file_path=None, password=None, asset=None): asset_path, asset_json = _Utilities.resolve_path(asset) print(f'un7zip({zip_file_path},{asset_path},{asset_json})') if _Utilities.is_extracted(asset): return namespace = 'x7zipfile' loader = importlib.machinery.SourceFileLoader( namespace, os.path.join(PACKAGE_PATH, 'externals', 'x7zipfile', 'x7zipfile.py')) x7zipfile = loader.load_module(namespace) # pylint: disable=deprecated-method try: with x7zipfile.x7ZipFile(zip_file_path, pwd=password) as zip_file: zip_file.extractall(path=asset_path) except x7zipfile.x7ZipCannotExec as ex: raise MessageException( _('Failed to execute 7z\nPlease install p7zip-full or 7-zip and setup the PATH properly.' )) from ex _Utilities.write_json(asset) ImportActionExecutor.chmod_recursively(asset_path, stat.S_IWRITE)
def to_targets(breast_bones: List[bpy.types.EditBone], deform_mesh_objects: List[bpy.types.Object], head_tail: float) -> List[Target]: targets: List[Target] = [] for breast_bone in breast_bones: breast_bone_direction_vector: Vector = breast_bone.vector.normalized() bone_name = breast_bone.name parent_bone_name: str origin: Vector direction: Vector if breast_bone_direction_vector.z < -0.6: if not breast_bone.use_connect: raise MessageException('Unsupported breast bone structure.') from None parent_bone_name = breast_bone.parent.parent.name direction = breast_bone.parent.vector.normalized() origin = breast_bone.parent.head + breast_bone.vector / 2 + head_tail * breast_bone.parent.vector elif -0.4 < breast_bone_direction_vector.z < +0.4: parent_bone_name = breast_bone.parent.name direction = breast_bone_direction_vector origin = breast_bone.head + head_tail * breast_bone.vector else: raise MessageException(f'Unsupported breast bone structure. too shallow angle {breast_bone_direction_vector.z}') from None for deform_mesh_object in deform_mesh_objects: vertex_group = deform_mesh_object.vertex_groups.get(bone_name) if vertex_group is None: continue targets.append(Target( bone_name, parent_bone_name, deform_mesh_object, vertex_group, origin, direction, )) return targets
def execute_import_action(asset: AssetDescription, target_file: Union[str, None]): tree = ast.parse(asset.import_action) functions = { 'unzip': functools.partial(ImportActionExecutor.unzip, zip_file_path=target_file, asset=asset), 'un7zip': functools.partial(ImportActionExecutor.un7zip, zip_file_path=target_file, asset=asset), 'unrar': functools.partial(ImportActionExecutor.unrar, rar_file_path=target_file, asset=asset), 'link': functools.partial(ImportActionExecutor.link, from_path=target_file, asset=asset), 'import_collections': functools.partial(ImportActionExecutor.import_collections, asset=asset), 'import_world': functools.partial(ImportActionExecutor.import_world, asset=asset), 'import_pmx': functools.partial(ImportActionExecutor.import_pmx, asset=asset), 'import_vmd': functools.partial(ImportActionExecutor.import_vmd, asset=asset), 'import_vpd': functools.partial(ImportActionExecutor.import_vpd, asset=asset), 'delete_objects': functools.partial(ImportActionExecutor.delete_objects), } RestrictionChecker(*(functions.keys())).visit(tree) try: exec( # pylint: disable=exec-used compile(tree, '<source>', 'exec'), {'__builtins__': {}}, {**functions}) except FileNotFoundError as ex: if os.sys.platform == 'win32': message = str(ex) if message.startswith('[Errno 2] No such file or directory: ' ) and len(message) > 260 + 38: raise MessageException( _('The file path is too long. This can be alleviated to some extent by shortening the Asset Extract Root Folder in the Add-on Preferences.' )) from ex raise
def to_pyramid_vertices(deform_bmesh: bmesh.types.BMesh, target: Target, base_area_factor: float, project_vertically: bool) -> Tuple[Vector, List[Vector]]: to_world_matrix: Matrix = target.deform_mesh_object.matrix_world target_direction: Vector = target.direction target_origin: Vector = target.origin apex_vertex, vid2weight = to_apex_vertex(deform_bmesh, to_world_matrix, target_direction, target_origin, target.vertex_group.index) if apex_vertex is None: raise MessageException(f'The intersection of {target.bone_name} and {target.deform_mesh_object.name} not found.') from None base_vertices = to_base_vertices(deform_bmesh.verts, to_world_matrix, target_direction, target_origin, vid2weight, base_area_factor, project_vertically) return apex_vertex, base_vertices
def add_pyramid_mesh_objects( breast_bones: List[bpy.types.EditBone], mesh_objects: List[bpy.types.Object], head_tail: float, string_length_ratio: float, base_area_factor: float, project_vertically: bool ) -> List[bpy.types.Object]: targets = to_targets(breast_bones, mesh_objects, head_tail) if len(targets) == 0: raise MessageException(_('Target bones not found.')) from None pyramid_mesh_objects: List[bpy.types.Object] = [] for target in targets: pyramid_mesh_objects.append(build_pyramid_mesh_object(target, string_length_ratio, base_area_factor, project_vertically)) return pyramid_mesh_objects
def import_pmx(pmx_file_path, scale=0.08, asset=None): asset_path, _ = _Utilities.resolve_path(asset) print(f'import_pmx({pmx_file_path},{scale},{asset_path})') try: bpy.ops.mmd_tools.import_model('INVOKE_DEFAULT', filepath=os.path.join( asset_path, pmx_file_path), scale=scale) except AttributeError as ex: if str( ex ) != 'Calling operator "bpy.ops.mmd_tools.import_model" error, could not be found': raise raise MessageException( _('Failed to invoke mmd_tools\nPlease install mmd_tools.') ) from ex
def execute(self, context: bpy.types.Context): try: mmd_find_root = import_mmd_tools().core.model.Model.findRoot target_mmd_root_object = None rigid_body_objects: List[bpy.types.Object] = [] mesh_objects: List[bpy.types.Object] = [] obj: bpy.types.Object for obj in context.selected_objects: if obj.type != 'MESH': continue mmd_root_object = mmd_find_root(obj) if mmd_root_object is None: continue if target_mmd_root_object is None: target_mmd_root_object = mmd_root_object elif target_mmd_root_object != mmd_root_object: raise MessageException( _('Multiple MMD models selected. Please select single model at a time.' )) if obj.mmd_type == 'RIGID_BODY': rigid_body_objects.append(obj) elif obj.mmd_type == 'NONE': mesh_objects.append(obj) RigidBodyToClothConverter.convert(target_mmd_root_object, rigid_body_objects, mesh_objects, self.subdivision_level, self.ribbon_stiffness, PhysicsMode[self.physics_mode], self.extend_ribbon_area) except MessageException as ex: self.report(type={'ERROR'}, message=str(ex)) return {'CANCELLED'} return {'FINISHED'}
def unrar(rar_file_path=None, password=None, asset=None): asset_path, asset_json = _Utilities.resolve_path(asset) print(f'unrar({rar_file_path},{asset_path},{asset_json})') if _Utilities.is_extracted(asset): return namespace = 'xrarfile' loader = importlib.machinery.SourceFileLoader( namespace, os.path.join(PACKAGE_PATH, 'externals', 'xrarfile', 'xrarfile.py')) xrarfile = loader.load_module(namespace) # pylint: disable=deprecated-method try: with xrarfile.XRarFile(rar_file_path) as rar: rar.extractall(path=asset_path, pwd=password) except xrarfile.XRarCannotExec as ex: raise MessageException( _('Failed to execute unrar or WinRAR\nPlease install unrar or WinRAR and setup the PATH properly.' )) from ex _Utilities.write_json(asset) ImportActionExecutor.chmod_recursively(asset_path, stat.S_IWRITE)
def convert(cls, mmd_root_object: bpy.types.Object, rigid_body_objects: List[bpy.types.Object], mesh_objects: List[bpy.types.Object], subdivision_level: int, ribbon_stiffness: float, physics_mode: PhysicsMode, extend_ribbon_area: bool): # pylint: disable=too-many-arguments # pylint: disable=too-many-locals, too-many-statements mmd_model = import_mmd_tools().core.model.Model(mmd_root_object) mmd_mesh_object = mesh_objects[0] mmd_armature_object = mmd_model.armature() rigid_bodys_count = len(rigid_body_objects) rigid_body_index_dict = { rigid_body_objects[i]: i for i in range(rigid_bodys_count) } pose_bones: List[bpy.types.PoseBone] = [] for rigid_body_object in rigid_body_objects: pose_bone = mmd_armature_object.pose.bones.get( rigid_body_object.mmd_rigid.bone) if pose_bone is None: raise MessageException( iface_( 'No bones related with {rigid_body_name}, Please relate a bone to the Rigid Body.' ).format(rigid_body_name=rigid_body_object.name)) pose_bones.append(pose_bone) def remove_objects(objects: Iterable[bpy.types.Object]): for obj in objects: bpy.data.objects.remove(obj) joint_objects, joint_edge_indices, side_joint_objects = cls.collect_joints( mmd_model, rigid_body_index_dict) remove_objects(joint_objects) cloth_mesh = bpy.data.meshes.new('physics_cloth') cloth_mesh.from_pydata([r.location for r in rigid_body_objects], joint_edge_indices, []) cloth_mesh.validate() cloth_mesh_object = bpy.data.objects.new('physics_cloth', cloth_mesh) cloth_mesh_object.parent = mmd_model.clothGroupObject() cloth_mesh_object.hide_render = True cloth_mesh_object.display_type = 'WIRE' # 标记出特殊边和点 # These are special edge and vertex cloth_bm: bmesh.types.BMesh = bmesh.new() cloth_bm.from_mesh(cloth_mesh) cls.clean_mesh(cloth_bm, joint_edge_indices) # 标出头部,尾部,飘带顶点 # try mark head,tail,ribbon vertex cloth_bm.verts.ensure_lookup_table() cloth_bm.edges.ensure_lookup_table() vertices = cls.collect_vertices(cloth_bm, pose_bones, physics_mode, extend_ribbon_area) edges = cls.collect_edges(cloth_bm, vertices) new_up_verts = cls.extend_up_edges(cloth_bm, pose_bones, vertices, edges, physics_mode) new_down_verts = cls.extend_down_edges(cloth_bm, pose_bones, vertices, edges) cls.fill_faces(cloth_bm, edges.up_edges, new_up_verts) cls.fill_faces(cloth_bm, edges.down_edges, new_down_verts) cloth_bm.verts.index_update() cloth_bm.faces.ensure_lookup_table() new_side_verts = cls.extend_side_vertices(cloth_bm, vertices, edges) cls.fill_faces(cloth_bm, edges.side_edges, new_side_verts) new_ribbon_verts = cls.extend_ribbon_vertices(cloth_bm) cls.fill_faces(cloth_bm, [e for e in cloth_bm.edges if e.is_wire], new_ribbon_verts) cls.normals_make_consistent(cloth_bm) cloth_bm.verts.ensure_lookup_table() cloth_bm.edges.ensure_lookup_table() cloth_bm.to_mesh(cloth_mesh) pin_vertex_group = cls.new_pin_vertex_group(cloth_mesh_object, side_joint_objects, new_up_verts, new_side_verts, rigid_body_index_dict) remove_objects(side_joint_objects) deform_vertex_group: bpy.types.VertexGroup = mmd_mesh_object.vertex_groups.new( name='physics_cloth_deform') mesh_editor = MeshEditor(cloth_mesh_object) mesh_editor.link_to_active_collection() mesh_editor.add_subsurface_modifier('physics_cloth_subsurface', subdivision_level, subdivision_level) mesh_editor.add_armature_modifier('physics_cloth_armature', mmd_armature_object, vertex_group=pin_vertex_group.name) mesh_editor.edit_cloth_modifier( 'physics_cloth', vertex_group_mass=pin_vertex_group.name) corrective_smooth_modifier = mesh_editor.add_corrective_smooth_modifier( 'physics_cloth_smooth', smooth_type='LENGTH_WEIGHTED', rest_source='BIND') bpy.ops.object.correctivesmooth_bind( modifier=corrective_smooth_modifier.name) if subdivision_level == 0: corrective_smooth_modifier.show_viewport = False deform_vertex_group_index = deform_vertex_group.index vertices_ribbon_verts = vertices.ribbon_verts cls.bind_mmd_mesh(mmd_mesh_object, cloth_mesh_object, cloth_bm, pose_bones, deform_vertex_group_index, vertices_ribbon_verts, physics_mode) cls.set_pin_vertex_weight(pin_vertex_group, vertices_ribbon_verts, ribbon_stiffness, physics_mode) remove_objects(rigid_body_objects) if not vertices.all_ribbon and physics_mode in { PhysicsMode.AUTO, PhysicsMode.SURFACE_DEFORM }: bpy.context.view_layer.objects.active = mmd_mesh_object bpy.ops.object.surfacedeform_bind(modifier=MeshEditor( mmd_mesh_object).add_surface_deform_modifier( 'physics_cloth_deform', target=cloth_mesh_object, vertex_group=deform_vertex_group.name).name) cloth_bm.free()