151 lines
5.6 KiB
Python
151 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2014 MMD Tools authors
|
|
# This file was originally part of the MMD Tools add-on for Blender
|
|
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
|
|
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
|
|
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
|
|
|
|
import re
|
|
|
|
from bpy.types import Operator
|
|
from mathutils import Matrix
|
|
|
|
|
|
class _SetShadingBase:
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
@staticmethod
|
|
def _get_view3d_spaces(context):
|
|
if getattr(context.area, "type", None) == "VIEW_3D":
|
|
return (context.area.spaces[0],)
|
|
return (area.spaces[0] for area in getattr(context.screen, "areas", ()) if area.type == "VIEW_3D")
|
|
|
|
@staticmethod
|
|
def _reset_color_management(context, use_display_device=True):
|
|
try:
|
|
context.scene.display_settings.display_device = ("None", "sRGB")[use_display_device]
|
|
except TypeError:
|
|
pass
|
|
|
|
@staticmethod
|
|
def _reset_material_shading(context, use_shadeless=False):
|
|
for i in (x for x in context.scene.objects if x.type == "MESH" and x.mmd_type == "NONE"):
|
|
for s in i.material_slots:
|
|
if s.material is None:
|
|
continue
|
|
s.material.use_nodes = False
|
|
s.material.use_shadeless = use_shadeless
|
|
|
|
def execute(self, context):
|
|
context.scene.render.engine = "BLENDER_EEVEE_NEXT"
|
|
|
|
shading_mode = getattr(self, "_shading_mode", None)
|
|
for space in self._get_view3d_spaces(context):
|
|
shading = space.shading
|
|
shading.type = "SOLID"
|
|
shading.light = "FLAT" if shading_mode == "SHADELESS" else "STUDIO"
|
|
shading.color_type = "TEXTURE" if shading_mode else "MATERIAL"
|
|
shading.show_object_outline = False
|
|
shading.show_backface_culling = False
|
|
return {"FINISHED"}
|
|
|
|
|
|
class SetGLSLShading(Operator, _SetShadingBase):
|
|
bl_idname = "mmd_tools.set_glsl_shading"
|
|
bl_label = "GLSL View"
|
|
bl_description = "Use GLSL shading with additional lighting"
|
|
|
|
_shading_mode = "GLSL"
|
|
|
|
|
|
class SetShadelessGLSLShading(Operator, _SetShadingBase):
|
|
bl_idname = "mmd_tools.set_shadeless_glsl_shading"
|
|
bl_label = "Shadeless GLSL View"
|
|
bl_description = "Use only toon shading"
|
|
|
|
_shading_mode = "SHADELESS"
|
|
|
|
|
|
class ResetShading(Operator, _SetShadingBase):
|
|
bl_idname = "mmd_tools.reset_shading"
|
|
bl_label = "Reset View"
|
|
bl_description = "Reset to default Blender shading"
|
|
|
|
|
|
class FlipPose(Operator):
|
|
bl_idname = "mmd_tools.flip_pose"
|
|
bl_label = "Flip Pose"
|
|
bl_description = "Apply the current pose of selected bones to matching bone on opposite side of X-Axis."
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
# https://docs.blender.org/manual/en/dev/rigging/armatures/bones/editing/naming.html
|
|
__LR_REGEX = [
|
|
{"re": re.compile(r"^(.+)(RIGHT|LEFT)(\.\d+)?$", re.IGNORECASE), "lr": 1},
|
|
{"re": re.compile(r"^(.+)([\.\- _])(L|R)(\.\d+)?$", re.IGNORECASE), "lr": 2},
|
|
{"re": re.compile(r"^(LEFT|RIGHT)(.+)$", re.IGNORECASE), "lr": 0},
|
|
{"re": re.compile(r"^(L|R)([\.\- _])(.+)$", re.IGNORECASE), "lr": 0},
|
|
{"re": re.compile(r"^(.+)(左|右)(\.\d+)?$"), "lr": 1},
|
|
{"re": re.compile(r"^(左|右)(.+)$"), "lr": 0},
|
|
]
|
|
__LR_MAP = {
|
|
"RIGHT": "LEFT",
|
|
"Right": "Left",
|
|
"right": "left",
|
|
"LEFT": "RIGHT",
|
|
"Left": "Right",
|
|
"left": "right",
|
|
"L": "R",
|
|
"l": "r",
|
|
"R": "L",
|
|
"r": "l",
|
|
"左": "右",
|
|
"右": "左",
|
|
}
|
|
|
|
@classmethod
|
|
def flip_name(cls, name):
|
|
for regex in cls.__LR_REGEX:
|
|
match = regex["re"].match(name)
|
|
if match:
|
|
groups = match.groups()
|
|
lr = groups[regex["lr"]]
|
|
if lr in cls.__LR_MAP:
|
|
flip_lr = cls.__LR_MAP[lr]
|
|
name = ""
|
|
for i, s in enumerate(groups):
|
|
if i == regex["lr"]:
|
|
name += flip_lr
|
|
elif s:
|
|
name += s
|
|
return name
|
|
return ""
|
|
|
|
@staticmethod
|
|
def __cmul(vec1, vec2):
|
|
return type(vec1)([x * y for x, y in zip(vec1, vec2)])
|
|
|
|
@staticmethod
|
|
def __matrix_compose(loc, rot, scale):
|
|
return (Matrix.Translation(loc) @ rot.to_matrix().to_4x4()) @ Matrix([(scale[0], 0, 0, 0), (0, scale[1], 0, 0), (0, 0, scale[2], 0), (0, 0, 0, 1)])
|
|
|
|
@classmethod
|
|
def __flip_pose(cls, matrix_basis, bone_src, bone_dest):
|
|
from mathutils import Quaternion
|
|
|
|
m = bone_dest.bone.matrix_local.to_3x3().transposed()
|
|
mi = bone_src.bone.matrix_local.to_3x3().transposed().inverted() if bone_src != bone_dest else m.inverted()
|
|
loc, rot, scale = matrix_basis.decompose()
|
|
loc = cls.__cmul(mi @ loc, (-1, 1, 1))
|
|
rot = cls.__cmul(Quaternion(mi @ rot.axis, rot.angle).normalized(), (1, 1, -1, -1))
|
|
bone_dest.matrix_basis = cls.__matrix_compose(m @ loc, Quaternion(m @ rot.axis, rot.angle).normalized(), scale)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.active_object and context.active_object.type == "ARMATURE" and context.active_object.mode == "POSE"
|
|
|
|
def execute(self, context):
|
|
pose_bones = context.active_object.pose.bones
|
|
for b, mat in [(x, x.matrix_basis.copy()) for x in context.selected_pose_bones]:
|
|
self.__flip_pose(mat, b, pose_bones.get(self.flip_name(b.name), b))
|
|
return {"FINISHED"}
|