def apply_additional_transformation(cls, armature): def __is_dirty_bone(b): if b.is_mmd_shadow_bone: return False mmd_bone = b.mmd_bone if mmd_bone.has_additional_rotation or mmd_bone.has_additional_location: return True return mmd_bone.is_additional_transform_dirty dirty_bones = [b for b in armature.pose.bones if __is_dirty_bone(b)] # setup constraints shadow_bone_pool = [] for p_bone in dirty_bones: sb = cls.__setup_constraints(p_bone) if sb: shadow_bone_pool.append(sb) # setup shadow bones with bpyutils.edit_object(armature) as data: edit_bones = data.edit_bones for sb in shadow_bone_pool: sb.update_edit_bones(edit_bones) pose_bones = armature.pose.bones for sb in shadow_bone_pool: sb.update_pose_bones(pose_bones) # finish for p_bone in dirty_bones: p_bone.mmd_bone.is_additional_transform_dirty = False cls.patch_rna_idprop(armature.pose.bones)
def clean_additional_transformation(cls, armature): # clean shadow bones shadow_bone_types = { 'DUMMY', 'SHADOW', 'ADDITIONAL_TRANSFORM', 'ADDITIONAL_TRANSFORM_INVERT', } def __is_at_shadow_bone(b): return b.is_mmd_shadow_bone and b.mmd_shadow_bone_type in shadow_bone_types shadow_bone_names = [ b.name for b in armature.pose.bones if __is_at_shadow_bone(b) ] if len(shadow_bone_names) > 0: with bpyutils.edit_object(armature) as data: remove_edit_bones(data.edit_bones, shadow_bone_names) # clean constraints for p_bone in armature.pose.bones: p_bone.mmd_bone.is_additional_transform_dirty = True constraints = p_bone.constraints remove_constraint(constraints, 'mmd_additional_rotation') remove_constraint(constraints, 'mmd_additional_location') if remove_constraint(constraints, 'mmd_additional_parent'): p_bone.bone.use_inherit_rotation = True cls.patch_rna_idprop(armature.pose.bones)
def apply_auto_bone_roll(cls, armature): bone_names = [] for b in armature.pose.bones: if (not b.is_mmd_shadow_bone and not b.mmd_bone.enabled_local_axes and cls.has_auto_local_axis(b.mmd_bone.name_j)): bone_names.append(b.name) with bpyutils.edit_object(armature) as data: for bone in data.edit_bones: if bone.name not in bone_names: select = False continue cls.update_auto_bone_roll(bone) bone.select = True
def apply_bone_fixed_axis(cls, armature): bone_map = {} for b in armature.pose.bones: if b.is_mmd_shadow_bone or not b.mmd_bone.enabled_fixed_axis: continue mmd_bone = b.mmd_bone parent_tip = b.parent and not b.parent.is_mmd_shadow_bone and b.parent.mmd_bone.is_tip bone_map[b.name] = (mmd_bone.fixed_axis.normalized(), mmd_bone.is_tip, parent_tip) force_align = True with bpyutils.edit_object(armature) as data: for bone in data.edit_bones: if bone.name not in bone_map: bone.select = False continue fixed_axis, is_tip, parent_tip = bone_map[bone.name] if fixed_axis.length: axes = [bone.x_axis, bone.y_axis, bone.z_axis] direction = fixed_axis.normalized().xzy idx, val = max([(i, direction.dot(v)) for i, v in enumerate(axes)], key=lambda x: abs(x[1])) idx_1, idx_2 = (idx + 1) % 3, (idx + 2) % 3 axes[idx] = -direction if val < 0 else direction axes[idx_2] = axes[idx].cross(axes[idx_1]) axes[idx_1] = axes[idx_2].cross(axes[idx]) if parent_tip and bone.use_connect: bone.use_connect = False bone.head = bone.parent.head if force_align: tail = bone.head + axes[1].normalized() * bone.length if is_tip or (tail - bone.tail).length > 1e-4: for c in bone.children: if c.use_connect: c.use_connect = False if is_tip: c.head = bone.head bone.tail = tail bone.align_roll(axes[2]) bone_map[bone.name] = tuple(i != idx for i in range(3)) else: bone_map[bone.name] = (True, True, True) bone.select = True for bone_name, locks in bone_map.items(): b = armature.pose.bones[bone_name] b.lock_location = (True, True, True) b.lock_ik_x, b.lock_ik_y, b.lock_ik_z = b.lock_rotation = locks
def create(name, name_e='', scale=1, obj_name=None, armature=None, add_root_bone=False): scene = SceneOp(bpy.context) if obj_name is None: obj_name = name root = 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.empty_draw_size = scale / 0.2 scene.link_object(root) armObj = armature if armObj: m = armObj.matrix_world armObj.parent_type = 'OBJECT' armObj.parent = root #armObj.matrix_world = m root.matrix_world = m armObj.matrix_local.identity() else: arm = bpy.data.armatures.new(name=obj_name) #arm.draw_type = 'STICK' armObj = bpy.data.objects.new(name=obj_name + '_arm', object_data=arm) armObj.parent = root scene.link_object(armObj) armObj.lock_rotation = armObj.lock_location = armObj.lock_scale = [ True, True, True ] armObj.show_x_ray = True armObj.draw_type = 'WIRE' if add_root_bone: bone_name = u'全ての親' with bpyutils.edit_object(armObj) as data: bone = data.edit_bones.new(name=bone_name) bone.head = [0.0, 0.0, 0.0] bone.tail = [0.0, 0.0, root.empty_draw_size] armObj.pose.bones[bone_name].mmd_bone.name_j = bone_name armObj.pose.bones[bone_name].mmd_bone.name_e = 'Root' bpyutils.select_object(root) return Model(root)
def apply_bone_local_axes(cls, armature): bone_map = {} for b in armature.pose.bones: if b.is_mmd_shadow_bone or not b.mmd_bone.enabled_local_axes: continue mmd_bone = b.mmd_bone bone_map[b.name] = (mmd_bone.local_axis_x, mmd_bone.local_axis_z) with bpyutils.edit_object(armature) as data: for bone in data.edit_bones: if bone.name not in bone_map: bone.select = False continue local_axis_x, local_axis_z = bone_map[bone.name] cls.update_bone_roll(bone, local_axis_x, local_axis_z) bone.select = True
def create_ik_constraint(self, bone, ik_target, threshold=0.1): """ create IK constraint If the distance of the ik_target head and the bone tail is greater than threashold, then a dummy ik target bone is created. Args: bone: A pose bone to add a IK constraint id_target: A pose bone for IK target threshold: Threshold of creating a dummy bone Returns: The bpy.types.KinematicConstraint object created. It is set target and subtarget options. """ ik_target_name = ik_target.name if 0 and (ik_target.head - bone.tail).length > threshold: logging.debug('*** create a ik_target_dummy of bone %s', ik_target.name) with bpyutils.edit_object(self.__arm) as data: dummy_target = data.edit_bones.new(name=ik_target.name + '.ik_target_dummy') dummy_target.head = bone.tail dummy_target.tail = dummy_target.head + mathutils.Vector( [0, 0, 1]) dummy_target.layers = (False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) dummy_target.parent = data.edit_bones[ik_target.name] ik_target_name = dummy_target.name dummy_ik_target = self.__arm.pose.bones[ik_target_name] dummy_ik_target.is_mmd_shadow_bone = True dummy_ik_target.mmd_shadow_bone_type = 'IK_TARGET' ik_const = bone.constraints.new('IK') ik_const.target = self.__arm ik_const.subtarget = ik_target_name return ik_const
def bind(self): #bpy.context.user_preferences.system.use_scripts_auto_execute = True rig = self.__rig root = rig.rootObject() armObj = rig.armature() mmd_root = root.mmd_root obj = self.create() arm = self.__dummy_armature(obj, create=True) morph_key_blocks = obj.data.shape_keys.key_blocks # data gathering group_map = {} 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 kb in key_blocks: kb_name = kb.name if kb_name not in morph_key_blocks: continue name_bind = 'mmd_bind%s' % hash(morph_key_blocks[kb_name]) if name_bind not in key_blocks: mesh.shape_key_add(name=name_bind) kb_bind = key_blocks[name_bind] kb_bind.relative_key = kb kb_bind.slider_min = -10 kb_bind.slider_max = 10 data_path = 'data.shape_keys.key_blocks["%s"].value' % kb_name.replace( '"', '\\"') groups = [] 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 += [''] * (5 - len(uv_layers)) for vg, morph_name, axis in FnMorph.get_uv_morph_vertex_groups( mesh): 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: 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.show_expanded = False mod.vertex_group = vg.name mod.axis_u, mod.axis_v = ('Y', 'X') if axis[1] in 'YW' else ('X', 'Y') mod.uv_layer = uv_layer name_bind = 'mmd_bind%s' % hash(morph_name) mod.object_from = mod.object_to = arm if axis[0] == '-': mod.bone_from, mod.bone_to = 'mmd_bind_ctrl_base', name_bind else: mod.bone_from, mod.bone_to = name_bind, 'mmd_bind_ctrl_base' bone_offset_map = {} with bpyutils.edit_object(arm) as data: edit_bones = data.edit_bones def __get_bone(name, layer, parent): b = edit_bones.get(name, None) or edit_bones.new(name=name) b.layers = [x == layer for x in range(len(b.layers))] b.head = (0, 0, 0) b.tail = (0, 0, 1) b.use_deform = False b.parent = parent return b for m in mmd_root.bone_morphs: data_path = 'data.shape_keys.key_blocks["%s"].value' % m.name.replace( '"', '\\"') for d in m.data: if not d.bone: d.name = '' continue d.name = str(hash(d)) name_bind = 'mmd_bind%s' % hash(d) b = __get_bone(name_bind, 10, None) groups = [] bone_offset_map[name_bind] = (m.name, d, b.name, data_path, groups) group_map.setdefault(('bone_morphs', m.name), []).append(groups) ctrl_base = __get_bone('mmd_bind_ctrl_base', 11, None) for m in mmd_root.uv_morphs: morph_name = m.name.replace('"', '\\"') data_path = 'data.shape_keys.key_blocks["%s"].value' % morph_name scale_path = 'mmd_root.uv_morphs["%s"].vertex_group_scale' % morph_name name_bind = 'mmd_bind%s' % hash(m.name) b = __get_bone(name_bind, 11, ctrl_base) groups = [] uv_morph_map.setdefault(name_bind, []).append( (b.name, data_path, scale_path, groups)) group_map.setdefault(('uv_morphs', m.name), []).append(groups) used_bone_names = bone_offset_map.keys() | uv_morph_map.keys() used_bone_names.add(ctrl_base.name) for b in edit_bones: # cleanup if b.name.startswith( 'mmd_bind') and b.name not in used_bone_names: edit_bones.remove(b) for m in mmd_root.group_morphs: for d in m.data: for groups in group_map.get((d.morph_type, d.name), ()): morph_name = m.name.replace('"', '\\"') param = (morph_name, d.name.replace('"', '\\"')) factor_path = 'mmd_root.group_morphs["%s"].data["%s"].factor' % param morph_path = 'data.shape_keys.key_blocks["%s"].value' % morph_name groups.append((m.name, morph_path, factor_path)) self.__cleanup(shape_key_map.keys() | bone_offset_map.keys() | uv_morph_map.keys()) def __config_groups(variables, expression, groups): for g_name, morph_path, factor_path in groups: var = self.__add_single_prop(variables, obj, morph_path, 'g') fvar = self.__add_single_prop(variables, root, factor_path, 'w') expression = '%s+%s*%s' % (expression, var.name, fvar.name) return expression # vertex morphs for kb_bind, morph_data_path, groups in ( i for l in shape_key_map.values() for i in l): driver, variables = self.__driver_variables(kb_bind, 'value') var = self.__add_single_prop(variables, obj, morph_data_path, 'v') driver.expression = '-(%s)' % __config_groups( variables, var.name, groups) kb_bind.relative_key.mute = True kb_bind.mute = False # bone morphs def __config_bone_morph(constraints, map_type, attributes, val, val_str): c_name = 'mmd_bind%s.%s' % (hash(data), map_type[:3]) c = TransformConstraintOp.create(constraints, c_name, map_type) TransformConstraintOp.update_min_max(c, val, None) c.show_expanded = False c.target = arm c.subtarget = bname for attr in attributes: driver, variables = self.__driver_variables( armObj, c.path_from_id(attr)) var = self.__add_single_prop(variables, obj, morph_data_path, 'b') expression = __config_groups(variables, var.name, groups) sign = '-' if attr.startswith('to_min') else '' driver.expression = '%s%s*(%s)' % (sign, val_str, expression) from math import pi attributes_rot = TransformConstraintOp.min_max_attributes( 'ROTATION', 'to') attributes_loc = TransformConstraintOp.min_max_attributes( 'LOCATION', 'to') for morph_name, data, bname, morph_data_path, groups in bone_offset_map.values( ): b = arm.pose.bones[bname] root_path = 'mmd_root.bone_morphs["%s"].data["%s"]' % ( morph_name.replace('"', '\\"'), data.name) for i in range(3): data_path = '%s.location[%d]' % (root_path, i) driver, variables = self.__driver_variables(b, 'location', index=i) driver.expression = self.__add_single_prop( variables, root, data_path, 'L').name for i in range(4): data_path = '%s.rotation[%d]' % (root_path, i) driver, variables = self.__driver_variables( b, 'rotation_quaternion', index=i) driver.expression = self.__add_single_prop( variables, root, data_path, 'R').name b.is_mmd_shadow_bone = True b.mmd_shadow_bone_type = 'BIND' pb = armObj.pose.bones[data.bone] __config_bone_morph(pb.constraints, 'ROTATION', attributes_rot, pi, 'pi') __config_bone_morph(pb.constraints, 'LOCATION', attributes_loc, 100, '100') # uv morphs b = arm.pose.bones['mmd_bind_ctrl_base'] b.is_mmd_shadow_bone = True b.mmd_shadow_bone_type = 'BIND' for bname, data_path, scale_path, groups in ( i for l in uv_morph_map.values() for i in l): b = arm.pose.bones[bname] b.is_mmd_shadow_bone = True b.mmd_shadow_bone_type = 'BIND' driver, variables = self.__driver_variables(b, 'location', index=0) var = self.__add_single_prop(variables, obj, data_path, 'u') fvar = self.__add_single_prop(variables, root, scale_path, 's') driver.expression = '(%s)*%s' % (__config_groups( variables, var.name, groups), fvar.name) #TODO material morphs if possible morph_key_blocks[0].mute = False
def __createEditBones(self, obj, pmx_bones): """ create EditBones from pmx file data. @return the list of bone names which can be accessed by the bone index of pmx data. """ editBoneTable = [] nameTable = [] specialTipBones = [] dependency_cycle_ik_bones = [] #for i, p_bone in enumerate(pmx_bones): # if p_bone.isIK: # if p_bone.target != -1: # t = pmx_bones[p_bone.target] # if p_bone.parent == t.parent: # dependency_cycle_ik_bones.append(i) with bpyutils.edit_object(obj) as data: for i in pmx_bones: bone = data.edit_bones.new(name=i.name) loc = Vector(i.location).xzy * self.__scale bone.head = loc editBoneTable.append(bone) nameTable.append(bone.name) for i, (b_bone, m_bone) in enumerate(zip(editBoneTable, pmx_bones)): if m_bone.parent != -1: if i not in dependency_cycle_ik_bones: b_bone.parent = editBoneTable[m_bone.parent] else: b_bone.parent = editBoneTable[m_bone.parent].parent for b_bone, m_bone in zip(editBoneTable, pmx_bones): if isinstance(m_bone.displayConnection, int): if m_bone.displayConnection != -1: b_bone.tail = editBoneTable[ m_bone.displayConnection].head else: b_bone.tail = b_bone.head else: loc = Vector(m_bone.displayConnection).xzy * self.__scale b_bone.tail = b_bone.head + loc for b_bone, m_bone in zip(editBoneTable, pmx_bones): if m_bone.isIK and m_bone.target != -1: logging.debug(' - checking IK links of %s', b_bone.name) b_target = editBoneTable[m_bone.target] for i in range(len(m_bone.ik_links)): b_bone_link = editBoneTable[m_bone.ik_links[i].target] if self.__fix_IK_links or b_bone_link.length < 0.001: b_bone_tail = b_target if i == 0 else editBoneTable[ m_bone.ik_links[i - 1].target] loc = b_bone_tail.head - b_bone_link.head if loc.length < 0.001: logging.warning(' ** unsolved IK link %s **', b_bone_link.name) elif b_bone_tail.parent != b_bone_link: logging.warning(' ** skipped IK link %s **', b_bone_link.name) elif (b_bone_link.tail - b_bone_tail.head).length > 1e-4: logging.debug(' * fix IK link %s', b_bone_link.name) b_bone_link.tail = b_bone_link.head + loc for b_bone, m_bone in zip(editBoneTable, pmx_bones): # Set the length of too short bones to 1 because Blender delete them. if b_bone.length < 0.001: if not self.__apply_bone_fixed_axis and m_bone.axis is not None: fixed_axis = Vector(m_bone.axis) if fixed_axis.length: b_bone.tail = b_bone.head + fixed_axis.xzy.normalized( ) * self.__scale else: b_bone.tail = b_bone.head + Vector( (0, 0, 1)) * self.__scale else: b_bone.tail = b_bone.head + Vector( (0, 0, 1)) * self.__scale if m_bone.displayConnection != -1 and m_bone.displayConnection != [ 0.0, 0.0, 0.0 ]: logging.debug(' * special tip bone %s, display %s', b_bone.name, str(m_bone.displayConnection)) specialTipBones.append(b_bone.name) for b_bone, m_bone in zip(editBoneTable, pmx_bones): if m_bone.localCoordinate is not None: FnBone.update_bone_roll(b_bone, m_bone.localCoordinate.x_axis, m_bone.localCoordinate.z_axis) elif FnBone.has_auto_local_axis(m_bone.name): FnBone.update_auto_bone_roll(b_bone) for b_bone, m_bone in zip(editBoneTable, pmx_bones): if isinstance(m_bone.displayConnection, int) and m_bone.displayConnection >= 0: t = editBoneTable[m_bone.displayConnection] if t.parent is None or t.parent != b_bone: logging.warning(' * disconnected: %s (%d)<> %s', b_bone.name, len(b_bone.children), t.name) continue if pmx_bones[m_bone.displayConnection].isMovable: logging.warning(' * disconnected: %s (%d)-> %s', b_bone.name, len(b_bone.children), t.name) continue if (b_bone.tail - t.head).length > 1e-4: logging.warning(' * disconnected: %s (%d)=> %s', b_bone.name, len(b_bone.children), t.name) continue t.use_connect = True return nameTable, specialTipBones