Merge branch 'master' into xr-controller-support
This commit is contained in:
commit
a85360cbdf
|
@ -1836,8 +1836,8 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
|
|||
|
||||
row.emboss = 'NONE'
|
||||
op = row.operator('wm.blenderkit_tooltip', text=str(s), icon_value=pcoll['trophy'].icon_id)
|
||||
op.tooltip = 'Asset score calculated from averaged user ratings. \n\n' \
|
||||
'Score = quality × complexity × 10*\n\n *Happiness multiplier'
|
||||
op.tooltip = 'Asset score calculated from user ratings. \n\n' \
|
||||
'Score = average quality × median complexity × 10*\n\n *Happiness multiplier'
|
||||
row.label(text=' ')
|
||||
|
||||
tooltip_extension = f'.\n\nRatings results are shown for assets with more than {show_rating_threshold} ratings'
|
||||
|
@ -1847,7 +1847,7 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
|
|||
row.label(text=' ')
|
||||
|
||||
op = row.operator('wm.blenderkit_tooltip', text=str(c), icon_value=pcoll['dumbbell'].icon_id)
|
||||
op.tooltip = f"Complexity, average from {rc['workingHours']} ratings" \
|
||||
op.tooltip = f"Complexity, median from {rc['workingHours']} ratings" \
|
||||
f"{tooltip_extension if rcount <= show_rating_threshold else ''}"
|
||||
|
||||
if rcount <= show_rating_prompt_threshold:
|
||||
|
@ -2029,9 +2029,9 @@ class SetCategoryOperator(bpy.types.Operator):
|
|||
|
||||
|
||||
class ClosePopupButton(bpy.types.Operator):
|
||||
"""Visit subcategory"""
|
||||
"""Close popup window"""
|
||||
bl_idname = "view3d.close_popup_button"
|
||||
bl_label = "BlenderKit close popup"
|
||||
bl_label = "Close popup"
|
||||
bl_options = {'REGISTER', 'INTERNAL'}
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -692,7 +692,12 @@ def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False
|
|||
bpy.ops.object.material_slot_select()
|
||||
|
||||
scale = (scale.x + scale.y + scale.z) / 3.0
|
||||
|
||||
if tex_size == 0:# prevent division by zero, it's possible to have 0 in tex size by unskilled uploaders
|
||||
tex_size = 1
|
||||
|
||||
if not just_scale:
|
||||
|
||||
bpy.ops.uv.cube_project(
|
||||
cube_size=scale * 2.0 / (tex_size),
|
||||
correct_aspect=False) # it's * 2.0 because blender can't tell size of a unit cube :)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
bl_info = {
|
||||
"name": "FBX format",
|
||||
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
|
||||
"version": (4, 23, 0),
|
||||
"version": (4, 24, 0),
|
||||
"blender": (2, 90, 0),
|
||||
"location": "File > Import-Export",
|
||||
"description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
|
||||
|
|
|
@ -150,7 +150,8 @@ FBX_FRAMERATES = (
|
|||
|
||||
# ##### Misc utilities #####
|
||||
|
||||
DO_PERFMON = True
|
||||
# Enable performance reports (measuring time used to perform various steps of importing or exporting).
|
||||
DO_PERFMON = False
|
||||
|
||||
if DO_PERFMON:
|
||||
class PerfMon():
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
bl_info = {
|
||||
'name': 'glTF 2.0 format',
|
||||
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
|
||||
"version": (1, 7, 23),
|
||||
"version": (1, 7, 28),
|
||||
'blender': (2, 91, 0),
|
||||
'location': 'File > Import-Export',
|
||||
'description': 'Import-Export as glTF 2.0',
|
||||
|
|
|
@ -387,7 +387,7 @@ def __get_positions(blender_mesh, key_blocks, armature, blender_object, export_s
|
|||
|
||||
# Transform for skinning
|
||||
if armature and blender_object:
|
||||
apply_matrix = armature.matrix_world.inverted() @ blender_object.matrix_world
|
||||
apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
|
||||
loc_transform = armature.matrix_world @ apply_matrix
|
||||
|
||||
loc_transform = blender_object.matrix_world
|
||||
|
@ -427,8 +427,8 @@ def __get_normals(blender_mesh, key_blocks, armature, blender_object, export_set
|
|||
|
||||
# Transform for skinning
|
||||
if armature and blender_object:
|
||||
apply_matrix = (armature.matrix_world.inverted() @ blender_object.matrix_world)
|
||||
apply_matrix = apply_matrix.to_3x3().inverted().transposed()
|
||||
apply_matrix = (armature.matrix_world.inverted_safe() @ blender_object.matrix_world)
|
||||
apply_matrix = apply_matrix.to_3x3().inverted_safe().transposed()
|
||||
normal_transform = armature.matrix_world.to_3x3() @ apply_matrix
|
||||
|
||||
normals[:] = __apply_mat_to_all(normal_transform, normals)
|
||||
|
@ -463,7 +463,7 @@ def __get_tangents(blender_mesh, armature, blender_object, export_settings):
|
|||
|
||||
# Transform for skinning
|
||||
if armature and blender_object:
|
||||
apply_matrix = armature.matrix_world.inverted() @ blender_object.matrix_world
|
||||
apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
|
||||
tangent_transform = apply_matrix.to_quaternion().to_matrix()
|
||||
tangents = __apply_mat_to_all(tangent_transform, tangents)
|
||||
__normalize_vecs(tangents)
|
||||
|
@ -482,7 +482,7 @@ def __get_bitangent_signs(blender_mesh, armature, blender_object, export_setting
|
|||
if armature and blender_object:
|
||||
# Bitangent signs should flip when handedness changes
|
||||
# TODO: confirm
|
||||
apply_matrix = armature.matrix_world.inverted() @ blender_object.matrix_world
|
||||
apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
|
||||
tangent_transform = apply_matrix.to_quaternion().to_matrix()
|
||||
flipped = tangent_transform.determinant() < 0
|
||||
if flipped:
|
||||
|
|
|
@ -317,14 +317,14 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec
|
|||
# We can ignore this keyframes
|
||||
# if there are some fcurve, we can keep only 2 keyframes, first and last
|
||||
if blender_object_if_armature is not None:
|
||||
std = np.ptp(np.ptp([[k.value[i] for i in range(len(keyframes[0].value))] for k in keyframes], axis=0))
|
||||
cst = all([j < 0.0001 for j in np.ptp([[k.value[i] for i in range(len(keyframes[0].value))] for k in keyframes], axis=0)])
|
||||
|
||||
if node_channel_is_animated is True: # fcurve on this bone for this property
|
||||
# Keep animation, but keep only 2 keyframes if data are not changing
|
||||
return [keyframes[0], keyframes[-1]] if std < 0.0001 and len(keyframes) >= 2 else keyframes
|
||||
return [keyframes[0], keyframes[-1]] if cst is True and len(keyframes) >= 2 else keyframes
|
||||
else: # bone is not animated (no fcurve)
|
||||
# Not keeping if not changing property
|
||||
return None if std < 0.0001 else keyframes
|
||||
return None if cst is True else keyframes
|
||||
|
||||
return keyframes
|
||||
|
||||
|
|
|
@ -370,7 +370,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
|
|||
correction_matrix_local = axis_basis_change @ bone.bone.matrix_local
|
||||
else:
|
||||
correction_matrix_local = (
|
||||
bone.parent.bone.matrix_local.inverted() @
|
||||
bone.parent.bone.matrix_local.inverted_safe() @
|
||||
bone.bone.matrix_local
|
||||
)
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ def gather_joint(blender_object, blender_bone, export_settings):
|
|||
correction_matrix_local = axis_basis_change @ blender_bone.bone.matrix_local
|
||||
else:
|
||||
correction_matrix_local = (
|
||||
blender_bone.parent.bone.matrix_local.inverted() @
|
||||
blender_bone.parent.bone.matrix_local.inverted_safe() @
|
||||
blender_bone.bone.matrix_local
|
||||
)
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ def gather_node(blender_object, library, blender_scene, dupli_object_parent, exp
|
|||
|
||||
@cached
|
||||
def __gather_node(blender_object, library, blender_scene, dupli_object_parent, export_settings):
|
||||
children = __gather_children(blender_object, blender_scene, export_settings)
|
||||
children, only_bone_children = __gather_children(blender_object, blender_scene, export_settings)
|
||||
|
||||
camera = None
|
||||
mesh = None
|
||||
|
@ -64,6 +64,12 @@ def __gather_node(blender_object, library, blender_scene, dupli_object_parent, e
|
|||
# This node should be filtered out, but has un-filtered children present.
|
||||
# So, export this node, excluding its camera, mesh, skin, and weights.
|
||||
# The transformations and animations on this node will have visible effects on children.
|
||||
|
||||
# Armature always have children node(s) (that are bone(s))
|
||||
# We have to check if children are only bones or not for armatures
|
||||
if blender_object.type == "ARMATURE" and only_bone_children is True:
|
||||
return None
|
||||
|
||||
pass
|
||||
else:
|
||||
# This node is filtered out, and has no un-filtered children or descendants.
|
||||
|
@ -158,6 +164,7 @@ def __gather_camera(blender_object, export_settings):
|
|||
|
||||
def __gather_children(blender_object, blender_scene, export_settings):
|
||||
children = []
|
||||
only_bone_children = True # True by default, will be set to False if needed
|
||||
# standard children
|
||||
for _child_object in blender_object.children:
|
||||
if _child_object.parent_bone:
|
||||
|
@ -173,6 +180,7 @@ def __gather_children(blender_object, blender_scene, export_settings):
|
|||
blender_scene, None, export_settings)
|
||||
if node is not None:
|
||||
children.append(node)
|
||||
only_bone_children = False
|
||||
# blender dupli objects
|
||||
if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection:
|
||||
for dupli_object in blender_object.instance_collection.objects:
|
||||
|
@ -185,6 +193,7 @@ def __gather_children(blender_object, blender_scene, export_settings):
|
|||
blender_scene, blender_object.name, export_settings)
|
||||
if node is not None:
|
||||
children.append(node)
|
||||
only_bone_children = False
|
||||
|
||||
# blender bones
|
||||
if blender_object.type == "ARMATURE":
|
||||
|
@ -201,6 +210,8 @@ def __gather_children(blender_object, blender_scene, export_settings):
|
|||
root_joints.append(joint)
|
||||
# handle objects directly parented to bones
|
||||
direct_bone_children = [child for child in blender_object.children if child.parent_bone]
|
||||
if len(direct_bone_children) != 0:
|
||||
only_bone_children = False
|
||||
def find_parent_joint(joints, name):
|
||||
for joint in joints:
|
||||
if joint.name == name:
|
||||
|
@ -246,7 +257,7 @@ def __gather_children(blender_object, blender_scene, export_settings):
|
|||
|
||||
parent_joint.children.append(child_node)
|
||||
|
||||
return children
|
||||
return children, only_bone_children
|
||||
|
||||
|
||||
def __gather_extensions(blender_object, export_settings):
|
||||
|
@ -292,6 +303,9 @@ def __gather_mesh(blender_object, library, export_settings):
|
|||
if blender_object.type != "MESH":
|
||||
return None
|
||||
|
||||
# Be sure that object is valid (no NaN for example)
|
||||
blender_object.data.validate()
|
||||
|
||||
# If not using vertex group, they are irrelevant for caching --> ensure that they do not trigger a cache miss
|
||||
vertex_groups = blender_object.vertex_groups
|
||||
modifiers = blender_object.modifiers
|
||||
|
|
|
@ -90,7 +90,7 @@ def __gather_inverse_bind_matrices(blender_object, export_settings):
|
|||
blender_object.matrix_world @
|
||||
bone.bone.matrix_local
|
||||
)
|
||||
).inverted()
|
||||
).inverted_safe()
|
||||
matrices.append(inverse_bind_matrix)
|
||||
|
||||
if export_settings['gltf_def_bones'] is False:
|
||||
|
|
|
@ -184,6 +184,10 @@ class BlenderNode():
|
|||
@staticmethod
|
||||
def create_mesh_object(gltf, vnode):
|
||||
pynode = gltf.data.nodes[vnode.mesh_node_idx]
|
||||
if not (0 <= pynode.mesh < len(gltf.data.meshes)):
|
||||
# Avoid traceback for invalid gltf file: invalid reference to meshes array
|
||||
# So return an empty blender object)
|
||||
return bpy.data.objects.new(vnode.name or mesh.name, None)
|
||||
pymesh = gltf.data.meshes[pynode.mesh]
|
||||
|
||||
# Key to cache the Blender mesh by.
|
||||
|
|
|
@ -1533,7 +1533,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1545,7 +1544,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1557,7 +1555,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="0"
|
||||
|
|
|
@ -1454,7 +1454,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="12"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="1"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1466,7 +1465,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1478,7 +1476,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="0"
|
||||
|
|
|
@ -1533,7 +1533,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="1"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1545,7 +1544,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="10"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1557,7 +1555,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="10"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
|
|
@ -1454,7 +1454,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="1"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1466,7 +1465,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1478,7 +1476,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="10"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="1"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
|
|
@ -1533,7 +1533,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="12"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1545,7 +1544,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1557,7 +1555,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="1"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
|
|
@ -1454,7 +1454,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="12"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1466,7 +1465,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1478,7 +1476,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
|
|
@ -1454,7 +1454,6 @@
|
|||
<panel_title>
|
||||
<ThemeFontStyle
|
||||
points="12"
|
||||
font_kerning_style="FITTED"
|
||||
shadow="2"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1466,7 +1465,6 @@
|
|||
<widget_label>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="UNFITTED"
|
||||
shadow="3"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="-1"
|
||||
|
@ -1478,7 +1476,6 @@
|
|||
<widget>
|
||||
<ThemeFontStyle
|
||||
points="11"
|
||||
font_kerning_style="UNFITTED"
|
||||
shadow="0"
|
||||
shadow_offset_x="0"
|
||||
shadow_offset_y="0"
|
||||
|
|
|
@ -140,6 +140,11 @@ from bpy.props import (
|
|||
)
|
||||
|
||||
|
||||
def get_generator():
|
||||
"""Returns the currently active generator instance."""
|
||||
return base_generate.BaseGenerator.instance
|
||||
|
||||
|
||||
class RigifyFeatureSets(bpy.types.PropertyGroup):
|
||||
name: bpy.props.StringProperty()
|
||||
module_name: bpy.props.StringProperty()
|
||||
|
@ -525,6 +530,10 @@ def register():
|
|||
description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created",
|
||||
default=False)
|
||||
|
||||
bpy.types.Armature.rigify_mirror_widgets = BoolProperty(name="Mirror Widgets",
|
||||
description="Make widgets for left and right side bones linked duplicates with negative X scale for the right side, based on bone name symmetry",
|
||||
default=True)
|
||||
|
||||
bpy.types.Armature.rigify_target_rig = PointerProperty(type=bpy.types.Object,
|
||||
name="Rigify Target Rig",
|
||||
description="Defines which rig to overwrite. If unset, a new one called 'rig' will be created",
|
||||
|
@ -534,6 +543,10 @@ def register():
|
|||
name="Rigify Target Rig UI",
|
||||
description="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used")
|
||||
|
||||
bpy.types.Armature.rigify_finalize_script = PointerProperty(type=bpy.types.Text,
|
||||
name="Finalize Script",
|
||||
description="Run this script after generation to apply user-specific changes")
|
||||
|
||||
bpy.types.Armature.rigify_rig_basename = StringProperty(name="Rigify Rig Name",
|
||||
description="Defines the name of the Rig. If unset, in 'new' mode 'rig' will be used, in 'overwrite' mode the target rig name will be used",
|
||||
default="")
|
||||
|
|
|
@ -189,6 +189,8 @@ class LegacyRig(base_rig.BaseRig):
|
|||
class BaseGenerator:
|
||||
"""Base class for the main generator object. Contains rig and plugin management code."""
|
||||
|
||||
instance = None
|
||||
|
||||
def __init__(self, context, metarig):
|
||||
self.context = context
|
||||
self.scene = context.scene
|
||||
|
|
|
@ -25,7 +25,7 @@ import time
|
|||
from .utils.errors import MetarigError
|
||||
from .utils.bones import new_bone
|
||||
from .utils.layers import ORG_LAYER, MCH_LAYER, DEF_LAYER, ROOT_LAYER
|
||||
from .utils.naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name
|
||||
from .utils.naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name, change_name_side, get_name_side, Side
|
||||
from .utils.widgets import WGT_PREFIX
|
||||
from .utils.widgets_special import create_root_widget
|
||||
from .utils.mechanism import refresh_all_drivers
|
||||
|
@ -165,27 +165,44 @@ class Generator(base_generate.BaseGenerator):
|
|||
for obj in list(old_collection.objects):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
# Rename widgets and collection if renaming
|
||||
if self.rig_old_name:
|
||||
old_prefix = WGT_PREFIX + self.rig_old_name + "_"
|
||||
new_prefix = WGT_PREFIX + self.obj.name + "_"
|
||||
|
||||
for obj in list(old_collection.objects):
|
||||
if obj.name.startswith(old_prefix):
|
||||
new_name = new_prefix + obj.name[len(old_prefix):]
|
||||
elif obj.name == wgts_group_name:
|
||||
new_name = new_group_name
|
||||
else:
|
||||
continue
|
||||
|
||||
obj.data.name = new_name
|
||||
obj.name = new_name
|
||||
|
||||
old_collection.name = new_group_name
|
||||
# Rename the collection
|
||||
old_collection.name = new_group_name
|
||||
|
||||
# Create/find widget collection
|
||||
self.widget_collection = ensure_widget_collection(self.context, new_group_name)
|
||||
self.wgts_group_name = new_group_name
|
||||
self.use_mirror_widgets = self.metarig.data.rigify_mirror_widgets
|
||||
|
||||
# Build tables for existing widgets
|
||||
self.old_widget_table = {}
|
||||
self.new_widget_table = {}
|
||||
self.widget_mirror_mesh = {}
|
||||
|
||||
if not self.metarig.data.rigify_force_widget_update and self.obj.pose:
|
||||
# Find all widgets from the collection referenced by the old rig
|
||||
known_widgets = set(obj.name for obj in self.widget_collection.objects)
|
||||
|
||||
for bone in self.obj.pose.bones:
|
||||
if bone.custom_shape and bone.custom_shape.name in known_widgets:
|
||||
self.old_widget_table[bone.name] = bone.custom_shape
|
||||
|
||||
# Rename widgets in case the rig was renamed
|
||||
name_prefix = WGT_PREFIX + self.obj.name + "_"
|
||||
|
||||
for bone_name, widget in self.old_widget_table.items():
|
||||
old_data_name = change_name_side(widget.name, get_name_side(widget.data.name))
|
||||
|
||||
widget.name = name_prefix + bone_name
|
||||
|
||||
# If the mesh name is the same as the object, rename it too
|
||||
if widget.data.name == old_data_name:
|
||||
widget.data.name = change_name_side(widget.name, get_name_side(widget.data.name))
|
||||
|
||||
# Find meshes for mirroring
|
||||
if self.use_mirror_widgets:
|
||||
for bone_name, widget in self.old_widget_table.items():
|
||||
mid_name = change_name_side(bone_name, Side.MIDDLE)
|
||||
if bone_name != mid_name:
|
||||
self.widget_mirror_mesh[mid_name] = widget.data
|
||||
|
||||
|
||||
def __duplicate_rig(self):
|
||||
|
@ -373,6 +390,11 @@ class Generator(base_generate.BaseGenerator):
|
|||
# Assign shapes to bones
|
||||
# Object's with name WGT-<bone_name> get used as that bone's shape.
|
||||
for bone in self.obj.pose.bones:
|
||||
# First check the table built by create_widget
|
||||
if bone.name in self.new_widget_table:
|
||||
bone.custom_shape = self.new_widget_table[bone.name]
|
||||
continue
|
||||
|
||||
# Object names are limited to 63 characters... arg
|
||||
wgt_name = (WGT_PREFIX + self.obj.name + '_' + bone.name)[:63]
|
||||
|
||||
|
@ -573,6 +595,14 @@ class Generator(base_generate.BaseGenerator):
|
|||
# Clear any transient errors in drivers
|
||||
refresh_all_drivers()
|
||||
|
||||
#----------------------------------
|
||||
# Execute the finalize script
|
||||
|
||||
if metarig.data.rigify_finalize_script:
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
exec(metarig.data.rigify_finalize_script.as_string(), {})
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
#----------------------------------
|
||||
# Restore active collection
|
||||
view_layer.active_layer_collection = self.layer_collection
|
||||
|
@ -587,7 +617,11 @@ def generate_rig(context, metarig):
|
|||
metarig.data.pose_position = 'REST'
|
||||
|
||||
try:
|
||||
Generator(context, metarig).generate()
|
||||
generator = Generator(context, metarig)
|
||||
|
||||
base_generate.BaseGenerator.instance = generator
|
||||
|
||||
generator.generate()
|
||||
|
||||
metarig.data.pose_position = rest_backup
|
||||
|
||||
|
@ -601,6 +635,9 @@ def generate_rig(context, metarig):
|
|||
# Continue the exception
|
||||
raise e
|
||||
|
||||
finally:
|
||||
base_generate.BaseGenerator.instance = None
|
||||
|
||||
|
||||
def create_selection_set_for_rig_layer(
|
||||
rig: bpy.types.Object,
|
||||
|
|
|
@ -97,7 +97,7 @@ def create_ikarrow_widget(rig, bone_name, size=1.0, bone_transform_name=None, ro
|
|||
|
||||
def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
|
||||
# Create hand widget
|
||||
obj = create_widget(rig, bone_name, bone_transform_name)
|
||||
obj = create_widget(rig, bone_name, bone_transform_name, subsurf=2)
|
||||
if obj is not None:
|
||||
verts = [(0.0*size, 1.5*size, -0.7000000476837158*size), (1.1920928955078125e-07*size, -0.25*size, -0.6999999284744263*size), (0.0*size, -0.25*size, 0.7000000476837158*size), (-1.1920928955078125e-07*size, 1.5*size, 0.6999999284744263*size), (5.960464477539063e-08*size, 0.7229999899864197*size, -0.699999988079071*size), (-5.960464477539063e-08*size, 0.7229999899864197*size, 0.699999988079071*size), (1.1920928955078125e-07*size, -2.9802322387695312e-08*size, -0.699999988079071*size), (0.0*size, 2.9802322387695312e-08*size, 0.699999988079071*size), ]
|
||||
edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
|
||||
|
@ -107,8 +107,6 @@ def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
|
|||
mesh.from_pydata(verts, edges, faces)
|
||||
mesh.update()
|
||||
|
||||
mod = obj.modifiers.new("subsurf", 'SUBSURF')
|
||||
mod.levels = 2
|
||||
return obj
|
||||
else:
|
||||
return None
|
||||
|
@ -116,7 +114,7 @@ def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
|
|||
|
||||
def create_foot_widget(rig, bone_name, size=1.0, bone_transform_name=None):
|
||||
# Create hand widget
|
||||
obj = create_widget(rig, bone_name, bone_transform_name)
|
||||
obj = create_widget(rig, bone_name, bone_transform_name, subsurf=2)
|
||||
if obj is not None:
|
||||
verts = [(-0.6999998688697815*size, -0.5242648720741272*size, 0.0*size), (-0.7000001072883606*size, 1.2257349491119385*size, 0.0*size), (0.6999998688697815*size, 1.2257351875305176*size, 0.0*size), (0.7000001072883606*size, -0.5242648720741272*size, 0.0*size), (-0.6999998688697815*size, 0.2527350187301636*size, 0.0*size), (0.7000001072883606*size, 0.2527352571487427*size, 0.0*size), (-0.7000001072883606*size, 0.975735068321228*size, 0.0*size), (0.6999998688697815*size, 0.9757352471351624*size, 0.0*size), ]
|
||||
edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7), ]
|
||||
|
@ -126,8 +124,6 @@ def create_foot_widget(rig, bone_name, size=1.0, bone_transform_name=None):
|
|||
mesh.from_pydata(verts, edges, faces)
|
||||
mesh.update()
|
||||
|
||||
mod = obj.modifiers.new("subsurf", 'SUBSURF')
|
||||
mod.levels = 2
|
||||
return obj
|
||||
else:
|
||||
return None
|
||||
|
|
|
@ -177,6 +177,9 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
|
|||
if armature_id_store.rigify_generate_mode == 'new':
|
||||
row.enabled = False
|
||||
|
||||
col.prop(armature_id_store, "rigify_mirror_widgets")
|
||||
col.prop(armature_id_store, "rigify_finalize_script", text="Run Script")
|
||||
|
||||
elif obj.mode == 'EDIT':
|
||||
# Build types list
|
||||
build_type_list(context, id_store.rigify_types)
|
||||
|
|
|
@ -28,6 +28,7 @@ from itertools import count
|
|||
|
||||
from .errors import MetarigError
|
||||
from .collections import ensure_widget_collection
|
||||
from .naming import change_name_side, get_name_side, Side
|
||||
|
||||
WGT_PREFIX = "WGT-" # Prefix for widget objects
|
||||
|
||||
|
@ -56,47 +57,113 @@ def obj_to_bone(obj, rig, bone_name, bone_transform_name=None):
|
|||
elif bone.custom_shape_transform:
|
||||
bone = bone.custom_shape_transform
|
||||
|
||||
shape_mat = Matrix.Translation(loc) @ (Euler(rot).to_matrix() @ Matrix.Diagonal(scale)).to_4x4()
|
||||
shape_mat = Matrix.LocRotScale(loc, Euler(rot), scale)
|
||||
|
||||
obj.rotation_mode = 'XYZ'
|
||||
obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ shape_mat
|
||||
|
||||
|
||||
def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False):
|
||||
def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False, subsurf=0):
|
||||
""" Creates an empty widget object for a bone, and returns the object.
|
||||
"""
|
||||
assert rig.mode != 'EDIT'
|
||||
|
||||
obj_name = widget_name or WGT_PREFIX + rig.name + '_' + bone_name
|
||||
from ..base_generate import BaseGenerator
|
||||
|
||||
scene = bpy.context.scene
|
||||
collection = ensure_widget_collection(bpy.context, 'WGTS_' + rig.name)
|
||||
bone = rig.pose.bones[bone_name]
|
||||
|
||||
# Access the current generator instance when generating (ugh, globals)
|
||||
generator = BaseGenerator.instance
|
||||
|
||||
if generator:
|
||||
collection = generator.widget_collection
|
||||
else:
|
||||
collection = ensure_widget_collection(bpy.context, 'WGTS_' + rig.name)
|
||||
|
||||
use_mirror = generator and generator.use_mirror_widgets
|
||||
|
||||
if use_mirror:
|
||||
bone_mid_name = change_name_side(bone_name, Side.MIDDLE)
|
||||
|
||||
obj_name = widget_name or WGT_PREFIX + rig.name + '_' + bone_name
|
||||
reuse_mesh = None
|
||||
|
||||
# Check if it already exists in the scene
|
||||
if not widget_force_new:
|
||||
if obj_name in scene.objects:
|
||||
obj = None
|
||||
|
||||
if generator:
|
||||
# Check if the widget was already generated
|
||||
if bone_name in generator.new_widget_table:
|
||||
return None
|
||||
|
||||
# If re-generating, check widgets used by the previous rig
|
||||
obj = generator.old_widget_table.get(bone_name)
|
||||
|
||||
if not obj:
|
||||
# Search the scene by name
|
||||
obj = scene.objects.get(obj_name)
|
||||
|
||||
if obj:
|
||||
# Record the generated widget
|
||||
if generator:
|
||||
generator.new_widget_table[bone_name] = obj
|
||||
|
||||
# Re-add to the collection if not there for some reason
|
||||
if obj.name not in collection.objects:
|
||||
collection.objects.link(obj)
|
||||
|
||||
# Flip scale for originally mirrored widgets
|
||||
if obj.scale.x < 0 and bone.custom_shape_scale_xyz.x > 0:
|
||||
bone.custom_shape_scale_xyz.x *= -1
|
||||
|
||||
# Move object to bone position, in case it changed
|
||||
obj = scene.objects[obj_name]
|
||||
obj_to_bone(obj, rig, bone_name, bone_transform_name)
|
||||
|
||||
return None
|
||||
|
||||
# Delete object if it exists in blend data but not scene data.
|
||||
# This is necessary so we can then create the object without
|
||||
# name conflicts.
|
||||
if obj_name in bpy.data.objects:
|
||||
bpy.data.objects.remove(bpy.data.objects[obj_name])
|
||||
|
||||
# Create a linked duplicate of the widget assigned in the metarig
|
||||
reuse_widget = rig.pose.bones[bone_name].custom_shape
|
||||
if reuse_widget:
|
||||
subsurf = 0
|
||||
reuse_mesh = reuse_widget.data
|
||||
|
||||
# Create mesh object
|
||||
mesh = reuse_mesh or bpy.data.meshes.new(obj_name)
|
||||
# Create a linked duplicate with the mirror widget
|
||||
if not reuse_mesh and use_mirror and bone_mid_name != bone_name:
|
||||
reuse_mesh = generator.widget_mirror_mesh.get(bone_mid_name)
|
||||
|
||||
# Create an empty mesh datablock if not linking
|
||||
if reuse_mesh:
|
||||
mesh = reuse_mesh
|
||||
|
||||
elif use_mirror and bone_mid_name != bone_name:
|
||||
# When mirroring, untag side from mesh name, and remember it
|
||||
mesh = bpy.data.meshes.new(change_name_side(obj_name, Side.MIDDLE))
|
||||
|
||||
generator.widget_mirror_mesh[bone_mid_name] = mesh
|
||||
|
||||
else:
|
||||
mesh = bpy.data.meshes.new(obj_name)
|
||||
|
||||
# Create the object
|
||||
obj = bpy.data.objects.new(obj_name, mesh)
|
||||
collection.objects.link(obj)
|
||||
|
||||
# Add the subdivision surface modifier
|
||||
if subsurf > 0:
|
||||
mod = obj.modifiers.new("subsurf", 'SUBSURF')
|
||||
mod.levels = subsurf
|
||||
|
||||
# Record the generated widget
|
||||
if generator:
|
||||
generator.new_widget_table[bone_name] = obj
|
||||
|
||||
# Flip scale for right side if mirroring widgets
|
||||
if use_mirror and get_name_side(bone_name) == Side.RIGHT:
|
||||
if bone.custom_shape_scale_xyz.x > 0:
|
||||
bone.custom_shape_scale_xyz.x *= -1
|
||||
|
||||
# Move object to bone position and set layers
|
||||
obj_to_bone(obj, rig, bone_name, bone_transform_name)
|
||||
|
||||
|
@ -187,7 +254,7 @@ def widget_generator(generate_func=None, *, register=None, subsurf=0):
|
|||
"""
|
||||
@functools.wraps(generate_func)
|
||||
def wrapper(rig, bone_name, bone_transform_name=None, widget_name=None, widget_force_new=False, **kwargs):
|
||||
obj = create_widget(rig, bone_name, bone_transform_name, widget_name=widget_name, widget_force_new=widget_force_new)
|
||||
obj = create_widget(rig, bone_name, bone_transform_name, widget_name=widget_name, widget_force_new=widget_force_new, subsurf=subsurf)
|
||||
if obj is not None:
|
||||
geom = GeometryData()
|
||||
|
||||
|
@ -197,10 +264,6 @@ def widget_generator(generate_func=None, *, register=None, subsurf=0):
|
|||
mesh.from_pydata(geom.verts, geom.edges, geom.faces)
|
||||
mesh.update()
|
||||
|
||||
if subsurf:
|
||||
mod = obj.modifiers.new("subsurf", 'SUBSURF')
|
||||
mod.levels = subsurf
|
||||
|
||||
return obj
|
||||
else:
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue