Skip to content

Commit

Permalink
Support for Blender v4.1
Browse files Browse the repository at this point in the history
Remove layers, calc_normals_split, free_normals_split, use_auto_smooth, calc_normals, auto_smooth_angle
  • Loading branch information
UuuNyaa committed Apr 17, 2024
1 parent 704f905 commit 83e645d
Show file tree
Hide file tree
Showing 34 changed files with 754 additions and 607 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
- name: Copy LICENSE file
run: cp -p LICENSE mmd_tools/

- name: Remove typings for development
run: rm -rf mmd_tools/typings

- name: Create a zip
run: zip -r -9 mmd_tools-${GITHUB_REF_NAME}.zip mmd_tools/

Expand Down
2 changes: 1 addition & 1 deletion mmd_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"name": "mmd_tools",
"author": "sugiany",
"version": (4, 0, 0),
"blender": (4, 3, 0),
"blender": (4, 1, 1),
"location": "View3D > Sidebar > MMD Panel",
"description": "Utility tools for MMD model editing. (UuuNyaa's forked version)",
"warning": "",
Expand Down
91 changes: 56 additions & 35 deletions mmd_tools/bpyutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright 2013 MMD Tools authors
# This file is part of MMD Tools.

from typing import Optional, Union
from typing import List, Optional, Union

import bpy

Expand Down Expand Up @@ -107,10 +107,7 @@ def __exit__(self, _type, _value, _traceback):


def addon_preferences(attrname, default=None):
if hasattr(bpy.context, "preferences"):
addon = bpy.context.preferences.addons.get(__package__, None)
else:
addon = bpy.context.user_preferences.addons.get(__package__, None)
addon = bpy.context.preferences.addons.get(__package__, None)
return getattr(addon.preferences, attrname, default) if addon else default


Expand Down Expand Up @@ -164,26 +161,7 @@ def activate_layer_collection(target: Union[bpy.types.Object, bpy.types.LayerCol


def duplicateObject(obj, total_len):
for i in bpy.context.selected_objects:
i.select_set(False)
obj.select_set(True)
assert len(bpy.context.selected_objects) == 1
assert bpy.context.selected_objects[0] == obj
last_selected = objs = [obj]
while len(objs) < total_len:
bpy.ops.object.duplicate()
objs.extend(bpy.context.selected_objects)
remain = total_len - len(objs) - len(bpy.context.selected_objects)
if remain < 0:
last_selected = bpy.context.selected_objects
for i in range(-remain):
last_selected[i].select_set(False)
else:
for i in range(min(remain, len(last_selected))):
last_selected[i].select_set(True)
last_selected = bpy.context.selected_objects
assert len(objs) == total_len
return objs
return FnContext.duplicate_object(bpy.context, obj, total_len)


def makeCapsuleBak(segment=16, ring_count=8, radius=1.0, height=1.0, target_scene=None):
Expand Down Expand Up @@ -240,11 +218,8 @@ def makeCapsuleBak(segment=16, ring_count=8, radius=1.0, height=1.0, target_scen


def createObject(name="Object", object_data=None, target_scene=None):
target_scene = SceneOp(target_scene)
obj = bpy.data.objects.new(name=name, object_data=object_data)
target_scene.link_object(obj)
target_scene.active_object = obj
return obj
context = FnContext.ensure_context(target_scene)
return FnContext.set_active_object(context, FnContext.new_and_link_object(context, name, object_data))


def makeSphere(segment=8, ring_count=5, radius=1.0, target_object=None):
Expand Down Expand Up @@ -484,13 +459,21 @@ def id_objects(self):


class FnContext:
def __init__(self):
raise NotImplementedError("This class is not expected to be instantiated.")

@staticmethod
def ensure_context(context: Optional[bpy.types.Context] = None) -> bpy.types.Context:
return context or bpy.context

@staticmethod
def get_active_object(context: bpy.types.Context) -> Optional[bpy.types.Object]:
return context.active_object

@staticmethod
def set_active_object(context: bpy.types.Context, obj: bpy.types.Object) -> None:
def set_active_object(context: bpy.types.Context, obj: bpy.types.Object) -> bpy.types.Object:
context.view_layer.objects.active = obj
return obj

@staticmethod
def get_scene_objects(context: bpy.types.Context) -> bpy.types.SceneObjects:
Expand All @@ -504,7 +487,7 @@ def ensure_selectable(context: bpy.types.Context, obj: bpy.types.Object) -> bpy.

if obj not in context.selectable_objects:

def __layer_check(layer_collection: bpy.types.LayerCollection):
def __layer_check(layer_collection: bpy.types.LayerCollection) -> bool:
for lc in layer_collection.children:
if __layer_check(lc):
lc.hide_viewport = False
Expand All @@ -523,17 +506,55 @@ def __layer_check(layer_collection: bpy.types.LayerCollection):
for i in context.selected_objects:
if i not in selected_objects:
i.select_set(False)

return obj

@staticmethod
def select_object(context: bpy.types.Context, obj: bpy.types.Object) -> bpy.types.Object:
FnContext.ensure_selectable(context, obj)
obj.select_set(True)
FnContext.ensure_selectable(context, obj).select_set(True)
return obj

@staticmethod
def select_single_object(context: bpy.types.Context, obj: bpy.types.Object) -> bpy.types.Object:
for i in context.selected_objects:
i.select_set(False)
return FnContext.select_object(context, obj)

@staticmethod
def link_object(context: bpy.types.Context, obj: bpy.types.Object) -> bpy.types.Object:
context.collection.objects.link(obj)
return obj

@staticmethod
def new_and_link_object(context: bpy.types.Context, name: str, object_data: Optional[bpy.types.ID]) -> bpy.types.Object:
return FnContext.link_object(context, bpy.data.objects.new(name=name, object_data=object_data))

@staticmethod
def duplicate_object(context: bpy.types.Context, object_to_duplicate: bpy.types.Object, target_count: int) -> List[bpy.types.Object]:
"""Duplicate object
Args:
context (bpy.types.Context): context
obj (bpy.types.Object): object to duplicate
target_count (int): target count of duplicated objects
Returns:
List[bpy.types.Object]: duplicated objects
"""
for o in context.selected_objects:
o.select_set(False)
object_to_duplicate.select_set(True)
assert len(context.selected_objects) == 1
assert context.selected_objects[0] == object_to_duplicate
last_selected_objects = result_objects = [object_to_duplicate]
while len(result_objects) < target_count:
bpy.ops.object.duplicate()
result_objects.extend(context.selected_objects)
remain = target_count - len(result_objects) - len(context.selected_objects)
if remain < 0:
last_selected_objects = context.selected_objects
for i in range(-remain):
last_selected_objects[i].select_set(False)
else:
for i in range(min(remain, len(last_selected_objects))):
last_selected_objects[i].select_set(True)
last_selected_objects = context.selected_objects
assert len(result_objects) == target_count
return result_objects
5 changes: 4 additions & 1 deletion mmd_tools/core/bone.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def sync_bone_collections_from_armature(armature_object: bpy.types.Object):

from mmd_tools.core.model import FnModel

root_object: bpy.types.Object = FnModel.find_root(armature_object)
root_object: bpy.types.Object = FnModel.find_root_object(armature_object)
mmd_root = root_object.mmd_root

bones = armature.bones
Expand Down Expand Up @@ -198,6 +198,7 @@ def apply_bone_fixed_axis(armature_object: bpy.types.Object):

force_align = True
with bpyutils.edit_object(armature_object) as data:
bone: bpy.types.EditBone
for bone in data.edit_bones:
if bone.name not in bone_map:
bone.select = False
Expand Down Expand Up @@ -254,6 +255,7 @@ def apply_bone_local_axes(armature_object: bpy.types.Object):
bone_map[b.name] = (mmd_bone.local_axis_x, mmd_bone.local_axis_z)

with bpyutils.edit_object(armature_object) as data:
bone: bpy.types.EditBone
for bone in data.edit_bones:
if bone.name not in bone_map:
bone.select = False
Expand Down Expand Up @@ -283,6 +285,7 @@ def apply_auto_bone_roll(armature):
if not b.is_mmd_shadow_bone and not b.mmd_bone.enabled_local_axes and FnBone.has_auto_local_axis(b.mmd_bone.name_j):
bone_names.append(b.name)
with bpyutils.edit_object(armature) as data:
bone: bpy.types.EditBone
for bone in data.edit_bones:
if bone.name not in bone_names:
continue
Expand Down
Loading

0 comments on commit 83e645d

Please sign in to comment.