예제 #1
0
    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
예제 #2
0
    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)
예제 #3
0
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
예제 #4
0
    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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
    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
예제 #8
0
    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'}
예제 #9
0
    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)
예제 #10
0
    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()