Skip to content

Commit

Permalink
Prepare for 4.2 LTS API changes
Browse files Browse the repository at this point in the history
  • Loading branch information
UuuNyaa committed Jun 12, 2024
1 parent 0e9ca43 commit a7e75d7
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 420 deletions.
4 changes: 2 additions & 2 deletions mmd_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
bl_info = {
"name": "mmd_tools",
"author": "sugiany",
"version": (4, 1, 0),
"blender": (4, 1, 1),
"version": (4, 2, 0),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > MMD Panel",
"description": "Utility tools for MMD model editing. (UuuNyaa's forked version)",
"warning": "",
Expand Down
292 changes: 151 additions & 141 deletions mmd_tools/bpyutils.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions mmd_tools/core/bone.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def get_or_assign_bone_id(pose_bone: bpy.types.PoseBone) -> int:
@staticmethod
def __get_selected_pose_bones(armature_object: bpy.types.Object) -> Iterable[bpy.types.PoseBone]:
if armature_object.mode == "EDIT":
with bpyutils.select_object(armature_object): # update selected bones
bpy.ops.object.mode_set(mode="EDIT") # back to edit mode
bpy.ops.object.mode_set(mode="OBJECT") # update selected bones
bpy.ops.object.mode_set(mode="EDIT") # back to edit mode
context_selected_bones = bpy.context.selected_pose_bones or bpy.context.selected_bones or []
bones = armature_object.pose.bones
return (bones[b.name] for b in context_selected_bones if not bones[b.name].is_mmd_shadow_bone)
Expand Down
6 changes: 3 additions & 3 deletions mmd_tools/core/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import bpy
from mathutils import Vector

from mmd_tools.bpyutils import addon_preferences
from mmd_tools.bpyutils import FnContext
from mmd_tools.core.exceptions import MaterialNotFoundError
from mmd_tools.core.shader import _NodeGroupUtils

Expand Down Expand Up @@ -171,7 +171,7 @@ def update_toon_texture(self):
return
mmd_mat: MMDMaterial = self.__material.mmd_material
if mmd_mat.is_shared_toon_texture:
shared_toon_folder = addon_preferences("shared_toon_folder", "")
shared_toon_folder = FnContext.get_addon_preferences_attribute(FnContext.ensure_context(), "shared_toon_folder", "")
toon_path = os.path.join(shared_toon_folder, "toon%02d.bmp" % (mmd_mat.shared_toon_texture + 1))
self.create_toon_texture(bpy.path.resolve_ncase(path=toon_path))
elif mmd_mat.toon_texture != "":
Expand Down Expand Up @@ -432,7 +432,7 @@ def search_tex_image_node(node: bpy.types.ShaderNode):
active_render_engine = context.engine
preferred_output_node_target = {
"CYCLES": "CYCLES",
"BLENDER_EEVEE": "EEVEE",
"BLENDER_EEVEE_NEXT": "EEVEE",
}.get(active_render_engine, "ALL")

tex_node = None
Expand Down
46 changes: 23 additions & 23 deletions mmd_tools/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,49 +571,49 @@ def __init__(self, root_obj):
self.__temporary_grp: Optional[bpy.types.Object] = None

@staticmethod
def create(name, name_e="", scale=1, obj_name=None, armature=None, add_root_bone=False):
def create(name: str, name_e: str = "", scale: float = 1, obj_name: Optional[str] = None, armature_object: Optional[bpy.types.Object] = None, add_root_bone: bool = False):
if obj_name is None:
obj_name = name

root = bpy.data.objects.new(name=obj_name, object_data=None)
context = FnContext.ensure_context()

root: bpy.types.Object = bpy.data.objects.new(name=obj_name, object_data=None)
root.mmd_type = "ROOT"
root.mmd_root.name = name
root.mmd_root.name_e = name_e
root["mmd_tools_version"] = MMD_TOOLS_VERSION
setattr(root, Props.empty_display_size, scale / 0.2)
FnContext.link_object(FnContext.ensure_context(), root)

armObj = armature
if armObj:
m = armObj.matrix_world
armObj.parent_type = "OBJECT"
armObj.parent = root
# armObj.matrix_world = m
FnContext.link_object(context, root)

if armature_object:
m = armature_object.matrix_world
armature_object.parent_type = "OBJECT"
armature_object.parent = root
# armature_object.matrix_world = m
root.matrix_world = m
armObj.matrix_local.identity()
armature_object.matrix_local.identity()
else:
arm = bpy.data.armatures.new(name=obj_name)
armObj = bpy.data.objects.new(name=obj_name + "_arm", object_data=arm)
armObj.parent = root
FnContext.link_object(FnContext.ensure_context(), armObj)
armObj.lock_rotation = armObj.lock_location = armObj.lock_scale = [True, True, True]
setattr(armObj, Props.show_in_front, True)
setattr(armObj, Props.display_type, "WIRE")
armature_object = bpy.data.objects.new(name=obj_name + "_arm", object_data=bpy.data.armatures.new(name=obj_name))
armature_object.parent = root
FnContext.link_object(context, armature_object)
armature_object.lock_rotation = armature_object.lock_location = armature_object.lock_scale = [True, True, True]
setattr(armature_object, Props.show_in_front, True)
setattr(armature_object, Props.display_type, "WIRE")

from mmd_tools.core.bone import FnBone

FnBone.setup_special_bone_collections(armObj)
FnBone.setup_special_bone_collections(armature_object)

if add_root_bone:
bone_name = "全ての親"
with bpyutils.edit_object(armObj) as data:
with bpyutils.edit_object(armature_object) as data:
bone = data.edit_bones.new(name=bone_name)
bone.head = [0.0, 0.0, 0.0]
bone.tail = [0.0, 0.0, getattr(root, Props.empty_display_size)]
armObj.pose.bones[bone_name].mmd_bone.name_j = bone_name
armObj.pose.bones[bone_name].mmd_bone.name_e = "Root"
armature_object.pose.bones[bone_name].mmd_bone.name_j = bone_name
armature_object.pose.bones[bone_name].mmd_bone.name_e = "Root"

bpyutils.select_object(root)
FnContext.set_active_object(context, FnContext.select_single_object(context, root))
return Model(root)

@staticmethod
Expand Down
108 changes: 67 additions & 41 deletions mmd_tools/core/morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@

import logging
import re
from typing import TYPE_CHECKING, Tuple, cast

import bpy

import mmd_tools
from mmd_tools import bpyutils
from mmd_tools.bpyutils import FnContext, ObjectOp, TransformConstraintOp
from mmd_tools.bpyutils import FnContext, FnObject, TransformConstraintOp

if TYPE_CHECKING:
import mmd_tools.core.model


class FnMorph:
def __init__(self, morph, model):
def __init__(self, morph, model: "mmd_tools.core.model.Model"):
self.__morph = morph
self.__rig = model

Expand Down Expand Up @@ -85,22 +90,38 @@ def load_morphs(cls, rig):
cls.category_guess(item)

@staticmethod
def remove_shape_key(obj, key_name):
key_blocks = getattr(obj.data.shape_keys, "key_blocks", None)
if key_blocks and key_name in key_blocks:
ObjectOp(obj).shape_key_remove(key_blocks[key_name])
def remove_shape_key(mesh_object: bpy.types.Object, shape_key_name: str):
assert isinstance(mesh_object.data, bpy.types.Mesh)

shape_keys = mesh_object.data.shape_keys
if shape_keys is None:
return

key_blocks = shape_keys.key_blocks
if key_blocks and shape_key_name in key_blocks:
FnObject.mesh_remove_shape_key(mesh_object, key_blocks[shape_key_name])

@staticmethod
def copy_shape_key(obj, src_name, dest_name):
key_blocks = getattr(obj.data.shape_keys, "key_blocks", None)
if key_blocks and src_name in key_blocks:
if dest_name in key_blocks:
ObjectOp(obj).shape_key_remove(key_blocks[dest_name])
obj.active_shape_key_index = key_blocks.find(src_name)
obj.show_only_shape_key, last = True, obj.show_only_shape_key
obj.shape_key_add(name=dest_name, from_mix=True)
obj.show_only_shape_key = last
obj.active_shape_key_index = key_blocks.find(dest_name)
def copy_shape_key(mesh_object: bpy.types.Object, src_name: str, dest_name: str):
assert isinstance(mesh_object.data, bpy.types.Mesh)

shape_keys = mesh_object.data.shape_keys
if shape_keys is None:
return

key_blocks = shape_keys.key_blocks

if src_name not in key_blocks:
return

if dest_name in key_blocks:
FnObject.mesh_remove_shape_key(mesh_object, key_blocks[dest_name])

mesh_object.active_shape_key_index = key_blocks.find(src_name)
mesh_object.show_only_shape_key, last = True, mesh_object.show_only_shape_key
mesh_object.shape_key_add(name=dest_name, from_mix=True)
mesh_object.show_only_shape_key = last
mesh_object.active_shape_key_index = key_blocks.find(dest_name)

@staticmethod
def get_uv_morph_vertex_groups(obj, morph_name=None, offset_axes="XYZW"):
Expand Down Expand Up @@ -288,7 +309,7 @@ def morph_equals(l, r) -> bool:


class _MorphSlider:
def __init__(self, model):
def __init__(self, model: "mmd_tools.core.model.Model"):
self.__rig = model

def placeholder(self, create=False, binded=False):
Expand Down Expand Up @@ -392,23 +413,27 @@ def __cleanup(self, names_in_use=None):
rig = self.__rig
morph_sliders = self.placeholder()
morph_sliders = morph_sliders.data.shape_keys.key_blocks if morph_sliders else {}
for mesh in rig.meshes():
for kb in getattr(mesh.data.shape_keys, "key_blocks", ()):
if kb.name not in names_in_use:
if kb.name.startswith("mmd_bind"):
kb.driver_remove("value")
ms = morph_sliders[kb.relative_key.name]
kb.relative_key.slider_min, kb.relative_key.slider_max = min(ms.slider_min, floor(ms.value)), max(ms.slider_max, ceil(ms.value))
kb.relative_key.value = ms.value
kb.relative_key.mute = False
ObjectOp(mesh).shape_key_remove(kb)
elif kb.name in morph_sliders and self.__shape_key_driver_check(kb):
ms = morph_sliders[kb.name]
kb.driver_remove("value")
kb.slider_min, kb.slider_max = min(ms.slider_min, floor(kb.value)), max(ms.slider_max, ceil(kb.value))
for m in mesh.modifiers: # uv morph
for mesh_object in rig.meshes():
for kb in getattr(mesh_object.data.shape_keys, "key_blocks", cast(Tuple[bpy.types.ShapeKey], ())):
if kb.name in names_in_use:
continue

if kb.name.startswith("mmd_bind"):
kb.driver_remove("value")
ms = morph_sliders[kb.relative_key.name]
kb.relative_key.slider_min, kb.relative_key.slider_max = min(ms.slider_min, floor(ms.value)), max(ms.slider_max, ceil(ms.value))
kb.relative_key.value = ms.value
kb.relative_key.mute = False
FnObject.mesh_remove_shape_key(mesh_object, kb)

elif kb.name in morph_sliders and self.__shape_key_driver_check(kb):
ms = morph_sliders[kb.name]
kb.driver_remove("value")
kb.slider_min, kb.slider_max = min(ms.slider_min, floor(kb.value)), max(ms.slider_max, ceil(kb.value))

for m in mesh_object.modifiers: # uv morph
if m.name.startswith("mmd_bind") and m.name not in names_in_use:
mesh.modifiers.remove(m)
mesh_object.modifiers.remove(m)

from mmd_tools.core.shader import _MaterialMorph

Expand Down Expand Up @@ -468,9 +493,9 @@ def bind(self):

shape_key_map = {}
uv_morph_map = {}
for mesh in rig.meshes():
mesh.show_only_shape_key = False
key_blocks = getattr(mesh.data.shape_keys, "key_blocks", ())
for mesh_object in rig.meshes():
mesh_object.show_only_shape_key = False
key_blocks = getattr(mesh_object.data.shape_keys, "key_blocks", ())
for kb in key_blocks:
kb_name = kb.name
if kb_name not in morph_sliders:
Expand All @@ -481,7 +506,7 @@ def bind(self):
else:
name_bind = "mmd_bind%s" % hash(morph_sliders[kb_name])
if name_bind not in key_blocks:
mesh.shape_key_add(name=name_bind, from_mix=False)
mesh_object.shape_key_add(name=name_bind, from_mix=False)
kb_bind = key_blocks[name_bind]
kb_bind.relative_key = kb
kb_bind.slider_min = -10
Expand All @@ -492,20 +517,20 @@ def bind(self):
shape_key_map.setdefault(name_bind, []).append((kb_bind, data_path, groups))
group_map.setdefault(("vertex_morphs", kb_name), []).append(groups)

uv_layers = [l.name for l in mesh.data.uv_layers if not l.name.startswith("_")]
uv_layers = [l.name for l in mesh_object.data.uv_layers if not l.name.startswith("_")]
uv_layers += [""] * (5 - len(uv_layers))
for vg, morph_name, axis in FnMorph.get_uv_morph_vertex_groups(mesh):
for vg, morph_name, axis in FnMorph.get_uv_morph_vertex_groups(mesh_object):
morph = mmd_root.uv_morphs.get(morph_name, None)
if morph is None or morph.data_type != "VERTEX_GROUP":
continue

uv_layer = "_" + uv_layers[morph.uv_index] if axis[1] in "ZW" else uv_layers[morph.uv_index]
if uv_layer not in mesh.data.uv_layers:
if uv_layer not in mesh_object.data.uv_layers:
continue

name_bind = "mmd_bind%s" % hash(vg.name)
uv_morph_map.setdefault(name_bind, ())
mod = mesh.modifiers.get(name_bind, None) or mesh.modifiers.new(name=name_bind, type="UV_WARP")
mod = mesh_object.modifiers.get(name_bind, None) or mesh_object.modifiers.new(name=name_bind, type="UV_WARP")
mod.show_expanded = False
mod.vertex_group = vg.name
mod.axis_u, mod.axis_v = ("Y", "X") if axis[1] in "YW" else ("X", "Y")
Expand Down Expand Up @@ -692,6 +717,7 @@ def update_mmd_morph():
for mat_morph in root.mmd_root.material_morphs:
for morph_data in mat_morph.data:
if morph_data.material_data is not None:
# SUPPORT_UNTIL: 5 LTS
# The material_id is also no longer used, but for compatibility with older version mmd_tools, keep it.
if "material_id" not in morph_data.material_data.mmd_material or "material_id" not in morph_data or morph_data.material_data.mmd_material["material_id"] == morph_data["material_id"]:
# In the new version, the related_mesh property is no longer used.
Expand Down
6 changes: 3 additions & 3 deletions mmd_tools/core/pmx/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import bpy
import mathutils

from mmd_tools import bpyutils
from mmd_tools.bpyutils import FnContext
from mmd_tools.core import pmx
from mmd_tools.core.material import FnMaterial
from mmd_tools.core.morph import FnMorph
Expand Down Expand Up @@ -225,7 +225,7 @@ def __exportTexture(self, filepath):

def __copy_textures(self, output_dir, base_folder=""):
tex_dir_fallback = os.path.join(output_dir, "textures")
tex_dir_preference = bpyutils.addon_preferences("base_texture_folder", "")
tex_dir_preference = FnContext.get_addon_preferences_attribute(FnContext.ensure_context(), "base_texture_folder", "")

path_set = set() # to prevent overwriting
tex_copy_list = []
Expand Down Expand Up @@ -1269,7 +1269,7 @@ def execute(self, filepath, **args):
if args.get("copy_textures", False):
output_dir = os.path.dirname(filepath)
import_folder = root.get("import_folder", "") if root else ""
base_folder = bpyutils.addon_preferences("base_texture_folder", "")
base_folder = FnContext.get_addon_preferences_attribute(FnContext.ensure_context(), "base_texture_folder", "")
self.__copy_textures(output_dir, import_folder or base_folder)

pmx.save(filepath, self.__model, add_uv_count=self.__add_uv_count)
Expand Down
6 changes: 3 additions & 3 deletions mmd_tools/core/sdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import bpy
from mathutils import Matrix, Vector

from mmd_tools.bpyutils import FnObject


def _hash(v):
if isinstance(v, (bpy.types.Object, bpy.types.PoseBone)):
Expand Down Expand Up @@ -293,11 +295,9 @@ def bind(cls, obj, bulk_update=None, use_skip=True, use_scale=False):

@classmethod
def unbind(cls, obj):
from mmd_tools.bpyutils import ObjectOp

if obj.data.shape_keys:
if cls.SHAPEKEY_NAME in obj.data.shape_keys.key_blocks:
ObjectOp(obj).shape_key_remove(obj.data.shape_keys.key_blocks[cls.SHAPEKEY_NAME])
FnObject.mesh_remove_shape_key(obj, obj.data.shape_keys.key_blocks[cls.SHAPEKEY_NAME])
for mod in obj.modifiers:
if mod.type == "ARMATURE" and mod.vertex_group == cls.MASK_NAME:
mod.vertex_group = ""
Expand Down
Loading

0 comments on commit a7e75d7

Please sign in to comment.