Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--add_curve_extra_objects/__init__.py8
-rw-r--r--add_curve_extra_objects/add_curve_simple.py28
-rw-r--r--add_curve_ivygen.py4
-rw-r--r--add_curve_sapling/__init__.py4
-rw-r--r--ant_landscape/__init__.py4
-rw-r--r--btrace/__init__.py3
-rw-r--r--curve_assign_shapekey.py36
-rw-r--r--curve_simplify.py4
-rw-r--r--curve_tools/__init__.py7
-rw-r--r--curve_tools/cad.py12
-rw-r--r--curve_tools/operators.py129
-rw-r--r--curve_tools/path_finder.py4
-rw-r--r--curve_tools/remove_doubles.py2
-rw-r--r--io_mesh_ply/__init__.py51
-rw-r--r--io_mesh_ply/export_ply.py248
-rw-r--r--io_mesh_ply/import_ply.py117
-rw-r--r--io_mesh_stl/__init__.py183
-rw-r--r--io_mesh_stl/blender_utils.py10
-rw-r--r--io_mesh_stl/stl_utils.py26
-rwxr-xr-xio_scene_gltf2/__init__.py49
-rw-r--r--io_scene_gltf2/blender/com/gltf2_blender_extras.py91
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_extract.py207
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather.py12
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channel_target.py9
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py10
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py4
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_cameras.py4
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py12
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py3
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py4
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py4
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py11
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_skins.py71
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_camera.py2
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_light.py4
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_material.py4
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_mesh.py3
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_node.py5
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_skin.py7
-rwxr-xr-xio_scene_gltf2/io/imp/gltf2_io_binary.py73
-rwxr-xr-xio_scene_gltf2/io/imp/gltf2_io_gltf.py95
-rw-r--r--measureit/__init__.py4
-rw-r--r--mesh_bsurfaces.py500
-rw-r--r--mesh_looptools.py14
-rw-r--r--mesh_snap_utilities_line/common_utilities.py2
-rw-r--r--mesh_snap_utilities_line/preferences.py2
-rw-r--r--mesh_snap_utilities_line/snap_context_l/__init__.py6
-rw-r--r--node_wrangler.py2
-rw-r--r--object_print3d_utils/__init__.py8
-rw-r--r--object_print3d_utils/export.py80
-rw-r--r--object_print3d_utils/mesh_helpers.py62
-rw-r--r--object_print3d_utils/operators.py1
-rw-r--r--object_print3d_utils/ui.py1
-rw-r--r--object_scatter/operator.py6
-rw-r--r--object_scatter/ui.py11
-rw-r--r--render_povray/__init__.py61
-rw-r--r--render_povray/render.py1
-rw-r--r--render_povray/ui.py130
-rw-r--r--rigify/__init__.py37
-rw-r--r--rigify/base_rig.py10
-rw-r--r--rigify/generate.py46
-rw-r--r--rigify/metarigs/Animals/bird.py189
-rw-r--r--rigify/metarigs/Animals/cat.py147
-rw-r--r--rigify/metarigs/Animals/horse.py192
-rw-r--r--rigify/metarigs/Animals/shark.py277
-rw-r--r--rigify/metarigs/Animals/wolf.py155
-rw-r--r--rigify/metarigs/Basic/basic_human.py19
-rw-r--r--rigify/metarigs/Basic/basic_quadruped.py123
-rw-r--r--rigify/metarigs/human.py43
-rw-r--r--rigify/rig_ui_template.py30
-rw-r--r--rigify/rigs/basic/copy_chain.py33
-rw-r--r--rigify/rigs/basic/pivot.py236
-rw-r--r--rigify/rigs/basic/super_copy.py108
-rw-r--r--rigify/rigs/chain_rigs.py6
-rw-r--r--rigify/rigs/experimental/super_chain.py8
-rw-r--r--rigify/rigs/limbs/arm.py69
-rw-r--r--rigify/rigs/limbs/leg.py190
-rw-r--r--rigify/rigs/limbs/limb_rigs.py97
-rw-r--r--rigify/rigs/limbs/paw.py8
-rw-r--r--rigify/rigs/limbs/simple_tentacle.py2
-rw-r--r--rigify/rigs/limbs/super_finger.py42
-rw-r--r--rigify/rigs/limbs/super_palm.py3
-rw-r--r--rigify/rigs/spines/basic_spine.py145
-rw-r--r--rigify/rigs/spines/basic_tail.py7
-rw-r--r--rigify/rigs/spines/spine_rigs.py60
-rw-r--r--rigify/rigs/spines/super_head.py5
-rw-r--r--rigify/rigs/spines/super_spine.py4
-rw-r--r--rigify/ui.py43
-rw-r--r--rigify/utils/animation.py212
-rw-r--r--rigify/utils/bones.py14
-rw-r--r--rigify/utils/components.py87
-rw-r--r--rigify/utils/layers.py13
-rw-r--r--rigify/utils/misc.py8
-rw-r--r--rigify/utils/rig.py1
-rw-r--r--rigify/utils/switch_parent.py118
-rw-r--r--rigify/utils/widgets_basic.py2
-rw-r--r--space_view3d_3d_navigation.py4
-rw-r--r--space_view3d_brush_menus/__init__.py5
-rw-r--r--space_view3d_brush_menus/brushes.py3
-rw-r--r--space_view3d_math_vis/__init__.py4
-rw-r--r--space_view3d_stored_views/__init__.py4
101 files changed, 3350 insertions, 1914 deletions
diff --git a/add_curve_extra_objects/__init__.py b/add_curve_extra_objects/__init__.py
index fbb0838b..e5a1bbcd 100644
--- a/add_curve_extra_objects/__init__.py
+++ b/add_curve_extra_objects/__init__.py
@@ -28,8 +28,8 @@ bl_info = {
"location": "View3D > Add > Curve > Extra Objects",
"description": "Add extra curve object types",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Curve_Objects",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/extra_objects.html",
"category": "Add Curve"
}
@@ -247,9 +247,9 @@ def menu_func(self, context):
layout = self.layout
layout.operator_menu_enum("curve.curveaceous_galore", "ProfileType", icon='CURVE_DATA')
- layout.operator_menu_enum("curve.spirals", "spiral_type", icon='CURVE_DATA')
+ layout.operator_menu_enum("curve.spirals", "spiral_type", icon='FORCE_VORTEX')
layout.separator()
- layout.operator("curve.curlycurve", text="Curly Curve", icon='CURVE_DATA')
+ layout.operator("curve.curlycurve", text="Curly Curve", icon='GP_ONLY_SELECTED')
if context.mode != 'OBJECT':
# fix in D2142 will allow to work in EDIT_CURVE
return None
diff --git a/add_curve_extra_objects/add_curve_simple.py b/add_curve_extra_objects/add_curve_simple.py
index e0dc2807..c73cb623 100644
--- a/add_curve_extra_objects/add_curve_simple.py
+++ b/add_curve_extra_objects/add_curve_simple.py
@@ -817,60 +817,60 @@ def main(context, self, align_matrix, use_enter_edit_mode):
Curve.data.fill_mode = 'BOTH'
def menu(self, context):
- oper1 = self.layout.operator(Simple.bl_idname, text="Angle", icon="MOD_CURVE")
+ oper1 = self.layout.operator(Simple.bl_idname, text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
oper1.Simple_Type = "Angle"
oper1.use_cyclic_u = False
- oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_CURVE")
+ oper2 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_THICKNESS")
oper2.Simple_Type = "Arc"
oper2.use_cyclic_u = False
- oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="MOD_CURVE")
+ oper3 = self.layout.operator(Simple.bl_idname, text="Circle", icon="ANTIALIASED")
oper3.Simple_Type = "Circle"
oper3.use_cyclic_u = True
- oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="MOD_CURVE")
+ oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="DRIVER_DISTANCE")
oper4.Simple_Type = "Distance"
oper4.use_cyclic_u = False
- oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MOD_CURVE")
+ oper5 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MESH_TORUS")
oper5.Simple_Type = "Ellipse"
oper5.use_cyclic_u = True
- oper6 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_CURVE")
+ oper6 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_SIMPLIFY")
oper6.Simple_Type = "Line"
oper6.use_cyclic_u = False
oper6.shape = '3D'
- oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="MOD_CURVE")
+ oper7 = self.layout.operator(Simple.bl_idname, text="Point", icon="LAYER_ACTIVE")
oper7.Simple_Type = "Point"
oper7.use_cyclic_u = False
- oper8 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="MOD_CURVE")
+ oper8 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="SEQ_CHROMA_SCOPE")
oper8.Simple_Type = "Polygon"
oper8.use_cyclic_u = True
- oper9 = self.layout.operator(Simple.bl_idname, text="Polygon ab", icon="MOD_CURVE")
+ oper9 = self.layout.operator(Simple.bl_idname, text="Polygon ab", icon="SEQ_CHROMA_SCOPE")
oper9.Simple_Type = "Polygon_ab"
oper9.use_cyclic_u = True
- oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MOD_CURVE")
+ oper10 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MESH_PLANE")
oper10.Simple_Type = "Rectangle"
oper10.use_cyclic_u = True
- oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="MOD_CURVE")
+ oper11 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="DECORATE_ANIMATE")
oper11.Simple_Type = "Rhomb"
oper11.use_cyclic_u = True
- oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="MOD_CURVE")
+ oper12 = self.layout.operator(Simple.bl_idname, text="Sector", icon="CON_SHRINKWRAP")
oper12.Simple_Type = "Sector"
oper12.use_cyclic_u = True
- oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_CURVE")
+ oper13 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_SIMPLEDEFORM")
oper13.Simple_Type = "Segment"
oper13.use_cyclic_u = True
- oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_CURVE")
+ oper14 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_EDGESPLIT")
oper14.Simple_Type = "Trapezoid"
oper14.use_cyclic_u = True
diff --git a/add_curve_ivygen.py b/add_curve_ivygen.py
index 6359b7fb..f6f0f8a3 100644
--- a/add_curve_ivygen.py
+++ b/add_curve_ivygen.py
@@ -27,8 +27,8 @@ bl_info = {
"description": "Adds generated ivy to a mesh object starting "
"at the 3D cursor",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Ivy_Gen",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/ivy_gen.html",
"category": "Add Curve",
}
diff --git a/add_curve_sapling/__init__.py b/add_curve_sapling/__init__.py
index 3b9bdd09..554f13f3 100644
--- a/add_curve_sapling/__init__.py
+++ b/add_curve_sapling/__init__.py
@@ -26,8 +26,8 @@ bl_info = {
"description": ("Adds a parametric tree. The method is presented by "
"Jason Weber & Joseph Penn in their paper 'Creation and Rendering of "
"Realistic Trees'"),
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Sapling_Tree",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/sapling.html",
"category": "Add Curve"}
if "bpy" in locals():
diff --git a/ant_landscape/__init__.py b/ant_landscape/__init__.py
index 789f0a37..e2d43c57 100644
--- a/ant_landscape/__init__.py
+++ b/ant_landscape/__init__.py
@@ -27,8 +27,8 @@ bl_info = {
"location": "View3D > Sidebar > Create Tab",
"description": "Another Noise Tool: Landscape and Displace",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Add_Mesh/ANT_Landscape",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_mesh/ant_landscape.html",
"category": "Add Mesh",
}
diff --git a/btrace/__init__.py b/btrace/__init__.py
index d12fe9ea..55d49e44 100644
--- a/btrace/__init__.py
+++ b/btrace/__init__.py
@@ -25,7 +25,8 @@ bl_info = {
"location": "View3D > Sidebar > Create Tab",
"description": "Tools for converting/animating objects/particles into curves",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Btrace",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/btracer.html",
"category": "Add Curve"}
import bpy
diff --git a/curve_assign_shapekey.py b/curve_assign_shapekey.py
index 46a2b0d1..318e2b25 100644
--- a/curve_assign_shapekey.py
+++ b/curve_assign_shapekey.py
@@ -27,21 +27,29 @@ bl_info = {
"location": "View 3D > Sidebar > Edit Tab",
"description": "Assigns one or more Bezier curves as shape keys to another Bezier curve",
"category": "Add Curve",
- "wiki_url": "https://github.com/Shriinivas/assignshapekey/blob/master/README.md",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/assign_shape_keys.html",
"blender": (2, 80, 0),
}
+alignList = [('minX', 'Min X', 'Align vertices with Min X'),
+ ('maxX', 'Max X', 'Align vertices with Max X'),
+ ('minY', 'Min Y', 'Align vertices with Min Y'),
+ ('maxY', 'Max Y', 'Align vertices with Max Y'),
+ ('minZ', 'Min Z', 'Align vertices with Min Z'),
+ ('maxZ', 'Max Z', 'Align vertices with Max Z')]
+
matchList = [('vCnt', 'Vertex Count', 'Match by vertex count'),
('bbArea', 'Area', 'Match by surface area of the bounding box'), \
('bbHeight', 'Height', 'Match by bounding box height'), \
('bbWidth', 'Width', 'Match by bounding box width'),
('bbDepth', 'Depth', 'Match by bounding box depth'),
- ('minX', 'Min X', 'Match by bounding bon Min X'),
- ('maxX', 'Max X', 'Match by bounding bon Max X'),
- ('minY', 'Min Y', 'Match by bounding bon Min Y'),
- ('maxY', 'Max Y', 'Match by bounding bon Max Y'),
- ('minZ', 'Min Z', 'Match by bounding bon Min Z'),
- ('maxZ', 'Max Z', 'Match by bounding bon Max Z')]
+ ('minX', 'Min X', 'Match by bounding box Min X'),
+ ('maxX', 'Max X', 'Match by bounding box Max X'),
+ ('minY', 'Min Y', 'Match by bounding box Min Y'),
+ ('maxY', 'Max Y', 'Match by bounding box Max Y'),
+ ('minZ', 'Min Z', 'Match by bounding box Min Z'),
+ ('maxZ', 'Max Z', 'Match by bounding box Max Z')]
DEF_ERR_MARGIN = 0.0001
@@ -778,7 +786,7 @@ class AssignShapeKeysOp(Operator):
matchCri2 = params.matchCri2
matchCri3 = params.matchCri3
- alignBy = params.alignList
+ alignBy = params.alignCos
alignVal1 = params.alignVal1
alignVal2 = params.alignVal2
alignVal3 = params.alignVal3
@@ -999,20 +1007,20 @@ class AssignShapeKeyParams(bpy.types.PropertyGroup):
('localspace', 'Local Space', 'localspace')], \
description = 'Space that shape keys are evluated in')
- alignList : EnumProperty(name="Vertex Alignment", items = \
+ alignCos : EnumProperty(name="Vertex Alignment", items = \
[("-None-", 'Manual Alignment', "Align curve segments based on starting vertex"), \
('vertCo', 'Vertex Coordinates', 'Align curve segments based on vertex coordinates')], \
description = 'Start aligning the vertices of target and shape keys from',
default = '-None-')
alignVal1 : EnumProperty(name="Value 1",
- items = matchList, default = 'minX', description='First align criterion')
+ items = alignList, default = 'minX', description='First align criterion')
alignVal2 : EnumProperty(name="Value 2",
- items = matchList, default = 'maxY', description='Second align criterion')
+ items = alignList, default = 'maxY', description='Second align criterion')
alignVal3 : EnumProperty(name="Value 3",
- items = matchList, default = 'minZ', description='Third align criterion')
+ items = alignList, default = 'minZ', description='Third align criterion')
matchParts : EnumProperty(name="Match Parts", items = \
[("-None-", 'None', "Don't match parts"), \
@@ -1062,9 +1070,9 @@ class AssignShapeKeysPanel(Panel):
row.prop(params, "space")
row = col.row()
- row.prop(params, "alignList")
+ row.prop(params, "alignCos")
- if(params.alignList == 'vertCo'):
+ if(params.alignCos == 'vertCo'):
row = col.row()
row.prop(params, "alignVal1")
row.prop(params, "alignVal2")
diff --git a/curve_simplify.py b/curve_simplify.py
index 2f6d1a8f..136eebea 100644
--- a/curve_simplify.py
+++ b/curve_simplify.py
@@ -24,8 +24,8 @@ bl_info = {
"location": "3D View, Dopesheet & Graph Editors",
"description": "Simplify Curves: 3dview, Dopesheet, Graph. Distance Merge: 3d view curve edit",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Curve_Simplify",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/simplify_curves.html",
"category": "Add Curve",
}
diff --git a/curve_tools/__init__.py b/curve_tools/__init__.py
index 736e60fb..47297f43 100644
--- a/curve_tools/__init__.py
+++ b/curve_tools/__init__.py
@@ -25,12 +25,13 @@ bl_info = {
"name": "Curve Tools",
"description": "Adds some functionality for bezier/nurbs curve/surface modeling",
"author": "Mackraken",
- "version": (0, 4, 0),
+ "version": (0, 4, 3),
"blender": (2, 80, 0),
"location": "View3D > Tool Shelf > Edit Tab",
"warning": "WIP",
"wiki_url": "",
- "tracker_url": "",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "add_curve/curve_tools.html",
"category": "Add Curve"}
@@ -333,6 +334,8 @@ class VIEW3D_PT_CurvePanel(Panel):
row = col.row(align=True)
row.operator("curvetools.sep_outline", text="Separate Outline or selected")
row = col.row(align=True)
+ row.operator("curvetools.bezier_curve_boolean", text="2D Curve Boolean")
+ row = col.row(align=True)
row.operator("curvetools.bezier_points_fillet", text='Fillet')
row = col.row(align=True)
row.operator("curvetools.bezier_cad_handle_projection", text='Handle Projection')
diff --git a/curve_tools/cad.py b/curve_tools/cad.py
index 49ccf171..b7478450 100644
--- a/curve_tools/cad.py
+++ b/curve_tools/cad.py
@@ -66,7 +66,7 @@ class Boolean(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return util.Selected1OrMoreCurves()
+ return util.Selected1Curve()
def execute(self, context):
current_mode = bpy.context.object.mode
@@ -80,7 +80,7 @@ class Boolean(bpy.types.Operator):
if len(splines) != 2:
self.report({'WARNING'}, 'Invalid selection. Only work to selected two spline.')
return {'CANCELLED'}
- bpy.ops.curvetools.spline_type_set(type='BEZIER')
+ bpy.ops.curve.spline_type_set(type='BEZIER')
splineA = bpy.context.object.data.splines.active
splineB = splines[0] if (splines[1] == splineA) else splines[1]
if not internal.bezierBooleanGeometry(splineA, splineB, self.operation):
@@ -169,13 +169,13 @@ class MergeEnds(bpy.types.Operator):
points[0].handle_left = handle
points[0].co = new_co
- bpy.ops.curvetools.select_all(action='DESELECT')
+ bpy.ops.curve.select_all(action='DESELECT')
points[1].select_control_point = True
- bpy.ops.curvetools.delete()
+ bpy.ops.curve.delete()
selected_splines[0].bezier_points[-1 if is_last_point[0] else 0].select_control_point = True
selected_splines[1].bezier_points[-1 if is_last_point[1] else 0].select_control_point = True
- bpy.ops.curvetools.make_segment()
- bpy.ops.curvetools.select_all(action='DESELECT')
+ bpy.ops.curve.make_segment()
+ bpy.ops.curve.select_all(action='DESELECT')
return {'FINISHED'}
class Subdivide(bpy.types.Operator):
diff --git a/curve_tools/operators.py b/curve_tools/operators.py
index e4fe24dd..191aff08 100644
--- a/curve_tools/operators.py
+++ b/curve_tools/operators.py
@@ -13,6 +13,7 @@ from . import intersections
from . import util
from . import surfaces
from . import mathematics
+from . import internal
# 1 CURVE SELECTED
# ################
@@ -1020,6 +1021,133 @@ class SeparateOutline(bpy.types.Operator):
bpy.ops.curve.separate()
return {'FINISHED'}
+
+class CurveBoolean(bpy.types.Operator):
+ bl_idname = "curvetools.bezier_curve_boolean"
+ bl_description = "Curve Boolean"
+ bl_label = "Curve Boolean"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ operation: bpy.props.EnumProperty(name='Type', items=[
+ ('UNION', 'Union', 'Boolean OR', 0),
+ ('INTERSECTION', 'Intersection', 'Boolean AND', 1),
+ ('DIFFERENCE', 'Difference', 'Active minus Selected', 2),
+ ])
+ number : IntProperty(
+ name="Spline Number",
+ default=1,
+ min=1,
+ description="Spline Number"
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return util.Selected1OrMoreCurves()
+
+ def draw(self, context):
+ layout = self.layout
+
+ # general options
+ col = layout.column()
+ col.prop(self, "operation")
+ if self.operation == 'DIFFERENCE':
+ col.prop(self, "number")
+
+ def execute(self, context):
+ current_mode = bpy.context.object.mode
+
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+
+ selected_Curves = util.GetSelectedCurves()
+ len_selected_curves = len(selected_Curves)
+ if len_selected_curves < 2:
+ return {'FINISHED'}
+
+ min_number = 1
+
+ max_number = 0
+ for iCurve in range(0, len_selected_curves):
+ len_splines = len(selected_Curves[iCurve].data.splines)
+ max_number += len_splines
+
+ if self.number < min_number:
+ self.number = min_number
+ if self.number > max_number:
+ self.number = max_number
+
+ j = 0
+ first_curve = 0
+ first_spline = 0
+ for iCurve in range(0, len_selected_curves):
+ len_splines = len(selected_Curves[iCurve].data.splines)
+ for iSpline in range(0, len_splines):
+ if j == self.number:
+ first_curve = iCurve
+ first_spline = iSpline
+ j += 1
+
+ bpy.ops.object.select_all(action='DESELECT')
+
+ spline1 = selected_Curves[first_curve].data.splines[first_spline]
+ matrix_world1 = selected_Curves[first_curve].matrix_world
+
+ len_spline1 = len(spline1.bezier_points)
+
+ dataCurve = bpy.data.curves.new(self.operation, type='CURVE')
+ dataCurve.dimensions = '2D'
+ newSpline1 = dataCurve.splines.new(type='BEZIER')
+ newSpline1.use_cyclic_u = True
+ newSpline1.bezier_points.add(len_spline1 - 1)
+ for n in range(0, len_spline1):
+ newSpline1.bezier_points[n].co = matrix_world1 @ spline1.bezier_points[n].co
+ newSpline1.bezier_points[n].handle_left_type = spline1.bezier_points[n].handle_left_type
+ newSpline1.bezier_points[n].handle_left = matrix_world1 @ spline1.bezier_points[n].handle_left
+ newSpline1.bezier_points[n].handle_right_type = spline1.bezier_points[n].handle_right_type
+ newSpline1.bezier_points[n].handle_right = matrix_world1 @ spline1.bezier_points[n].handle_right
+
+ Curve = object_utils.object_data_add(context, dataCurve)
+ bpy.context.view_layer.objects.active = Curve
+ Curve.select_set(True)
+ Curve.location = (0.0, 0.0, 0.0)
+
+ j = 0
+ for iCurve in range(0, len_selected_curves):
+ matrix_world = selected_Curves[iCurve].matrix_world
+ len_splines = len(selected_Curves[iCurve].data.splines)
+ for iSpline in range(0, len_splines):
+ if iCurve == first_curve and iSpline == first_spline:
+ continue
+ spline = selected_Curves[iCurve].data.splines[iSpline]
+ len_spline = len(spline.bezier_points)
+ newSpline = dataCurve.splines.new(type='BEZIER')
+ newSpline.use_cyclic_u = True
+ newSpline.bezier_points.add(len_spline - 1)
+ for n in range(0, len_spline):
+ newSpline.bezier_points[n].co = matrix_world @ spline.bezier_points[n].co
+ newSpline.bezier_points[n].handle_left_type = spline.bezier_points[n].handle_left_type
+ newSpline.bezier_points[n].handle_left = matrix_world @ spline.bezier_points[n].handle_left
+ newSpline.bezier_points[n].handle_right_type = spline.bezier_points[n].handle_right_type
+ newSpline.bezier_points[n].handle_right = matrix_world @ spline.bezier_points[n].handle_right
+
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.curve.select_all(action='SELECT')
+ splines = internal.getSelectedSplines(True, True)
+ if len(splines) < 2:
+ continue
+ splineA = splines[0]
+ splineB = splines[1]
+ dataCurve.splines.active = newSpline1
+
+ if not internal.bezierBooleanGeometry(splineA, splineB, self.operation):
+ self.report({'WARNING'}, 'Invalid selection.')
+ return {'CANCELLED'}
+
+ j += 1
+
+ bpy.ops.object.mode_set (mode = current_mode)
+
+ return {'FINISHED'}
def register():
for cls in classes:
@@ -1053,4 +1181,5 @@ operators = [
CurveScaleReset,
Split,
SeparateOutline,
+ CurveBoolean,
]
diff --git a/curve_tools/path_finder.py b/curve_tools/path_finder.py
index 366f9bcc..30c673e0 100644
--- a/curve_tools/path_finder.py
+++ b/curve_tools/path_finder.py
@@ -96,7 +96,7 @@ def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thi
points = get_bezier_points(spline, matrix_world)
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
- batch = batch_for_shader(shader, 'LINES', {"pos": points})
+ batch = batch_for_shader(shader, 'POINTS', {"pos": points})
shader.bind()
shader.uniform_float("color", path_color)
@@ -109,7 +109,7 @@ def draw_points(self, context, spline, matrix_world, path_color, path_thickness)
points = get_points(spline, matrix_world)
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
- batch = batch_for_shader(shader, 'LINES', {"pos": points})
+ batch = batch_for_shader(shader, 'POINTS', {"pos": points})
shader.bind()
shader.uniform_float("color", path_color)
diff --git a/curve_tools/remove_doubles.py b/curve_tools/remove_doubles.py
index 151b19b0..cf955980 100644
--- a/curve_tools/remove_doubles.py
+++ b/curve_tools/remove_doubles.py
@@ -109,7 +109,7 @@ class CurveRemvDbs(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return util.Selected1Curve()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
removed=main(context, self.distance)
diff --git a/io_mesh_ply/__init__.py b/io_mesh_ply/__init__.py
index b89732fd..c9a79e1f 100644
--- a/io_mesh_ply/__init__.py
+++ b/io_mesh_ply/__init__.py
@@ -21,11 +21,10 @@
bl_info = {
"name": "Stanford PLY format",
"author": "Bruce Merry, Campbell Barton",
- "version": (1, 0, 0),
- "blender": (2, 81, 6),
+ "version": (1, 1, 0),
+ "blender": (2, 82, 0),
"location": "File > Import-Export",
- "description": "Import-Export PLY mesh data with UV's and vertex colors",
- "warning": "",
+ "description": "Import-Export PLY mesh data with UVs and vertex colors",
"wiki_url": "https://docs.blender.org/manual/en/latest/addons/io_mesh_ply.html",
"support": 'OFFICIAL',
"category": "Import-Export",
@@ -34,8 +33,6 @@ bl_info = {
# Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
# Contributors: Bruce Merry, Campbell Barton
-# To support reload properly, try to access a package var,
-# if it's there, reload everything
if "bpy" in locals():
import importlib
if "export_ply" in locals():
@@ -44,13 +41,11 @@ if "bpy" in locals():
importlib.reload(import_ply)
-import os
import bpy
from bpy.props import (
CollectionProperty,
StringProperty,
BoolProperty,
- EnumProperty,
FloatProperty,
)
from bpy_extras.io_utils import (
@@ -81,6 +76,8 @@ class ImportPLY(bpy.types.Operator, ImportHelper):
filter_glob: StringProperty(default="*.ply", options={'HIDDEN'})
def execute(self, context):
+ import os
+
paths = [os.path.join(self.directory, name.name)
for name in self.files]
if not paths:
@@ -96,14 +93,18 @@ class ImportPLY(bpy.types.Operator, ImportHelper):
@orientation_helper(axis_forward='Y', axis_up='Z')
class ExportPLY(bpy.types.Operator, ExportHelper):
- """Export a single object as a Stanford PLY with normals, """ \
- """colors and texture coordinates"""
bl_idname = "export_mesh.ply"
bl_label = "Export PLY"
+ bl_description = "Export as a Stanford PLY with normals, vertex colors and texture coordinates"
filename_ext = ".ply"
filter_glob: StringProperty(default="*.ply", options={'HIDDEN'})
+ use_selection: BoolProperty(
+ name="Selection Only",
+ description="Export selected objects only",
+ default=False,
+ )
use_mesh_modifiers: BoolProperty(
name="Apply Modifiers",
description="Apply Modifiers to the exported mesh",
@@ -136,10 +137,6 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
default=1.0,
)
- @classmethod
- def poll(cls, context):
- return context.active_object is not None
-
def execute(self, context):
from . import export_ply
@@ -169,6 +166,30 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
pass
+class PLY_PT_export_include(bpy.types.Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Include"
+ bl_parent_id = "FILE_PT_operator"
+
+ @classmethod
+ def poll(cls, context):
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ return operator.bl_idname == "EXPORT_MESH_OT_ply"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ layout.prop(operator, "use_selection")
+
+
class PLY_PT_export_transform(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@@ -233,6 +254,7 @@ def menu_func_export(self, context):
classes = (
ImportPLY,
ExportPLY,
+ PLY_PT_export_include,
PLY_PT_export_transform,
PLY_PT_export_geometry,
)
@@ -253,5 +275,6 @@ def unregister():
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
+
if __name__ == "__main__":
register()
diff --git a/io_mesh_ply/export_ply.py b/io_mesh_ply/export_ply.py
index db79e950..812aeb54 100644
--- a/io_mesh_ply/export_ply.py
+++ b/io_mesh_ply/export_ply.py
@@ -21,20 +21,12 @@
"""
This script exports Stanford PLY files from Blender. It supports normals,
colors, and texture coordinates per face or per vertex.
-Only one mesh can be exported at a time.
"""
-import bpy
-import os
-
-def save_mesh(
- filepath,
- mesh,
- use_normals=True,
- use_uv_coords=True,
- use_colors=True,
-):
+def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=True):
+ import os
+ import bpy
def rvec3d(v):
return round(v[0], 6), round(v[1], 6), round(v[2], 6)
@@ -42,47 +34,26 @@ def save_mesh(
def rvec2d(v):
return round(v[0], 6), round(v[1], 6)
- file = open(filepath, "w", encoding="utf8", newline="\n")
- fw = file.write
-
- has_uv = bool(mesh.uv_layers)
- has_vcol = bool(mesh.vertex_colors)
-
- if not has_uv:
+ if use_uv_coords and mesh.uv_layers:
+ active_uv_layer = mesh.uv_layers.active.data
+ else:
use_uv_coords = False
- if not has_vcol:
- use_colors = False
- if not use_uv_coords:
- has_uv = False
- if not use_colors:
- has_vcol = False
-
- if has_uv:
- active_uv_layer = mesh.uv_layers.active
- if not active_uv_layer:
- use_uv_coords = False
- has_uv = False
- else:
- active_uv_layer = active_uv_layer.data
-
- if has_vcol:
- active_col_layer = mesh.vertex_colors.active
- if not active_col_layer:
- use_colors = False
- has_vcol = False
- else:
- active_col_layer = active_col_layer.data
+ if use_colors and mesh.vertex_colors:
+ active_col_layer = mesh.vertex_colors.active.data
+ else:
+ use_colors = False
# in case
color = uvcoord = uvcoord_key = normal = normal_key = None
- mesh_verts = mesh.vertices # save a lookup
- ply_verts = [] # list of dictionaries
+ mesh_verts = mesh.vertices
# vdict = {} # (index, normal, uv) -> new index
vdict = [{} for i in range(len(mesh_verts))]
+ ply_verts = []
ply_faces = [[] for f in range(len(mesh.polygons))]
vert_count = 0
+
for i, f in enumerate(mesh.polygons):
smooth = not use_normals or f.use_smooth
@@ -90,12 +61,12 @@ def save_mesh(
normal = f.normal[:]
normal_key = rvec3d(normal)
- if has_uv:
+ if use_uv_coords:
uv = [
active_uv_layer[l].uv[:]
for l in range(f.loop_start, f.loop_start + f.loop_total)
]
- if has_vcol:
+ if use_colors:
col = [
active_col_layer[l].color[:]
for l in range(f.loop_start, f.loop_start + f.loop_total)
@@ -109,11 +80,11 @@ def save_mesh(
normal = v.normal[:]
normal_key = rvec3d(normal)
- if has_uv:
+ if use_uv_coords:
uvcoord = uv[j][0], uv[j][1]
uvcoord_key = rvec2d(uvcoord)
- if has_vcol:
+ if use_colors:
color = col[j]
color = (
int(color[0] * 255.0),
@@ -126,106 +97,141 @@ def save_mesh(
vdict_local = vdict[vidx]
pf_vidx = vdict_local.get(key) # Will be None initially
- if pf_vidx is None: # same as vdict_local.has_key(key)
+ if pf_vidx is None: # Same as vdict_local.has_key(key)
pf_vidx = vdict_local[key] = vert_count
ply_verts.append((vidx, normal, uvcoord, color))
vert_count += 1
pf.append(pf_vidx)
- fw("ply\n")
- fw("format ascii 1.0\n")
- fw("comment Created by Blender %s - "
- "www.blender.org, source file: %r\n" %
- (bpy.app.version_string, os.path.basename(bpy.data.filepath)))
-
- fw("element vertex %d\n" % len(ply_verts))
-
- fw("property float x\n"
- "property float y\n"
- "property float z\n")
-
- if use_normals:
- fw("property float nx\n"
- "property float ny\n"
- "property float nz\n")
- if use_uv_coords:
- fw("property float s\n"
- "property float t\n")
- if use_colors:
- fw("property uchar red\n"
- "property uchar green\n"
- "property uchar blue\n"
- "property uchar alpha\n")
-
- fw("element face %d\n" % len(mesh.polygons))
- fw("property list uchar uint vertex_indices\n")
- fw("end_header\n")
-
- for i, v in enumerate(ply_verts):
- fw("%.6f %.6f %.6f" % mesh_verts[v[0]].co[:]) # co
+ with open(filepath, "w", encoding="utf-8", newline="\n") as file:
+ fw = file.write
+
+ # Header
+ # ---------------------------
+
+ fw("ply\n")
+ fw("format ascii 1.0\n")
+ fw(
+ f"comment Created by Blender {bpy.app.version_string} - "
+ f"www.blender.org, source file: {os.path.basename(bpy.data.filepath)!r}\n"
+ )
+
+ fw(f"element vertex {len(ply_verts)}\n")
+ fw(
+ "property float x\n"
+ "property float y\n"
+ "property float z\n"
+ )
if use_normals:
- fw(" %.6f %.6f %.6f" % v[1]) # no
+ fw(
+ "property float nx\n"
+ "property float ny\n"
+ "property float nz\n"
+ )
if use_uv_coords:
- fw(" %.6f %.6f" % v[2]) # uv
+ fw(
+ "property float s\n"
+ "property float t\n"
+ )
if use_colors:
- fw(" %u %u %u %u" % v[3]) # col
- fw("\n")
-
- for pf in ply_faces:
- # fw(f"{len(pf)} {' '.join(str(x) for x in pf)}\n")
- fw("%d" % len(pf))
- for v in pf:
- fw(" %d" % v)
- fw("\n")
-
- file.close()
- print("writing %r done" % filepath)
+ fw(
+ "property uchar red\n"
+ "property uchar green\n"
+ "property uchar blue\n"
+ "property uchar alpha\n"
+ )
+
+ fw(f"element face {len(mesh.polygons)}\n")
+ fw("property list uchar uint vertex_indices\n")
+
+ fw("end_header\n")
+
+ # Vertex data
+ # ---------------------------
+
+ for i, v in enumerate(ply_verts):
+ fw("%.6f %.6f %.6f" % mesh_verts[v[0]].co[:])
+ if use_normals:
+ fw(" %.6f %.6f %.6f" % v[1])
+ if use_uv_coords:
+ fw(" %.6f %.6f" % v[2])
+ if use_colors:
+ fw(" %u %u %u %u" % v[3])
+ fw("\n")
+
+ # Face data
+ # ---------------------------
+
+ for pf in ply_faces:
+ fw(f"{len(pf)}")
+ for v in pf:
+ fw(f" {v}")
+ fw("\n")
+
+ print(f"Writing {filepath!r} done")
return {'FINISHED'}
def save(
- operator,
- context,
- filepath="",
- use_mesh_modifiers=True,
- use_normals=True,
- use_uv_coords=True,
- use_colors=True,
- global_matrix=None
+ operator,
+ context,
+ filepath="",
+ use_selection=False,
+ use_mesh_modifiers=True,
+ use_normals=True,
+ use_uv_coords=True,
+ use_colors=True,
+ global_matrix=None
):
- obj = context.active_object
-
- if global_matrix is None:
- from mathutils import Matrix
- global_matrix = Matrix()
+ import bpy
+ import bmesh
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
- mesh_owner_object = None
- if use_mesh_modifiers and obj.modifiers:
- depsgraph = context.evaluated_depsgraph_get()
- mesh_owner_object = obj.evaluated_get(depsgraph)
- mesh = mesh_owner_object.to_mesh()
+ if use_selection:
+ obs = context.selected_objects
else:
- mesh_owner_object = obj
- mesh = mesh_owner_object.to_mesh()
+ obs = context.scene.objects
- if not mesh:
- raise Exception("Error, could not get mesh data from active object")
+ depsgraph = context.evaluated_depsgraph_get()
+ bm = bmesh.new()
+
+ for ob in obs:
+ if use_mesh_modifiers:
+ ob_eval = ob.evaluated_get(depsgraph)
+ else:
+ ob_eval = ob
+
+ try:
+ me = ob_eval.to_mesh()
+ except RuntimeError:
+ continue
+
+ me.transform(ob.matrix_world)
+ bm.from_mesh(me)
+ ob_eval.to_mesh_clear()
+
+ mesh = bpy.data.meshes.new("TMP PLY EXPORT")
+ bm.to_mesh(mesh)
+ bm.free()
+
+ if global_matrix is not None:
+ mesh.transform(global_matrix)
- mesh.transform(global_matrix @ obj.matrix_world)
if use_normals:
mesh.calc_normals()
- ret = save_mesh(filepath, mesh,
- use_normals=use_normals,
- use_uv_coords=use_uv_coords,
- use_colors=use_colors,
- )
+ ret = save_mesh(
+ filepath,
+ mesh,
+ use_normals=use_normals,
+ use_uv_coords=use_uv_coords,
+ use_colors=use_colors,
+ )
- mesh_owner_object.to_mesh_clear()
+ bpy.data.meshes.remove(mesh)
return ret
diff --git a/io_mesh_ply/import_ply.py b/io_mesh_ply/import_ply.py
index 5da7f9d6..2bf91442 100644
--- a/io_mesh_ply/import_ply.py
+++ b/io_mesh_ply/import_ply.py
@@ -18,11 +18,8 @@
# <pep8 compliant>
-import re
-import struct
-
-class element_spec(object):
+class ElementSpec:
__slots__ = (
"name",
"count",
@@ -46,7 +43,7 @@ class element_spec(object):
return -1
-class property_spec(object):
+class PropertySpec:
__slots__ = (
"name",
"list_type",
@@ -59,14 +56,16 @@ class property_spec(object):
self.numeric_type = numeric_type
def read_format(self, format, count, num_type, stream):
+ import struct
+
if format == b'ascii':
if num_type == 's':
ans = []
for i in range(count):
s = stream[i]
if not (len(s) >= 2 and s.startswith(b'"') and s.endswith(b'"')):
- print('Invalid string', s)
- print('Note: ply_import.py does not handle whitespace in strings')
+ print("Invalid string", s)
+ print("Note: ply_import.py does not handle whitespace in strings")
return None
ans.append(s[1:-1])
stream[:count] = []
@@ -103,18 +102,18 @@ class property_spec(object):
return self.read_format(format, 1, self.numeric_type, stream)[0]
-class object_spec(object):
- __slots__ = ("specs",
- )
- 'A list of element_specs'
+class ObjectSpec:
+ __slots__ = ("specs",)
+
def __init__(self):
+ # A list of element_specs
self.specs = []
def load(self, format, stream):
return dict([(i.name, [i.load(format, stream) for j in range(i.count)]) for i in self.specs])
- '''
# Longhand for above LC
+ """
answer = {}
for i in self.specs:
answer[i.name] = []
@@ -123,10 +122,12 @@ class object_spec(object):
Blender.Window.DrawProgressBar(float(j) / i.count, 'Loading ' + i.name)
answer[i.name].append(i.load(format, stream))
return answer
- '''
+ """
def read(filepath):
+ import re
+
format = b''
texture = b''
version = b'1.0'
@@ -154,14 +155,14 @@ def read(filepath):
b'double': 'd',
b'string': 's',
}
- obj_spec = object_spec()
+ obj_spec = ObjectSpec()
invalid_ply = (None, None, None)
with open(filepath, 'rb') as plyf:
signature = plyf.readline()
if not signature.startswith(b'ply'):
- print('Signature line was invalid')
+ print("Signature line was invalid")
return invalid_ply
valid_header = False
@@ -178,7 +179,7 @@ def read(filepath):
continue
elif tokens[1] == b'TextureFile':
if len(tokens) < 4:
- print('Invalid texture line')
+ print("Invalid texture line")
else:
texture = tokens[2]
continue
@@ -187,34 +188,34 @@ def read(filepath):
continue
elif tokens[0] == b'format':
if len(tokens) < 3:
- print('Invalid format line')
+ print("Invalid format line")
return invalid_ply
if tokens[1] not in format_specs:
- print('Unknown format', tokens[1])
+ print("Unknown format", tokens[1])
return invalid_ply
try:
version_test = float(tokens[2])
except Exception as ex:
- print('Unknown version', ex)
+ print("Unknown version", ex)
version_test = None
if version_test != float(version):
- print('Unknown version', tokens[2])
+ print("Unknown version", tokens[2])
return invalid_ply
del version_test
format = tokens[1]
elif tokens[0] == b'element':
if len(tokens) < 3:
- print(b'Invalid element line')
+ print("Invalid element line")
return invalid_ply
- obj_spec.specs.append(element_spec(tokens[1], int(tokens[2])))
+ obj_spec.specs.append(ElementSpec(tokens[1], int(tokens[2])))
elif tokens[0] == b'property':
if not len(obj_spec.specs):
- print('Property without element')
+ print("Property without element")
return invalid_ply
if tokens[1] == b'list':
- obj_spec.specs[-1].properties.append(property_spec(tokens[4], type_specs[tokens[2]], type_specs[tokens[3]]))
+ obj_spec.specs[-1].properties.append(PropertySpec(tokens[4], type_specs[tokens[2]], type_specs[tokens[3]]))
else:
- obj_spec.specs[-1].properties.append(property_spec(tokens[2], None, type_specs[tokens[1]]))
+ obj_spec.specs[-1].properties.append(PropertySpec(tokens[2], None, type_specs[tokens[1]]))
if not valid_header:
print("Invalid header ('end_header' line not found!)")
return invalid_ply
@@ -224,22 +225,20 @@ def read(filepath):
return obj_spec, obj, texture
-import bpy
-
-
def load_ply_mesh(filepath, ply_name):
- from bpy_extras.io_utils import unpack_face_list
+ import bpy
obj_spec, obj, texture = read(filepath)
# XXX28: use texture
if obj is None:
- print('Invalid file')
+ print("Invalid file")
return
uvindices = colindices = None
colmultiply = None
- # noindices = None # Ignore normals
+ # TODO import normals
+ # noindices = None
for el in obj_spec.specs:
if el.name == b'vertex':
@@ -375,39 +374,39 @@ def load_ply_mesh(filepath, ply_name):
if texture and uvindices:
pass
- # XXX28: add support for using texture.
- '''
- import os
- import sys
- from bpy_extras.image_utils import load_image
-
- encoding = sys.getfilesystemencoding()
- encoded_texture = texture.decode(encoding=encoding)
- name = bpy.path.display_name_from_filepath(texture)
- image = load_image(encoded_texture, os.path.dirname(filepath), recursive=True, place_holder=True)
-
- if image:
- texture = bpy.data.textures.new(name=name, type='IMAGE')
- texture.image = image
-
- material = bpy.data.materials.new(name=name)
- material.use_shadeless = True
-
- mtex = material.texture_slots.add()
- mtex.texture = texture
- mtex.texture_coords = 'UV'
- mtex.use_map_color_diffuse = True
-
- mesh.materials.append(material)
- for face in mesh.uv_textures[0].data:
- face.image = image
- '''
+ # TODO add support for using texture.
+
+ # import os
+ # import sys
+ # from bpy_extras.image_utils import load_image
+
+ # encoding = sys.getfilesystemencoding()
+ # encoded_texture = texture.decode(encoding=encoding)
+ # name = bpy.path.display_name_from_filepath(texture)
+ # image = load_image(encoded_texture, os.path.dirname(filepath), recursive=True, place_holder=True)
+
+ # if image:
+ # texture = bpy.data.textures.new(name=name, type='IMAGE')
+ # texture.image = image
+
+ # material = bpy.data.materials.new(name=name)
+ # material.use_shadeless = True
+
+ # mtex = material.texture_slots.add()
+ # mtex.texture = texture
+ # mtex.texture_coords = 'UV'
+ # mtex.use_map_color_diffuse = True
+
+ # mesh.materials.append(material)
+ # for face in mesh.uv_textures[0].data:
+ # face.image = image
return mesh
def load_ply(filepath):
import time
+ import bpy
t = time.time()
ply_name = bpy.path.display_name_from_filepath(filepath)
@@ -421,7 +420,7 @@ def load_ply(filepath):
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
- print('\nSuccessfully imported %r in %.3f sec' % (filepath, time.time() - t))
+ print("\nSuccessfully imported %r in %.3f sec" % (filepath, time.time() - t))
return {'FINISHED'}
diff --git a/io_mesh_stl/__init__.py b/io_mesh_stl/__init__.py
index cd81b228..18246d4d 100644
--- a/io_mesh_stl/__init__.py
+++ b/io_mesh_stl/__init__.py
@@ -23,9 +23,8 @@ bl_info = {
"author": "Guillaume Bouchard (Guillaum)",
"version": (1, 1, 3),
"blender": (2, 81, 6),
- "location": "File > Import-Export > Stl",
+ "location": "File > Import-Export",
"description": "Import-Export STL files",
- "warning": "",
"wiki_url": "https://docs.blender.org/manual/en/latest/addons/io_mesh_stl.html",
"support": 'OFFICIAL',
"category": "Import-Export",
@@ -53,75 +52,70 @@ if "bpy" in locals():
if "blender_utils" in locals():
importlib.reload(blender_utils)
-import os
-
import bpy
from bpy.props import (
- StringProperty,
- BoolProperty,
- CollectionProperty,
- EnumProperty,
- FloatProperty,
- )
+ StringProperty,
+ BoolProperty,
+ CollectionProperty,
+ EnumProperty,
+ FloatProperty,
+)
from bpy_extras.io_utils import (
- ImportHelper,
- ExportHelper,
- orientation_helper,
- axis_conversion,
- )
+ ImportHelper,
+ ExportHelper,
+ orientation_helper,
+ axis_conversion,
+)
from bpy.types import (
- Operator,
- OperatorFileListElement,
- )
+ Operator,
+ OperatorFileListElement,
+)
@orientation_helper(axis_forward='Y', axis_up='Z')
class ImportSTL(Operator, ImportHelper):
- """Load STL triangle mesh data"""
bl_idname = "import_mesh.stl"
bl_label = "Import STL"
+ bl_description = "Load STL triangle mesh data"
bl_options = {'UNDO'}
filename_ext = ".stl"
filter_glob: StringProperty(
- default="*.stl",
- options={'HIDDEN'},
- )
+ default="*.stl",
+ options={'HIDDEN'},
+ )
files: CollectionProperty(
- name="File Path",
- type=OperatorFileListElement,
- )
+ name="File Path",
+ type=OperatorFileListElement,
+ )
directory: StringProperty(
- subtype='DIR_PATH',
- )
-
+ subtype='DIR_PATH',
+ )
global_scale: FloatProperty(
- name="Scale",
- soft_min=0.001, soft_max=1000.0,
- min=1e-6, max=1e6,
- default=1.0,
- )
-
+ name="Scale",
+ soft_min=0.001, soft_max=1000.0,
+ min=1e-6, max=1e6,
+ default=1.0,
+ )
use_scene_unit: BoolProperty(
- name="Scene Unit",
- description="Apply current scene's unit (as defined by unit scale) to imported data",
- default=False,
- )
-
+ name="Scene Unit",
+ description="Apply current scene's unit (as defined by unit scale) to imported data",
+ default=False,
+ )
use_facet_normal: BoolProperty(
- name="Facet Normals",
- description="Use (import) facet normals (note that this will still give flat shading)",
- default=False,
- )
+ name="Facet Normals",
+ description="Use (import) facet normals (note that this will still give flat shading)",
+ default=False,
+ )
def execute(self, context):
+ import os
+ from mathutils import Matrix
from . import stl_utils
from . import blender_utils
- from mathutils import Matrix
- paths = [os.path.join(self.directory, name.name)
- for name in self.files]
+ paths = [os.path.join(self.directory, name.name) for name in self.files]
scene = context.scene
@@ -130,9 +124,10 @@ class ImportSTL(Operator, ImportHelper):
if scene.unit_settings.system != 'NONE' and self.use_scene_unit:
global_scale /= scene.unit_settings.scale_length
- global_matrix = axis_conversion(from_forward=self.axis_forward,
- from_up=self.axis_up,
- ).to_4x4() @ Matrix.Scale(global_scale, 4)
+ global_matrix = axis_conversion(
+ from_forward=self.axis_forward,
+ from_up=self.axis_up,
+ ).to_4x4() @ Matrix.Scale(global_scale, 4)
if not paths:
paths.append(self.filepath)
@@ -209,64 +204,70 @@ class STL_PT_import_geometry(bpy.types.Panel):
@orientation_helper(axis_forward='Y', axis_up='Z')
class ExportSTL(Operator, ExportHelper):
- """Save STL triangle mesh data from the active object"""
bl_idname = "export_mesh.stl"
bl_label = "Export STL"
+ bl_description = """Save STL triangle mesh data"""
filename_ext = ".stl"
filter_glob: StringProperty(default="*.stl", options={'HIDDEN'})
use_selection: BoolProperty(
- name="Selection Only",
- description="Export selected objects only",
- default=False,
- )
+ name="Selection Only",
+ description="Export selected objects only",
+ default=False,
+ )
global_scale: FloatProperty(
- name="Scale",
- min=0.01, max=1000.0,
- default=1.0,
- )
-
+ name="Scale",
+ min=0.01, max=1000.0,
+ default=1.0,
+ )
use_scene_unit: BoolProperty(
- name="Scene Unit",
- description="Apply current scene's unit (as defined by unit scale) to exported data",
- default=False,
- )
+ name="Scene Unit",
+ description="Apply current scene's unit (as defined by unit scale) to exported data",
+ default=False,
+ )
ascii: BoolProperty(
- name="Ascii",
- description="Save the file in ASCII file format",
- default=False,
- )
+ name="Ascii",
+ description="Save the file in ASCII file format",
+ default=False,
+ )
use_mesh_modifiers: BoolProperty(
- name="Apply Modifiers",
- description="Apply the modifiers before saving",
- default=True,
- )
+ name="Apply Modifiers",
+ description="Apply the modifiers before saving",
+ default=True,
+ )
batch_mode: EnumProperty(
- name="Batch Mode",
- items=(('OFF', "Off", "All data in one file"),
- ('OBJECT', "Object", "Each object as a file"),
- ))
+ name="Batch Mode",
+ items=(
+ ('OFF', "Off", "All data in one file"),
+ ('OBJECT', "Object", "Each object as a file"),
+ ),
+ )
@property
def check_extension(self):
return self.batch_mode == 'OFF'
def execute(self, context):
- from . import stl_utils
- from . import blender_utils
+ import os
import itertools
from mathutils import Matrix
- keywords = self.as_keywords(ignore=("axis_forward",
- "axis_up",
- "use_selection",
- "global_scale",
- "check_existing",
- "filter_glob",
- "use_scene_unit",
- "use_mesh_modifiers",
- "batch_mode"
- ))
+ from . import stl_utils
+ from . import blender_utils
+
+ keywords = self.as_keywords(
+ ignore=(
+ "axis_forward",
+ "axis_up",
+ "use_selection",
+ "global_scale",
+ "check_existing",
+ "filter_glob",
+ "use_scene_unit",
+ "use_mesh_modifiers",
+ "batch_mode"
+ ),
+ )
scene = context.scene
if self.use_selection:
@@ -279,9 +280,10 @@ class ExportSTL(Operator, ExportHelper):
if scene.unit_settings.system != 'NONE' and self.use_scene_unit:
global_scale *= scene.unit_settings.scale_length
- global_matrix = axis_conversion(to_forward=self.axis_forward,
- to_up=self.axis_up,
- ).to_4x4() @ Matrix.Scale(global_scale, 4)
+ global_matrix = axis_conversion(
+ to_forward=self.axis_forward,
+ to_up=self.axis_up,
+ ).to_4x4() @ Matrix.Scale(global_scale, 4)
if self.batch_mode == 'OFF':
faces = itertools.chain.from_iterable(
@@ -424,6 +426,7 @@ classes = (
STL_PT_export_geometry,
)
+
def register():
for cls in classes:
bpy.utils.register_class(cls)
diff --git a/io_mesh_stl/blender_utils.py b/io_mesh_stl/blender_utils.py
index d1b14cf6..8589e196 100644
--- a/io_mesh_stl/blender_utils.py
+++ b/io_mesh_stl/blender_utils.py
@@ -18,10 +18,6 @@
# <pep8 compliant>
-import bpy
-import array
-from itertools import chain
-
def create_and_link_mesh(name, faces, face_nors, points, global_matrix):
"""
@@ -29,6 +25,10 @@ def create_and_link_mesh(name, faces, face_nors, points, global_matrix):
*points* and *faces* and link it in the current scene.
"""
+ import array
+ from itertools import chain
+ import bpy
+
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(points, [], faces)
@@ -77,6 +77,8 @@ def faces_from_mesh(ob, global_matrix, use_mesh_modifiers=False):
Split the quad into two triangles
"""
+ import bpy
+
# get the editmode data
ob.update_from_editmode()
diff --git a/io_mesh_stl/stl_utils.py b/io_mesh_stl/stl_utils.py
index 0c108e83..ee693375 100644
--- a/io_mesh_stl/stl_utils.py
+++ b/io_mesh_stl/stl_utils.py
@@ -26,14 +26,9 @@ Used as a blender script, it load all the stl files in the scene:
blender --python stl_utils.py -- file1.stl file2.stl file3.stl ...
"""
-import os
-import struct
-import contextlib
-import itertools
-from mathutils.geometry import normal
-
# TODO: endien
+
class ListDict(dict):
"""
Set struct with order.
@@ -88,6 +83,10 @@ def _is_ascii_file(data):
represents a binary file. It can be a (very *RARE* in real life, but
can easily be forged) ascii file.
"""
+
+ import os
+ import struct
+
# Skip header...
data.seek(BINARY_HEADER)
size = struct.unpack('<I', data.read(4))[0]
@@ -106,6 +105,10 @@ def _is_ascii_file(data):
def _binary_read(data):
# Skip header...
+
+ import os
+ import struct
+
data.seek(BINARY_HEADER)
size = struct.unpack('<I', data.read(4))[0]
@@ -164,6 +167,10 @@ def _ascii_read(data):
def _binary_write(filepath, faces):
+ import struct
+ import itertools
+ from mathutils.geometry import normal
+
with open(filepath, 'wb') as data:
fw = data.write
# header
@@ -191,6 +198,8 @@ def _binary_write(filepath, faces):
def _ascii_write(filepath, faces):
+ from mathutils.geometry import normal
+
with open(filepath, 'w') as data:
fw = data.write
header = _header_version()
@@ -206,10 +215,7 @@ def _ascii_write(filepath, faces):
fw('endsolid %s\n' % header)
-def write_stl(filepath="",
- faces=(),
- ascii=False,
- ):
+def write_stl(filepath="", faces=(), ascii=False):
"""
Write a stl file from faces,
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 2d44644b..8b7439e0 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (1, 0, 5),
+ "version": (1, 1, 0),
'blender': (2, 81, 6),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
@@ -57,7 +57,8 @@ import bpy
from bpy.props import (StringProperty,
BoolProperty,
EnumProperty,
- IntProperty)
+ IntProperty,
+ CollectionProperty)
from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper, ExportHelper
@@ -256,6 +257,12 @@ class ExportGLTF2_Base:
default=True
)
+ export_def_bones: BoolProperty(
+ name='Export Deformation bones only',
+ description='Export Deformation bones only (and needed bones for hierarchy)',
+ default=False
+ )
+
export_current_frame: BoolProperty(
name='Use Current Frame',
description='Export the scene in the current animation frame',
@@ -389,11 +396,16 @@ class ExportGLTF2_Base:
if self.export_animations:
export_settings['gltf_frame_range'] = self.export_frame_range
export_settings['gltf_force_sampling'] = self.export_force_sampling
+ if self.export_force_sampling:
+ export_settings['gltf_def_bones'] = self.export_def_bones
+ else:
+ export_settings['gltf_def_bones'] = False
export_settings['gltf_nla_strips'] = self.export_nla_strips
else:
export_settings['gltf_frame_range'] = False
export_settings['gltf_move_keyframes'] = False
export_settings['gltf_force_sampling'] = False
+ export_settings['gltf_def_bones'] = False
export_settings['gltf_skins'] = self.export_skins
if self.export_skins:
export_settings['gltf_all_vertex_influences'] = self.export_all_influences
@@ -642,6 +654,10 @@ class GLTF_PT_export_animation_export(bpy.types.Panel):
layout.prop(operator, 'export_force_sampling')
layout.prop(operator, 'export_nla_strips')
+ row = layout.row()
+ row.active = operator.export_force_sampling
+ row.prop(operator, 'export_def_bones')
+
class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
@@ -730,6 +746,11 @@ class ImportGLTF2(Operator, ImportHelper):
filter_glob: StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})
+ files: CollectionProperty(
+ name="File Path",
+ type=bpy.types.OperatorFileListElement,
+ )
+
loglevel: IntProperty(
name='Log Level',
description="Log Level")
@@ -758,14 +779,30 @@ class ImportGLTF2(Operator, ImportHelper):
return self.import_gltf2(context)
def import_gltf2(self, context):
- import time
- from .io.imp.gltf2_io_gltf import glTFImporter
- from .blender.imp.gltf2_blender_gltf import BlenderGlTF
+ import os
self.set_debug_log()
import_settings = self.as_keywords()
- self.gltf_importer = glTFImporter(self.filepath, import_settings)
+ if self.files:
+ # Multiple file import
+ ret = {'CANCELLED'}
+ dirname = os.path.dirname(self.filepath)
+ for file in self.files:
+ path = os.path.join(dirname, file.name)
+ if self.unit_import(path, import_settings) == {'FINISHED'}:
+ ret = {'FINISHED'}
+ return ret
+ else:
+ # Single file import
+ return self.unit_import(self.filepath, import_settings)
+
+ def unit_import(self, filename, import_settings):
+ import time
+ from .io.imp.gltf2_io_gltf import glTFImporter
+ from .blender.imp.gltf2_blender_gltf import BlenderGlTF
+
+ self.gltf_importer = glTFImporter(filename, import_settings)
success, txt = self.gltf_importer.read()
if not success:
self.report({'ERROR'}, txt)
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_extras.py b/io_scene_gltf2/blender/com/gltf2_blender_extras.py
new file mode 100644
index 00000000..0be942a0
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_extras.py
@@ -0,0 +1,91 @@
+# Copyright 2018-2019 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import bpy
+from .gltf2_blender_json import is_json_convertible
+
+
+# Custom properties, which are in most cases present and should not be imported/exported.
+BLACK_LIST = ['cycles', 'cycles_visibility', 'cycles_curves', '_RNA_UI']
+
+
+def generate_extras(blender_element):
+ """Filter and create a custom property, which is stored in the glTF extra field."""
+ if not blender_element:
+ return None
+
+ extras = {}
+
+ for custom_property in blender_element.keys():
+ if custom_property in BLACK_LIST:
+ continue
+
+ value = __to_json_compatible(blender_element[custom_property])
+
+ if value is not None:
+ extras[custom_property] = value
+
+ if not extras:
+ return None
+
+ return extras
+
+
+def __to_json_compatible(value):
+ """Make a value (usually a custom property) compatible with json"""
+
+ if isinstance(value, bpy.types.ID):
+ return value
+
+ elif isinstance(value, str):
+ return value
+
+ elif isinstance(value, (int, float)):
+ return value
+
+ # for list classes
+ elif isinstance(value, list):
+ value = list(value)
+ # make sure contents are json-compatible too
+ for index in range(len(value)):
+ value[index] = __to_json_compatible(value[index])
+ return value
+
+ # for IDPropertyArray classes
+ elif hasattr(value, "to_list"):
+ value = value.to_list()
+ return value
+
+ elif hasattr(value, "to_dict"):
+ value = value.to_dict()
+ if is_json_convertible(value):
+ return value
+
+ return None
+
+
+def set_extras(blender_element, extras, exclude=[]):
+ """Copy extras onto a Blender object."""
+ if not extras or not isinstance(extras, dict):
+ return
+
+ for custom_property, value in extras.items():
+ if custom_property in BLACK_LIST:
+ continue
+ if custom_property in exclude:
+ continue
+
+ blender_element[custom_property] = value
+
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
index bce1d60f..c7758538 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
@@ -563,7 +563,10 @@ def extract_primitives(glTF, blender_mesh, blender_object, blender_vertex_groups
#
- if not blender_polygon.material_index in material_idx_to_primitives:
+ if export_settings['gltf_materials'] is False:
+ primitive = material_idx_to_primitives[0]
+ vertex_index_to_new_indices = material_map[0]
+ elif not blender_polygon.material_index in material_idx_to_primitives:
primitive = material_idx_to_primitives[0]
vertex_index_to_new_indices = material_map[0]
else:
@@ -605,6 +608,7 @@ def extract_primitives(glTF, blender_mesh, blender_object, blender_vertex_groups
triangles = tessellate_polygon((polyline,))
for triangle in triangles:
+
for triangle_index in triangle:
loop_index_list.append(blender_polygon.loop_indices[triangle_index])
else:
@@ -961,203 +965,14 @@ def extract_primitives(glTF, blender_mesh, blender_object, blender_vertex_groups
attributes[target_tangent_id].extend(target_tangents[morph_index])
#
- # Add primitive plus split them if needed.
+ # Add non-empty primitives
#
- result_primitives = []
-
- for material_idx, primitive in material_idx_to_primitives.items():
- export_color = True
-
- #
-
- indices = primitive[INDICES_ID]
-
- if len(indices) == 0:
- continue
-
- position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]
- normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE]
- if use_tangents:
- tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE]
- tex_coords = []
- for tex_coord_index in range(0, tex_coord_max):
- tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX + str(tex_coord_index)])
- colors = []
- if export_color:
- for color_index in range(0, color_max):
- colors.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX + str(color_index)])
- joints = []
- weights = []
- if export_settings[gltf2_blender_export_keys.SKINS]:
- for bone_index in range(0, bone_max):
- joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX + str(bone_index)])
- weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX + str(bone_index)])
-
- target_positions = []
- target_normals = []
- target_tangents = []
- if export_settings[gltf2_blender_export_keys.MORPH]:
- for morph_index in range(0, morph_max):
- target_positions.append(primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX + str(morph_index)])
- target_normals.append(primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX + str(morph_index)])
- if use_tangents:
- target_tangents.append(primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX + str(morph_index)])
-
- #
-
- count = len(indices)
-
- if count == 0:
- continue
-
- max_index = max(indices)
-
- #
-
- # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
- # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index.
- # https://github.com/KhronosGroup/glTF/issues/1142
- # https://github.com/KhronosGroup/glTF/pull/1476/files
-
- range_indices = 65535
-
- #
-
- if max_index >= range_indices:
- #
- # Splitting result_primitives.
- #
-
- # At start, all indices are pending.
- pending_attributes = {
- POSITION_ATTRIBUTE: [],
- NORMAL_ATTRIBUTE: []
- }
-
- if use_tangents:
- pending_attributes[TANGENT_ATTRIBUTE] = []
-
- pending_primitive = {
- MATERIAL_ID: material_idx,
- INDICES_ID: [],
- ATTRIBUTES_ID: pending_attributes
- }
-
- pending_primitive[INDICES_ID].extend(indices)
-
- pending_attributes[POSITION_ATTRIBUTE].extend(position)
- pending_attributes[NORMAL_ATTRIBUTE].extend(normal)
- if use_tangents:
- pending_attributes[TANGENT_ATTRIBUTE].extend(tangent)
- tex_coord_index = 0
- for tex_coord in tex_coords:
- pending_attributes[TEXCOORD_PREFIX + str(tex_coord_index)] = tex_coord
- tex_coord_index += 1
- if export_color:
- color_index = 0
- for color in colors:
- pending_attributes[COLOR_PREFIX + str(color_index)] = color
- color_index += 1
- if export_settings[gltf2_blender_export_keys.SKINS]:
- joint_index = 0
- for joint in joints:
- pending_attributes[JOINTS_PREFIX + str(joint_index)] = joint
- joint_index += 1
- weight_index = 0
- for weight in weights:
- pending_attributes[WEIGHTS_PREFIX + str(weight_index)] = weight
- weight_index += 1
- if export_settings[gltf2_blender_export_keys.MORPH]:
- morph_index = 0
- for target_position in target_positions:
- pending_attributes[MORPH_POSITION_PREFIX + str(morph_index)] = target_position
- morph_index += 1
- morph_index = 0
- for target_normal in target_normals:
- pending_attributes[MORPH_NORMAL_PREFIX + str(morph_index)] = target_normal
- morph_index += 1
- if use_tangents:
- morph_index = 0
- for target_tangent in target_tangents:
- pending_attributes[MORPH_TANGENT_PREFIX + str(morph_index)] = target_tangent
- morph_index += 1
-
- pending_indices = pending_primitive[INDICES_ID]
-
- # Continue until all are processed.
- while len(pending_indices) > 0:
-
- process_indices = pending_primitive[INDICES_ID]
- max_index = max(process_indices)
-
- pending_indices = []
-
- #
- #
-
- all_local_indices = []
-
- for i in range(0, (max_index // range_indices) + 1):
- all_local_indices.append([])
-
- #
- #
-
- # For all faces ...
- for face_index in range(0, len(process_indices), 3):
-
- written = False
-
- face_min_index = min(process_indices[face_index + 0], process_indices[face_index + 1],
- process_indices[face_index + 2])
- face_max_index = max(process_indices[face_index + 0], process_indices[face_index + 1],
- process_indices[face_index + 2])
-
- # ... check if it can be but in a range of maximum indices.
- for i in range(0, (max_index // range_indices) + 1):
- offset = i * range_indices
-
- # Yes, so store the primitive with its indices.
- if face_min_index >= offset and face_max_index < offset + range_indices:
- all_local_indices[i].extend(
- [process_indices[face_index + 0], process_indices[face_index + 1],
- process_indices[face_index + 2]])
-
- written = True
- break
-
- # If not written, the triangle face has indices from different ranges.
- if not written:
- pending_indices.extend([process_indices[face_index + 0], process_indices[face_index + 1],
- process_indices[face_index + 2]])
-
- # Only add result_primitives, which do have indices in it.
- for local_indices in all_local_indices:
- if len(local_indices) > 0:
- current_primitive = extract_primitive_floor(pending_primitive, local_indices, use_tangents)
-
- result_primitives.append(current_primitive)
-
- print_console('DEBUG', 'Adding primitive with splitting. Indices: ' + str(
- len(current_primitive[INDICES_ID])) + ' Vertices: ' + str(
- len(current_primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3))
-
- # Process primitive faces having indices in several ranges.
- if len(pending_indices) > 0:
- pending_primitive = extract_primitive_pack(pending_primitive, pending_indices, use_tangents)
-
- print_console('DEBUG', 'Creating temporary primitive for splitting')
-
- else:
- #
- # No splitting needed.
- #
- result_primitives.append(primitive)
-
- print_console('DEBUG', 'Adding primitive without splitting. Indices: ' + str(
- len(primitive[INDICES_ID])) + ' Vertices: ' + str(
- len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3))
+ result_primitives = [
+ primitive
+ for primitive in material_idx_to_primitives.values()
+ if len(primitive[INDICES_ID]) != 0
+ ]
print_console('INFO', 'Primitives created: ' + str(len(result_primitives)))
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
index 2e4bfe31..f4fd70dd 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
@@ -19,7 +19,7 @@ from io_scene_gltf2.io.com.gltf2_io_debug import print_console
from io_scene_gltf2.blender.exp import gltf2_blender_gather_nodes
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animations
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
-from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.blender.exp import gltf2_blender_export_keys
@@ -97,6 +97,14 @@ def __gather_animations(blender_scene, export_settings):
to_delete_idx.append(anim_idx)
+ # Merging extras
+ # Warning, some values can be overwritten if present in multiple merged animations
+ if animations[anim_idx].extras is not None:
+ for k in animations[anim_idx].extras.keys():
+ if animations[base_animation_idx].extras is None:
+ animations[base_animation_idx].extras = {}
+ animations[base_animation_idx].extras[k] = animations[anim_idx].extras[k]
+
offset_sampler = len(animations[base_animation_idx].samplers)
for sampler in animations[anim_idx].samplers:
animations[base_animation_idx].samplers.append(sampler)
@@ -124,6 +132,6 @@ def __gather_animations(blender_scene, export_settings):
def __gather_extras(blender_object, export_settings):
if export_settings[gltf2_blender_export_keys.EXTRAS]:
- return gltf2_blender_generate_extras.generate_extras(blender_object)
+ return generate_extras(blender_object)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channel_target.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channel_target.py
index 04028d20..fa0f9976 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channel_target.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channel_target.py
@@ -19,7 +19,7 @@ from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.blender.exp import gltf2_blender_gather_nodes
from io_scene_gltf2.blender.exp import gltf2_blender_gather_joints
-
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_skins
@cached
def gather_animation_channel_target(channels: typing.Tuple[bpy.types.FCurve],
@@ -66,7 +66,12 @@ def __gather_node(channels: typing.Tuple[bpy.types.FCurve],
blender_bone = blender_object.path_resolve(channels[0].data_path.rsplit('.', 1)[0])
if isinstance(blender_bone, bpy.types.PoseBone):
- return gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings)
+ if export_settings["gltf_def_bones"] is False:
+ return gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings)
+ else:
+ bones, _, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_object)
+ if blender_bone.name in [b.name for b in bones]:
+ return gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings)
return gltf2_blender_gather_nodes.gather_node(blender_object, None, export_settings)
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
index edee0971..611cd74a 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py
@@ -22,6 +22,7 @@ from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_samplers
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channel_target
from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_skins
@cached
@@ -58,7 +59,14 @@ def gather_animation_channels(blender_action: bpy.types.Action,
return []
# Then bake all bones
- for bone in blender_object.data.bones:
+ bones_to_be_animated = []
+ if export_settings["gltf_def_bones"] is False:
+ bones_to_be_animated = blender_object.data.bones
+ else:
+ bones_to_be_animated, _, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_object)
+ bones_to_be_animated = [blender_object.pose.bones[b.name] for b in bones_to_be_animated]
+
+ for bone in bones_to_be_animated:
for p in ["location", "rotation_quaternion", "scale"]:
channel = __gather_animation_channel(
(),
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
index de205913..d55c20b1 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
@@ -18,6 +18,7 @@ import typing
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channels
from io_scene_gltf2.io.com.gltf2_io_debug import print_console
+from ..com.gltf2_blender_extras import generate_extras
def gather_animations(blender_object: bpy.types.Object,
@@ -139,6 +140,9 @@ def __gather_extras(blender_action: bpy.types.Action,
blender_object: bpy.types.Object,
export_settings
) -> typing.Any:
+
+ if export_settings['gltf_extras']:
+ return generate_extras(blender_action)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cameras.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cameras.py
index e6d82121..3cde0fcb 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cameras.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cameras.py
@@ -14,7 +14,7 @@
from . import gltf2_blender_export_keys
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
-from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.io.com import gltf2_io
import bpy
@@ -46,7 +46,7 @@ def __gather_extensions(blender_camera, export_settings):
def __gather_extras(blender_camera, export_settings):
if export_settings['gltf_extras']:
- return gltf2_blender_generate_extras.generate_extras(blender_camera)
+ return generate_extras(blender_camera)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py
index 2059ea88..dce70c31 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py
@@ -20,6 +20,7 @@ from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.io.com import gltf2_io_debug
from io_scene_gltf2.blender.exp import gltf2_blender_extract
from io_scene_gltf2.blender.com import gltf2_blender_math
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_skins
@cached
@@ -55,8 +56,15 @@ def gather_joint(blender_bone, export_settings):
# traverse into children
children = []
- for bone in blender_bone.children:
- children.append(gather_joint(bone, export_settings))
+
+ if export_settings["gltf_def_bones"] is False:
+ for bone in blender_bone.children:
+ children.append(gather_joint(bone, export_settings))
+ else:
+ _, children_, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_bone.id_data)
+ if blender_bone.name in children_.keys():
+ for bone in children_[blender_bone.name]:
+ children.append(gather_joint(blender_bone.id_data.pose.bones[bone], export_settings))
# finally add to the joints array containing all the joints in the hierarchy
return gltf2_io.Node(
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py
index c87318d0..dbb59e21 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py
@@ -17,6 +17,7 @@ import math
from typing import Optional, List, Dict, Any
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.io.com import gltf2_io_lights_punctual
from io_scene_gltf2.io.com import gltf2_io_debug
@@ -112,6 +113,8 @@ def __gather_extensions(blender_lamp, export_settings) -> Optional[dict]:
def __gather_extras(blender_lamp, export_settings) -> Optional[Any]:
+ if export_settings['gltf_extras']:
+ return generate_extras(blender_lamp)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
index 3f4466f5..9b9a9abe 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
@@ -23,7 +23,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_material_occlusion_t
from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_pbr_metallic_roughness
-from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.blender.exp import gltf2_blender_get
@@ -136,7 +136,7 @@ def __gather_extensions(blender_material, export_settings):
def __gather_extras(blender_material, export_settings):
if export_settings['gltf_extras']:
- return gltf2_blender_generate_extras.generate_extras(blender_material)
+ return generate_extras(blender_material)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
index ca79ef33..3af26ced 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
@@ -18,7 +18,7 @@ from .gltf2_blender_export_keys import MORPH
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp import gltf2_blender_gather_primitives
-from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.io.com.gltf2_io_debug import print_console
@@ -76,7 +76,7 @@ def __gather_extras(blender_mesh: bpy.types.Mesh,
extras = {}
if export_settings['gltf_extras']:
- extras = gltf2_blender_generate_extras.generate_extras(blender_mesh) or {}
+ extras = generate_extras(blender_mesh) or {}
if export_settings[MORPH] and blender_mesh.shape_keys:
morph_max = len(blender_mesh.shape_keys.key_blocks) - 1
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py
index 32745027..6ab77945 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py
@@ -25,7 +25,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_mesh
from io_scene_gltf2.blender.exp import gltf2_blender_gather_joints
from io_scene_gltf2.blender.exp import gltf2_blender_extract
from io_scene_gltf2.blender.exp import gltf2_blender_gather_lights
-from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from ..com.gltf2_blender_extras import generate_extras
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.io.com import gltf2_io_extensions
@@ -135,7 +135,12 @@ def __gather_children(blender_object, blender_scene, export_settings):
# blender bones
if blender_object.type == "ARMATURE":
root_joints = []
- for blender_bone in blender_object.pose.bones:
+ if export_settings["gltf_def_bones"] is False:
+ bones = blender_object.pose.bones
+ else:
+ bones, _, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_object)
+ bones = [blender_object.pose.bones[b.name] for b in bones]
+ for blender_bone in bones:
if not blender_bone.parent:
joint = gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings)
children.append(joint)
@@ -214,7 +219,7 @@ def __gather_extensions(blender_object, export_settings):
def __gather_extras(blender_object, export_settings):
if export_settings['gltf_extras']:
- return gltf2_blender_generate_extras.generate_extras(blender_object)
+ return generate_extras(blender_object)
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_skins.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_skins.py
index 59af7d72..18503fdd 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_skins.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_skins.py
@@ -67,11 +67,14 @@ def __gather_inverse_bind_matrices(blender_object, export_settings):
axis_basis_change = mathutils.Matrix(
((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0)))
- # build the hierarchy of nodes out of the bones
- root_bones = []
- for blender_bone in blender_object.pose.bones:
- if not blender_bone.parent:
- root_bones.append(blender_bone)
+ if export_settings['gltf_def_bones'] is False:
+ # build the hierarchy of nodes out of the bones
+ root_bones = []
+ for blender_bone in blender_object.pose.bones:
+ if not blender_bone.parent:
+ root_bones.append(blender_bone)
+ else:
+ _, children_, root_bones = get_bone_tree(None, blender_object)
matrices = []
@@ -86,8 +89,13 @@ def __gather_inverse_bind_matrices(blender_object, export_settings):
).inverted()
matrices.append(inverse_bind_matrix)
- for child in bone.children:
- __collect_matrices(child)
+ if export_settings['gltf_def_bones'] is False:
+ for child in bone.children:
+ __collect_matrices(child)
+ else:
+ if bone.name in children_.keys():
+ for child in children_[bone.name]:
+ __collect_matrices(blender_object.pose.bones[child])
# start with the "root" bones and recurse into children, in the same ordering as the how joints are gathered
for root_bone in root_bones:
@@ -114,18 +122,28 @@ def __gather_inverse_bind_matrices(blender_object, export_settings):
def __gather_joints(blender_object, export_settings):
root_joints = []
- # build the hierarchy of nodes out of the bones
- for blender_bone in blender_object.pose.bones:
- if not blender_bone.parent:
- root_joints.append(gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings))
+ if export_settings['gltf_def_bones'] is False:
+ # build the hierarchy of nodes out of the bones
+ for blender_bone in blender_object.pose.bones:
+ if not blender_bone.parent:
+ root_joints.append(gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings))
+ else:
+ _, children_, root_joints = get_bone_tree(None, blender_object)
+ root_joints = [gltf2_blender_gather_joints.gather_joint(i, export_settings) for i in root_joints]
# joints is a flat list containing all nodes belonging to the skin
joints = []
def __collect_joints(node):
joints.append(node)
- for child in node.children:
- __collect_joints(child)
+ if export_settings['gltf_def_bones'] is False:
+ for child in node.children:
+ __collect_joints(child)
+ else:
+ if node.name in children_.keys():
+ for child in children_[node.name]:
+ __collect_joints(gltf2_blender_gather_joints.gather_joint(blender_object.pose.bones[child], export_settings))
+
for joint in root_joints:
__collect_joints(joint)
@@ -140,3 +158,30 @@ def __gather_skeleton(blender_object, export_settings):
# In the future support the result of https://github.com/KhronosGroup/glTF/pull/1195
return None # gltf2_blender_gather_nodes.gather_node(blender_object, blender_scene, export_settings)
+@cached
+def get_bone_tree(blender_dummy, blender_object):
+
+ bones = []
+ children = {}
+ root_bones = []
+
+ def get_parent(bone):
+ bones.append(bone.name)
+ if bone.parent is not None:
+ if bone.parent.name not in children.keys():
+ children[bone.parent.name] = []
+ children[bone.parent.name].append(bone.name)
+ get_parent(bone.parent)
+ else:
+ root_bones.append(bone.name)
+
+ for bone in [b for b in blender_object.data.bones if b.use_deform is True]:
+ get_parent(bone)
+
+ # remove duplicates
+ for k, v in children.items():
+ children[k] = list(set(v))
+ list_ = list(set(bones))
+ root_ = list(set(root_bones))
+ return [blender_object.data.bones[b] for b in list_], children, [blender_object.pose.bones[b] for b in root_]
+
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_camera.py b/io_scene_gltf2/blender/imp/gltf2_blender_camera.py
index d746c1b1..ec5f0ee4 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_camera.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_camera.py
@@ -13,6 +13,7 @@
# limitations under the License.
import bpy
+from ..com.gltf2_blender_extras import set_extras
class BlenderCamera():
@@ -29,6 +30,7 @@ class BlenderCamera():
pycamera.name = "Camera"
cam = bpy.data.cameras.new(pycamera.name)
+ set_extras(cam, pycamera.extras)
# Blender create a perspective camera by default
if pycamera.type == "orthographic":
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_light.py b/io_scene_gltf2/blender/imp/gltf2_blender_light.py
index 492c552e..6213091e 100644
--- a/io_scene_gltf2/blender/imp/gltf2_blender_light.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_light.py
@@ -15,6 +15,8 @@
import bpy
from math import pi
+from ..com.gltf2_blender_extras import set_extras
+
class BlenderLight():
"""Blender Light."""
@@ -45,6 +47,8 @@ class BlenderLight():
else:
bpy.data.scenes[gltf.blender_scene].collection.objects.link(obj)
+ set_extras(obj.data, pylight.get('extras'))
+
return obj
@staticmethod
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_material.py b/io_scene_gltf2/blender/imp/gltf2_blender_material.py
index 55be1626..c5e3b51c 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_material.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_material.py
@@ -13,6 +13,8 @@
# limitations under the License.
import bpy
+
+from ..com.gltf2_blender_extras import set_extras
from .gltf2_blender_pbrMetallicRoughness import BlenderPbr
from .gltf2_blender_KHR_materials_pbrSpecularGlossiness import BlenderKHR_materials_pbrSpecularGlossiness
from .gltf2_blender_KHR_materials_unlit import BlenderKHR_materials_unlit
@@ -49,6 +51,8 @@ class BlenderMaterial():
mat = bpy.data.materials.new(name)
pymaterial.blender_material[vertex_color] = mat.name
+ set_extras(mat, pymaterial.extras)
+
mat.use_backface_culling = (pymaterial.double_sided != True)
ignore_map = False
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
index 1f7f7b66..eb92d9a3 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
@@ -16,6 +16,7 @@ import bpy
import bmesh
from mathutils import Vector
+from ..com.gltf2_blender_extras import set_extras
from .gltf2_blender_material import BlenderMaterial
from .gltf2_blender_primitive import BlenderPrimitive
from ...io.imp.gltf2_io_binary import BinaryData
@@ -75,6 +76,8 @@ class BlenderMesh():
mesh.materials.append(bpy.data.materials[name_material])
mesh.update()
+ set_extras(mesh, pymesh.extras, exclude=['targetNames'])
+
pymesh.blender_name = mesh.name
# Clear accessor cache after all primitives are done
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_node.py b/io_scene_gltf2/blender/imp/gltf2_blender_node.py
index 6a40aecc..a02514de 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_node.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_node.py
@@ -13,6 +13,7 @@
# limitations under the License.
import bpy
+from ..com.gltf2_blender_extras import set_extras
from .gltf2_blender_mesh import BlenderMesh
from .gltf2_blender_camera import BlenderCamera
from .gltf2_blender_skin import BlenderSkin
@@ -76,6 +77,7 @@ class BlenderNode():
name = "Object_" + str(node_idx)
obj = bpy.data.objects.new(name, mesh)
+ set_extras(obj, pynode.extras)
obj.rotation_mode = 'QUATERNION'
if gltf.blender_active_collection is not None:
bpy.data.collections[gltf.blender_active_collection].objects.link(obj)
@@ -104,6 +106,7 @@ class BlenderNode():
else:
gltf.log.info("Blender create Camera node")
obj = BlenderCamera.create(gltf, pynode.camera)
+ set_extras(obj, pynode.extras)
BlenderNode.set_transforms(gltf, node_idx, pynode, obj, parent) # TODO default rotation of cameras ?
pynode.blender_object = obj.name
BlenderNode.set_parent(gltf, obj, parent)
@@ -134,6 +137,7 @@ class BlenderNode():
if pynode.extensions is not None:
if 'KHR_lights_punctual' in pynode.extensions.keys():
obj = BlenderLight.create(gltf, pynode.extensions['KHR_lights_punctual']['light'])
+ set_extras(obj, pynode.extras)
obj.rotation_mode = 'QUATERNION'
BlenderNode.set_transforms(gltf, node_idx, pynode, obj, parent, correction=True)
pynode.blender_object = obj.name
@@ -154,6 +158,7 @@ class BlenderNode():
else:
gltf.log.info("Blender create Empty node")
obj = bpy.data.objects.new("Node", None)
+ set_extras(obj, pynode.extras)
obj.rotation_mode = 'QUATERNION'
if gltf.blender_active_collection is not None:
bpy.data.collections[gltf.blender_active_collection].objects.link(obj)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_skin.py b/io_scene_gltf2/blender/imp/gltf2_blender_skin.py
index 2758b348..9c8cd168 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_skin.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_skin.py
@@ -17,7 +17,7 @@ import bpy
from mathutils import Vector, Matrix
from ..com.gltf2_blender_conversion import matrix_gltf_to_blender, scale_to_matrix
from ...io.imp.gltf2_io_binary import BinaryData
-
+from ..com.gltf2_blender_extras import set_extras
class BlenderSkin():
"""Blender Skinning / Armature."""
@@ -130,10 +130,15 @@ class BlenderSkin():
pynode.blender_bone_name = bone.name
pynode.blender_armature_name = pyskin.blender_armature_name
bone.tail = Vector((0.0, 1.0, 0.0)) # Needed to keep bone alive
+ # Custom prop on edit bone
+ set_extras(bone, pynode.extras)
# set bind and pose transforms
BlenderSkin.set_bone_transforms(gltf, skin_id, bone, node_id, parent)
bpy.ops.object.mode_set(mode="OBJECT")
+ # Custom prop on pose bone
+ if pynode.blender_bone_name in obj.pose.bones:
+ set_extras(obj.pose.bones[pynode.blender_bone_name], pynode.extras)
@staticmethod
def create_vertex_groups(gltf, skin_id):
diff --git a/io_scene_gltf2/io/imp/gltf2_io_binary.py b/io_scene_gltf2/io/imp/gltf2_io_binary.py
index 4c5ea8f1..c72a893a 100755
--- a/io_scene_gltf2/io/imp/gltf2_io_binary.py
+++ b/io_scene_gltf2/io/imp/gltf2_io_binary.py
@@ -14,8 +14,6 @@
import struct
import base64
-from os.path import dirname, join, isfile, basename
-from urllib.parse import unquote
class BinaryData():
@@ -27,23 +25,31 @@ class BinaryData():
def get_binary_from_accessor(gltf, accessor_idx):
"""Get binary from accessor."""
accessor = gltf.data.accessors[accessor_idx]
- bufferView = gltf.data.buffer_views[accessor.buffer_view] # TODO initialize with 0 when not present!
- if bufferView.buffer in gltf.buffers.keys():
- buffer = gltf.buffers[bufferView.buffer]
- else:
- # load buffer
- gltf.load_buffer(bufferView.buffer)
- buffer = gltf.buffers[bufferView.buffer]
+ data = BinaryData.get_buffer_view(gltf, accessor.buffer_view) # TODO initialize with 0 when not present!
accessor_offset = accessor.byte_offset
- bufferview_offset = bufferView.byte_offset
-
if accessor_offset is None:
accessor_offset = 0
- if bufferview_offset is None:
- bufferview_offset = 0
- return buffer[accessor_offset + bufferview_offset:accessor_offset + bufferview_offset + bufferView.byte_length]
+ return data[accessor_offset:]
+
+ @staticmethod
+ def get_buffer_view(gltf, buffer_view_idx):
+ """Get binary data for buffer view."""
+ buffer_view = gltf.data.buffer_views[buffer_view_idx]
+
+ if buffer_view.buffer in gltf.buffers.keys():
+ buffer = gltf.buffers[buffer_view.buffer]
+ else:
+ # load buffer
+ gltf.load_buffer(buffer_view.buffer)
+ buffer = gltf.buffers[buffer_view.buffer]
+
+ byte_offset = buffer_view.byte_offset
+ if byte_offset is None:
+ byte_offset = 0
+
+ return buffer[byte_offset:byte_offset + buffer_view.byte_length]
@staticmethod
def get_data_from_accessor(gltf, accessor_idx, cache=False):
@@ -153,40 +159,17 @@ class BinaryData():
def get_image_data(gltf, img_idx):
"""Get data from image."""
pyimage = gltf.data.images[img_idx]
-
image_name = "Image_" + str(img_idx)
- if pyimage.uri:
- sep = ';base64,'
- if pyimage.uri[:5] == 'data:':
- idx = pyimage.uri.find(sep)
- if idx != -1:
- data = pyimage.uri[idx + len(sep):]
- return base64.b64decode(data), image_name
-
- if isfile(join(dirname(gltf.filename), unquote(pyimage.uri))):
- with open(join(dirname(gltf.filename), unquote(pyimage.uri)), 'rb') as f_:
- return f_.read(), basename(join(dirname(gltf.filename), unquote(pyimage.uri)))
- else:
- gltf.log.error("Missing file (index " + str(img_idx) + "): " + pyimage.uri)
- return None, None
-
- if pyimage.buffer_view is None:
- return None, None
-
- bufferView = gltf.data.buffer_views[pyimage.buffer_view]
-
- if bufferView.buffer in gltf.buffers.keys():
- buffer = gltf.buffers[bufferView.buffer]
- else:
- # load buffer
- gltf.load_buffer(bufferView.buffer)
- buffer = gltf.buffers[bufferView.buffer]
+ assert(not (pyimage.uri is not None and pyimage.buffer_view is not None))
- bufferview_offset = bufferView.byte_offset
+ if pyimage.uri is not None:
+ data, file_name = gltf.load_uri(pyimage.uri)
+ return data, file_name or image_name
- if bufferview_offset is None:
- bufferview_offset = 0
+ elif pyimage.buffer_view is not None:
+ data = BinaryData.get_buffer_view(gltf, pyimage.buffer_view)
+ return data, image_name
- return buffer[bufferview_offset:bufferview_offset + bufferView.byte_length], image_name
+ return None, None
diff --git a/io_scene_gltf2/io/imp/gltf2_io_gltf.py b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
index 34f205f9..1a30f258 100755
--- a/io_scene_gltf2/io/imp/gltf2_io_gltf.py
+++ b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
@@ -18,7 +18,8 @@ import logging
import json
import struct
import base64
-from os.path import dirname, join, getsize, isfile
+from os.path import dirname, join, isfile, basename
+from urllib.parse import unquote
class glTFImporter():
@@ -28,6 +29,7 @@ class glTFImporter():
"""initialization."""
self.filename = filename
self.import_settings = import_settings
+ self.glb_buffer = None
self.buffers = {}
self.accessor_cache = {}
@@ -104,36 +106,34 @@ class glTFImporter():
return False, "This file is not a glTF/glb file"
if self.version != 2:
- return False, "glTF version doesn't match to 2"
+ return False, "GLB version %d unsupported" % self.version
- if self.file_size != getsize(self.filename):
- return False, "File size doesn't match"
+ if self.file_size != len(self.content):
+ return False, "Bad GLB: file size doesn't match"
offset = 12 # header size = 12
- # TODO check json type for chunk 0, and BIN type for next ones
-
- # json
- type, len_, str_json, offset = self.load_chunk(offset)
- if len_ != len(str_json):
- return False, "Length of json part doesn't match"
+ # JSON chunk is first
+ type_, len_, json_bytes, offset = self.load_chunk(offset)
+ if type_ != b"JSON":
+ return False, "Bad GLB: first chunk not JSON"
+ if len_ != len(json_bytes):
+ return False, "Bad GLB: length of json chunk doesn't match"
try:
- json_ = json.loads(str_json.decode('utf-8'), parse_constant=glTFImporter.bad_json_value)
+ json_str = str(json_bytes, encoding='utf-8')
+ json_ = json.loads(json_str, parse_constant=glTFImporter.bad_json_value)
self.data = gltf_from_dict(json_)
except ValueError as e:
return False, e.args[0]
- # binary data
- chunk_cpt = 0
- while offset < len(self.content):
- type, len_, data, offset = self.load_chunk(offset)
- if len_ != len(data):
- return False, "Length of bin buffer " + str(chunk_cpt) + " doesn't match"
-
- self.buffers[chunk_cpt] = data
- chunk_cpt += 1
+ # BIN chunk is second (if it exists)
+ if offset < len(self.content):
+ type_, len_, data, offset = self.load_chunk(offset)
+ if type_ == b"BIN\0":
+ if len_ != len(data):
+ return False, "Bad GLB: length of BIN chunk doesn't match"
+ self.glb_buffer = data
- self.content = None
return True, None
def load_chunk(self, offset):
@@ -153,25 +153,25 @@ class glTFImporter():
# Check if file is gltf or glb
with open(self.filename, 'rb') as f:
- self.content = f.read()
+ self.content = memoryview(f.read())
self.is_glb_format = self.content[:4] == b'glTF'
# glTF file
if not self.is_glb_format:
+ content = str(self.content, encoding='utf-8')
self.content = None
- with open(self.filename, 'r') as f:
- content = f.read()
- try:
- self.data = gltf_from_dict(json.loads(content, parse_constant=glTFImporter.bad_json_value))
- return True, None
- except ValueError as e:
- return False, e.args[0]
+ try:
+ self.data = gltf_from_dict(json.loads(content, parse_constant=glTFImporter.bad_json_value))
+ return True, None
+ except ValueError as e:
+ return False, e.args[0]
# glb file
else:
# Parsing glb file
success, txt = self.load_glb()
+ self.content = None
return success, txt
def is_node_joint(self, node_idx):
@@ -190,14 +190,31 @@ class glTFImporter():
buffer = self.data.buffers[buffer_idx]
if buffer.uri:
- sep = ';base64,'
- if buffer.uri[:5] == 'data:':
- idx = buffer.uri.find(sep)
- if idx != -1:
- data = buffer.uri[idx + len(sep):]
- self.buffers[buffer_idx] = base64.b64decode(data)
- return
-
- with open(join(dirname(self.filename), buffer.uri), 'rb') as f_:
- self.buffers[buffer_idx] = f_.read()
+ data, _file_name = self.load_uri(buffer.uri)
+ if data is not None:
+ self.buffers[buffer_idx] = data
+
+ else:
+ # GLB-stored buffer
+ if buffer_idx == 0 and self.glb_buffer is not None:
+ self.buffers[buffer_idx] = self.glb_buffer
+
+ def load_uri(self, uri):
+ """Loads a URI.
+ Returns the data and the filename of the resource, if there is one.
+ """
+ sep = ';base64,'
+ if uri.startswith('data:'):
+ idx = uri.find(sep)
+ if idx != -1:
+ data = uri[idx + len(sep):]
+ return memoryview(base64.b64decode(data)), None
+
+ path = join(dirname(self.filename), unquote(uri))
+ try:
+ with open(path, 'rb') as f_:
+ return memoryview(f_.read()), basename(path)
+ except Exception:
+ self.log.error("Couldn't read file: " + path)
+ return None, None
diff --git a/measureit/__init__.py b/measureit/__init__.py
index d7e21e36..5f80de87 100644
--- a/measureit/__init__.py
+++ b/measureit/__init__.py
@@ -32,8 +32,8 @@ bl_info = {
"version": (1, 8, 1),
"blender": (2, 80, 0),
"description": "Tools for measuring objects.",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
- "Py/Scripts/3D_interaction/Measureit",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons"
+ "/3d_view/measureit.html",
"category": "3D View"
}
diff --git a/mesh_bsurfaces.py b/mesh_bsurfaces.py
index 9e9ae09d..d896d71f 100644
--- a/mesh_bsurfaces.py
+++ b/mesh_bsurfaces.py
@@ -20,11 +20,12 @@
bl_info = {
"name": "Bsurfaces GPL Edition",
"author": "Eclectiel, Spivak Vladimir(cwolf3d)",
- "version": (1, 7, 1),
+ "version": (1, 7, 5),
"blender": (2, 80, 0),
"location": "View3D EditMode > Sidebar > Edit Tab",
"description": "Modeling and retopology tool",
- "wiki_url": "https://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.64/Bsurfaces_1.5",
+ "wiki_url": "https://docs.blender.org/manual/nb/dev/addons/"
+ "mesh/bsurfaces.html",
"category": "Mesh",
}
@@ -62,9 +63,11 @@ from bpy.types import (
# ----------------------------
# GLOBAL
-global_color = [1.0, 0.0, 0.0, 1.0]
+global_color = [1.0, 0.0, 0.0, 0.3]
global_offset = 0.01
global_in_front = False
+global_shade_smooth = False
+global_show_wire = True
global_mesh_object = ""
global_gpencil_object = ""
global_curve_object = ""
@@ -92,6 +95,8 @@ class VIEW3D_PT_tools_SURFSK_mesh(Panel):
col.prop(scn, "SURFSK_mesh_color")
col.prop(scn, "SURFSK_Shrinkwrap_offset")
col.prop(scn, "SURFSK_in_front")
+ col.prop(scn, "SURFSK_shade_smooth")
+ col.prop(scn, "SURFSK_show_wire")
col.label(text="Guide strokes:")
col.row().prop(scn, "SURFSK_guide", expand=True)
@@ -101,14 +106,26 @@ class VIEW3D_PT_tools_SURFSK_mesh(Panel):
if scn.SURFSK_guide == 'Curve':
col.prop(scn, "SURFSK_curve", text="")
col.separator()
- props = col.operator("gpencil.surfsk_add_surface", text="Add Surface")
+ col.separator()
+ props = col.operator("gpencil.surfsk_add_surface", text="Add Surface")
col.operator("gpencil.surfsk_edit_surface", text="Edit Surface")
+
+ col.separator()
if scn.SURFSK_guide == 'GPencil':
col.operator("gpencil.surfsk_add_strokes", text="Add Strokes")
col.operator("gpencil.surfsk_edit_strokes", text="Edit Strokes")
+ col.separator()
+ col.operator("gpencil.surfsk_strokes_to_curves", text="Strokes to curves")
+
if scn.SURFSK_guide == 'Annotation':
col.operator("gpencil.surfsk_add_annotation", text="Add Annotation")
+ col.separator()
+ col.operator("gpencil.surfsk_annotations_to_curves", text="Annotation to curves")
+
+ if scn.SURFSK_guide == 'Curve':
+ col.operator("gpencil.surfsk_edit_curve", text="Edit curve")
+
col.separator()
col.label(text="Initial settings:")
col.prop(scn, "SURFSK_edges_U")
@@ -172,11 +189,6 @@ def get_strokes_type(context):
strokes_type = "GP_STROKES"
except:
strokes_type = "NO_STROKES"
-
- # Check if they are mesh
- global global_mesh_object
- main_object = bpy.data.objects[global_mesh_object]
- total_vert_sel = len([v for v in main_object.data.vertices if v.select])
# Check if they are curves, if there aren't grease pencil strokes
if context.scene.bsurfaces.SURFSK_guide == 'Curve':
@@ -198,16 +210,23 @@ def get_strokes_type(context):
except:
strokes_type = "NO_STROKES"
- # Check if there is a single stroke without any selection in the object
- if strokes_num == 1 and total_vert_sel == 0:
- if strokes_type == "EXTERNAL_CURVE":
- strokes_type = "SINGLE_CURVE_STROKE_NO_SELECTION"
- elif strokes_type == "GP_STROKES":
- strokes_type = "SINGLE_GP_STROKE_NO_SELECTION"
-
- if strokes_num == 0 and total_vert_sel > 0:
- strokes_type = "SELECTION_ALONE"
-
+ # Check if they are mesh
+ try:
+ global global_mesh_object
+ self.main_object = bpy.data.objects[global_mesh_object]
+ total_vert_sel = len([v for v in self.main_object.data.vertices if v.select])
+
+ # Check if there is a single stroke without any selection in the object
+ if strokes_num == 1 and total_vert_sel == 0:
+ if strokes_type == "EXTERNAL_CURVE":
+ strokes_type = "SINGLE_CURVE_STROKE_NO_SELECTION"
+ elif strokes_type == "GP_STROKES":
+ strokes_type = "SINGLE_GP_STROKE_NO_SELECTION"
+
+ if strokes_num == 0 and total_vert_sel > 0:
+ strokes_type = "SELECTION_ALONE"
+ except:
+ pass
return strokes_type
@@ -1042,6 +1061,8 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='EDIT')
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+
+ self.update()
return num_faces_created
@@ -1383,6 +1404,8 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
for m_idx in range(len(self.main_object.modifiers)):
self.main_object.modifiers[m_idx].show_viewport = self.modifiers_prev_viewport_state[m_idx]
+ self.update()
+
return
# Part of the Crosshatch process that is repeated when the operator is tweaked
@@ -1665,7 +1688,7 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
# on the original verts, and get them selected
crosshatch_verts_to_merge = []
if self.automatic_join:
- for i in range(len(ob_surface.data.vertices)):
+ for i in range(len(ob_surface.data.vertices)-1):
# Calculate the distance from each of the connected verts to the actual vert,
# and compare it with the distance they would have if joined.
# If they don't change much, that vert can be joined
@@ -1753,6 +1776,8 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
for m_idx in range(len(self.main_object.modifiers)):
self.main_object.modifiers[m_idx].show_viewport = self.modifiers_prev_viewport_state[m_idx]
+ self.update()
+
return {'FINISHED'}
def rectangular_surface(self, context):
@@ -3054,6 +3079,8 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
bpy.ops.mesh.normals_make_consistent('INVOKE_REGION_WIN', inside=False)
bpy.ops.mesh.select_all('INVOKE_REGION_WIN', action='DESELECT')
+ self.update()
+
return{'FINISHED'}
def update(self):
@@ -3073,25 +3100,44 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
else:
self.main_object.data.materials.append(material)
bpy.context.scene.bsurfaces.SURFSK_mesh_color = global_color
+ except:
+ pass
+ try:
global global_in_front
self.main_object.show_in_front = global_in_front
bpy.context.scene.bsurfaces.SURFSK_in_front = global_in_front
except:
pass
+
+ try:
+ global global_show_wire
+ self.main_object.show_wire = global_show_wire
+ bpy.context.scene.bsurfaces.SURFSK_show_wire = global_show_wire
+ except:
+ pass
+
+ try:
+ global global_shade_smooth
+ if global_shade_smooth:
+ bpy.ops.object.shade_smooth()
+ else:
+ bpy.ops.object.shade_flat()
+ bpy.context.scene.bsurfaces.SURFSK_shade_smooth = global_shade_smooth
+ except:
+ pass
return{'FINISHED'}
def execute(self, context):
- bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
-
- global global_mesh_object
-
- bsurfaces_props = bpy.context.scene.bsurfaces
- self.main_object = bpy.data.objects[global_mesh_object]
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
try:
+ global global_mesh_object
+ bsurfaces_props = bpy.context.scene.bsurfaces
+ self.main_object = bpy.data.objects[global_mesh_object]
self.main_object.select_set(True)
except:
self.report({'WARNING'}, "Specify the name of the object with retopology")
@@ -3143,16 +3189,20 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
strokes_for_rectangular_surface = True
strokes_for_crosshatch = False
- bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- self.main_object.select_set(True)
- bpy.context.view_layer.objects.active = self.main_object
-
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
if strokes_for_rectangular_surface:
self.rectangular_surface(context)
elif strokes_for_crosshatch:
- self.crosshatch_surface_execute()
+ self.crosshatch_surface_execute(context)
+
+ #Set Shade smooth to new polygons
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ global global_shade_smooth
+ if global_shade_smooth:
+ bpy.ops.object.shade_smooth()
+ else:
+ bpy.ops.object.shade_flat()
# Delete main splines
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
@@ -3205,7 +3255,8 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
def invoke(self, context, event):
- bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
bsurfaces_props = bpy.context.scene.bsurfaces
self.cyclic_cross = bsurfaces_props.SURFSK_cyclic_cross
@@ -3248,22 +3299,12 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
global global_gpencil_object
gp = bpy.data.objects[global_gpencil_object]
self.original_curve = conver_gpencil_to_curve(self, context, gp, 'GPensil')
- gplayer_prefix_translated = bpy.app.translations.pgettext_data('GP_Layer')
- for ob in bpy.context.selected_objects:
- if ob != bpy.context.view_layer.objects.active and \
- ob.name.startswith((gplayer_prefix_translated, 'GP_Layer')):
- self.original_curve = ob
self.using_external_curves = False
elif self.strokes_type == "GP_ANNOTATION":
# Convert grease pencil strokes to curve
gp = bpy.data.grease_pencils["Annotations"]
self.original_curve = conver_gpencil_to_curve(self, context, gp, 'Annotation')
- gplayer_prefix_translated = bpy.app.translations.pgettext_data('GP_Layer')
- for ob in bpy.context.selected_objects:
- if ob != bpy.context.view_layer.objects.active and \
- ob.name.startswith((gplayer_prefix_translated, 'GP_Layer')):
- self.original_curve = ob
self.using_external_curves = False
elif self.strokes_type == "EXTERNAL_CURVE":
@@ -3271,19 +3312,12 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
self.original_curve = bpy.data.objects[global_curve_object]
self.using_external_curves = True
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
-
# Make sure there are no objects left from erroneous
# executions of this operator, with the reserved names used here
for o in bpy.data.objects:
if o.name.find("SURFSKIO_") != -1:
bpy.ops.object.delete({"selected_objects": [o]})
- try:
- self.original_curve.select_set(True)
- except:
- self.report({'WARNING'}, "Specify the name of the object with curve")
- return{"CANCELLED"}
bpy.context.view_layer.objects.active = self.original_curve
bpy.ops.object.duplicate('INVOKE_REGION_WIN')
@@ -3422,32 +3456,28 @@ class GPENCIL_OT_SURFSK_add_surface(Operator):
# Delete temporary strokes curve object
bpy.ops.object.delete({"selected_objects": [self.temporary_curve]})
- # If "Keep strokes" option is not active, delete original strokes curve object
- if not self.stopping_errors or self.is_crosshatch:
- bpy.ops.object.delete({"selected_objects": [self.original_curve]})
-
- # Delete grease pencil strokes
- if self.strokes_type == "GP_STROKES" and not self.stopping_errors:
- try:
- bpy.context.scene.bsurfaces.SURFSK_gpencil.data.layers.active.clear()
- except:
- pass
-
- # Delete annotation strokes
- if self.strokes_type == "GP_ANNOTATION" and not self.stopping_errors:
- try:
- bpy.data.grease_pencils[0].layers.active.clear()
- except:
- pass
-
- bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- self.main_object.select_set(True)
- bpy.context.view_layer.objects.active = self.main_object
-
# Set again since "execute()" will turn it again to its initial value
self.execute(context)
if not self.stopping_errors:
+ # Delete grease pencil strokes
+ if self.strokes_type == "GP_STROKES":
+ try:
+ bpy.context.scene.bsurfaces.SURFSK_gpencil.data.layers.active.clear()
+ except:
+ pass
+
+ # Delete annotation strokes
+ elif self.strokes_type == "GP_ANNOTATION":
+ try:
+ bpy.data.grease_pencils[0].layers.active.clear()
+ except:
+ pass
+
+ bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
+ bpy.ops.object.delete({"selected_objects": [self.original_curve]})
+ bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
+
return {"FINISHED"}
else:
return{"CANCELLED"}
@@ -3495,6 +3525,7 @@ class GPENCIL_OT_SURFSK_init(Operator):
bl_idname = "gpencil.surfsk_init"
bl_label = "Bsurfaces initialize"
bl_description = "Bsurfaces initialize"
+ bl_options = {'REGISTER', 'UNDO'}
active_object: PointerProperty(type=bpy.types.Object)
@@ -3502,11 +3533,14 @@ class GPENCIL_OT_SURFSK_init(Operator):
bs = bpy.context.scene.bsurfaces
- bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
global global_color
global global_offset
global global_in_front
+ global global_show_wire
+ global global_shade_smooth
global global_mesh_object
global global_gpencil_object
@@ -3515,12 +3549,23 @@ class GPENCIL_OT_SURFSK_init(Operator):
mesh = bpy.data.meshes.new('BSurfaceMesh')
mesh_object = object_utils.object_data_add(context, mesh)
mesh_object.select_set(True)
+ bpy.context.view_layer.objects.active = mesh_object
+
mesh_object.show_all_edges = True
global_in_front = bpy.context.scene.bsurfaces.SURFSK_in_front
mesh_object.show_in_front = global_in_front
mesh_object.display_type = 'SOLID'
mesh_object.show_wire = True
- bpy.context.view_layer.objects.active = mesh_object
+
+ global_shade_smooth = bpy.context.scene.bsurfaces.SURFSK_shade_smooth
+ if global_shade_smooth:
+ bpy.ops.object.shade_smooth()
+ else:
+ bpy.ops.object.shade_flat()
+
+ global_show_wire = bpy.context.scene.bsurfaces.SURFSK_show_wire
+ mesh_object.show_wire = global_show_wire
+
global_color = bpy.context.scene.bsurfaces.SURFSK_mesh_color
material = makeMaterial("BSurfaceMesh", global_color)
mesh_object.data.materials.append(material)
@@ -3565,11 +3610,6 @@ class GPENCIL_OT_SURFSK_init(Operator):
if context.scene.bsurfaces.SURFSK_guide == 'Annotation':
bpy.ops.wm.tool_set_by_id(name="builtin.annotate")
bpy.context.scene.tool_settings.annotation_stroke_placement_view3d = 'SURFACE'
-
- if context.scene.bsurfaces.SURFSK_guide == 'Curve':
- bpy.data.objects[global_mesh_object].data.vertices.add(1)
-
- return {"FINISHED"}
def invoke(self, context, event):
if bpy.context.active_object:
@@ -3586,7 +3626,8 @@ class GPENCIL_OT_SURFSK_init(Operator):
class GPENCIL_OT_SURFSK_add_modifiers(Operator):
bl_idname = "gpencil.surfsk_add_modifiers"
bl_label = "Add Mirror and others modifiers"
- bl_description = "Add modifiers: Mirror, Shrinkwrap, Subdivision, Solidify "
+ bl_description = "Add modifiers: Mirror, Shrinkwrap, Subdivision, Solidify"
+ bl_options = {'REGISTER', 'UNDO'}
active_object: PointerProperty(type=bpy.types.Object)
@@ -3594,7 +3635,8 @@ class GPENCIL_OT_SURFSK_add_modifiers(Operator):
bs = bpy.context.scene.bsurfaces
- bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
if bs.SURFSK_mesh == None:
self.report({'ERROR_INVALID_INPUT'}, "Please select Mesh of BSurface or click Initialize")
@@ -3667,8 +3709,12 @@ class GPENCIL_OT_SURFSK_edit_surface(Operator):
bl_idname = "gpencil.surfsk_edit_surface"
bl_label = "Bsurfaces edit surface"
bl_description = "Edit surface mesh"
+ bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.context.scene.bsurfaces.SURFSK_mesh.select_set(True)
bpy.context.view_layer.objects.active = bpy.context.scene.bsurfaces.SURFSK_mesh
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='EDIT')
@@ -3684,38 +3730,26 @@ class GPENCIL_OT_SURFSK_edit_surface(Operator):
self.execute(context)
return {"FINISHED"}
-
+
# ----------------------------
# Add strokes operator
class GPENCIL_OT_SURFSK_add_strokes(Operator):
bl_idname = "gpencil.surfsk_add_strokes"
bl_label = "Bsurfaces add strokes"
bl_description = "Add the grease pencil strokes"
+ bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
- # Determine the type of the strokes
- self.strokes_type = get_strokes_type(context)
- # Check if strokes are grease pencil strokes or a curves object
- selected_objs = bpy.context.selected_objects
- if self.strokes_type == "EXTERNAL_CURVE" or self.strokes_type == "SINGLE_CURVE_STROKE_NO_SELECTION":
- for ob in selected_objs:
- if ob != bpy.context.view_layer.objects.active:
- curve_ob = ob
-
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- curve_ob.select_set(True)
- bpy.context.view_layer.objects.active = curve_ob
+ bpy.context.scene.bsurfaces.SURFSK_gpencil.select_set(True)
+ bpy.context.view_layer.objects.active = bpy.context.scene.bsurfaces.SURFSK_gpencil
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='PAINT_GPENCIL')
+ bpy.ops.wm.tool_set_by_id(name="builtin_brush.Draw")
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
- else:
- bpy.context.scene.bsurfaces.SURFSK_gpencil.select_set(True)
- bpy.context.view_layer.objects.active = bpy.context.scene.bsurfaces.SURFSK_gpencil
- bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='PAINT_GPENCIL')
- bpy.ops.wm.tool_set_by_id(name="builtin_brush.Draw")
-
- return{"FINISHED"}
+ return{"FINISHED"}
def invoke(self, context, event):
try:
@@ -3733,69 +3767,112 @@ class GPENCIL_OT_SURFSK_add_strokes(Operator):
class GPENCIL_OT_SURFSK_edit_strokes(Operator):
bl_idname = "gpencil.surfsk_edit_strokes"
bl_label = "Bsurfaces edit strokes"
- bl_description = "Edit the grease pencil strokes or curves used"
+ bl_description = "Edit the grease pencil strokes"
+ bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
- # Determine the type of the strokes
- self.strokes_type = get_strokes_type(context)
- # Check if strokes are grease pencil strokes or a curves object
- selected_objs = bpy.context.selected_objects
- if self.strokes_type == "EXTERNAL_CURVE" or self.strokes_type == "SINGLE_CURVE_STROKE_NO_SELECTION":
- for ob in selected_objs:
- if ob != bpy.context.view_layer.objects.active:
- curve_ob = ob
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
+
+ gpencil_object = bpy.context.scene.bsurfaces.SURFSK_gpencil
+
+ gpencil_object.select_set(True)
+ bpy.context.view_layer.objects.active = gpencil_object
+
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='EDIT_GPENCIL')
+ try:
+ bpy.ops.gpencil.select_all(action='SELECT')
+ except:
+ pass
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
+ def invoke(self, context, event):
+ try:
+ bpy.context.scene.bsurfaces.SURFSK_gpencil.select_set(True)
+ except:
+ self.report({'WARNING'}, "Specify the name of the object with strokes")
+ return{"CANCELLED"}
- bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- curve_ob.select_set(True)
- bpy.context.view_layer.objects.active = curve_ob
+ self.execute(context)
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
- elif self.strokes_type == "GP_STROKES" or self.strokes_type == "SINGLE_GP_STROKE_NO_SELECTION":
- # Convert grease pencil strokes to curve
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
- #bpy.ops.gpencil.convert('INVOKE_REGION_WIN', type='CURVE', use_link_strokes=False)
- gp = bpy.context.scene.bsurfaces.SURFSK_gpencil
- conver_gpencil_to_curve(self, context, gp, 'GPensil')
- for ob in bpy.context.selected_objects:
- if ob != bpy.context.view_layer.objects.active and ob.name.startswith("GP_Layer"):
- ob_gp_strokes = ob
+ return {"FINISHED"}
- ob_gp_strokes = bpy.context.object
+# ----------------------------
+# Convert annotation to curves operator
+class GPENCIL_OT_SURFSK_annotation_to_curves(Operator):
+ bl_idname = "gpencil.surfsk_annotations_to_curves"
+ bl_label = "Convert annotation to curves"
+ bl_description = "Convert annotation to curves for editing"
+ bl_options = {'REGISTER', 'UNDO'}
- # Delete grease pencil strokes
+ def execute(self, context):
+
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+
+ # Convert annotation to curve
+ curve = conver_gpencil_to_curve(self, context, None, 'Annotation')
+
+ if curve != None:
+ # Delete annotation strokes
try:
- bpy.context.scene.bsurfaces.SURFSK_gpencil.data.layers.active.clear()
+ bpy.data.grease_pencils[0].layers.active.clear()
except:
pass
# Clean up curves
- bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
- ob_gp_strokes.select_set(True)
- bpy.context.view_layer.objects.active = ob_gp_strokes
-
- curve_crv = ob_gp_strokes.data
- bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
- bpy.ops.curve.spline_type_set('INVOKE_REGION_WIN', type="BEZIER")
- bpy.ops.curve.handle_type_set('INVOKE_REGION_WIN', type="AUTOMATIC")
- #curve_crv.show_handles = False
- #curve_crv.show_normal_face = False
+ curve.select_set(True)
+ bpy.context.view_layer.objects.active = curve
+
+ bpy.ops.wm.tool_set_by_id(name="builtin.select_box")
+
+ return {"FINISHED"}
- elif self.strokes_type == "EXTERNAL_NO_CURVE":
- self.report({'WARNING'}, "The secondary object is not a Curve.")
+ def invoke(self, context, event):
+ try:
+ strokes = bpy.data.grease_pencils[0].layers.active.active_frame.strokes
+
+ strokes_num = len(strokes)
+ except:
+ self.report({'WARNING'}, "Not active annotation")
return{"CANCELLED"}
- elif self.strokes_type == "MORE_THAN_ONE_EXTERNAL":
- self.report({'WARNING'}, "There shouldn't be more than one secondary object selected.")
- return{"CANCELLED"}
+ self.execute(context)
- elif self.strokes_type == "NO_STROKES" or self.strokes_type == "SELECTION_ALONE":
- self.report({'WARNING'}, "There aren't any strokes attached to the object")
- return{"CANCELLED"}
+ return {"FINISHED"}
- else:
- return{"CANCELLED"}
+# ----------------------------
+# Convert strokes to curves operator
+class GPENCIL_OT_SURFSK_strokes_to_curves(Operator):
+ bl_idname = "gpencil.surfsk_strokes_to_curves"
+ bl_label = "Convert strokes to curves"
+ bl_description = "Convert grease pencil strokes to curves for editing"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+
+ # Convert grease pencil strokes to curve
+ gp = bpy.context.scene.bsurfaces.SURFSK_gpencil
+ curve = conver_gpencil_to_curve(self, context, gp, 'GPensil')
+
+ if curve != None:
+ # Delete grease pencil strokes
+ try:
+ bpy.context.scene.bsurfaces.SURFSK_gpencil.data.layers.active.clear()
+ except:
+ pass
+
+ # Clean up curves
+
+ curve.select_set(True)
+ bpy.context.view_layer.objects.active = curve
+
+ bpy.ops.wm.tool_set_by_id(name="builtin.select_box")
+
+ return {"FINISHED"}
def invoke(self, context, event):
try:
@@ -3814,6 +3891,7 @@ class GPENCIL_OT_SURFSK_add_annotation(Operator):
bl_idname = "gpencil.surfsk_add_annotation"
bl_label = "Bsurfaces add annotation"
bl_description = "Add annotation"
+ bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
bpy.ops.wm.tool_set_by_id(name="builtin.annotate")
@@ -3826,6 +3904,34 @@ class GPENCIL_OT_SURFSK_add_annotation(Operator):
self.execute(context)
return {"FINISHED"}
+
+
+# ----------------------------
+# Edit curve operator
+class GPENCIL_OT_SURFSK_edit_curve(Operator):
+ bl_idname = "gpencil.surfsk_edit_curve"
+ bl_label = "Bsurfaces edit curve"
+ bl_description = "Edit curve"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+ bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
+ bpy.context.scene.bsurfaces.SURFSK_curve.select_set(True)
+ bpy.context.view_layer.objects.active = bpy.context.scene.bsurfaces.SURFSK_curve
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='EDIT')
+
+ def invoke(self, context, event):
+ try:
+ bpy.context.scene.bsurfaces.SURFSK_curve.select_set(True)
+ except:
+ self.report({'WARNING'}, "Specify the name of the object with curve")
+ return{"CANCELLED"}
+
+ self.execute(context)
+
+ return {"FINISHED"}
# ----------------------------
# Reorder splines
@@ -4204,41 +4310,51 @@ panels = (
def conver_gpencil_to_curve(self, context, pencil, type):
- newCurve = bpy.data.curves.new('gpencil_curve', type='CURVE')
+ newCurve = bpy.data.curves.new(type + '_curve', type='CURVE')
newCurve.dimensions = '3D'
CurveObject = object_utils.object_data_add(context, newCurve)
+ error = False
if type == 'GPensil':
- strokes = pencil.data.layers.active.active_frame.strokes
+ try:
+ strokes = pencil.data.layers.active.active_frame.strokes
+ except:
+ error = True
CurveObject.location = pencil.location
CurveObject.rotation_euler = pencil.rotation_euler
CurveObject.scale = pencil.scale
elif type == 'Annotation':
grease_pencil = bpy.data.grease_pencils[0]
- strokes = grease_pencil.layers.active.active_frame.strokes
+ try:
+ strokes = grease_pencil.layers.active.active_frame.strokes
+ except:
+ error = True
CurveObject.location = (0.0, 0.0, 0.0)
CurveObject.rotation_euler = (0.0, 0.0, 0.0)
CurveObject.scale = (1.0, 1.0, 1.0)
- for i, stroke in enumerate(strokes):
- stroke_points = strokes[i].points
- data_list = [ (point.co.x, point.co.y, point.co.z)
- for point in stroke_points ]
- points_to_add = len(data_list)-1
+ if not error:
+ for i, stroke in enumerate(strokes):
+ stroke_points = strokes[i].points
+ data_list = [ (point.co.x, point.co.y, point.co.z)
+ for point in stroke_points ]
+ points_to_add = len(data_list)-1
- flat_list = []
- for point in data_list:
- flat_list.extend(point)
+ flat_list = []
+ for point in data_list:
+ flat_list.extend(point)
- spline = newCurve.splines.new(type='BEZIER')
- spline.bezier_points.add(points_to_add)
- spline.bezier_points.foreach_set("co", flat_list)
+ spline = newCurve.splines.new(type='BEZIER')
+ spline.bezier_points.add(points_to_add)
+ spline.bezier_points.foreach_set("co", flat_list)
- for point in spline.bezier_points:
- point.handle_left_type="AUTO"
- point.handle_right_type="AUTO"
+ for point in spline.bezier_points:
+ point.handle_left_type="AUTO"
+ point.handle_right_type="AUTO"
- return CurveObject
+ return CurveObject
+ else:
+ return None
def update_panel(self, context):
@@ -4341,6 +4457,41 @@ def update_in_front(self, context):
bpy.data.objects[global_mesh_object].show_in_front = global_in_front
except Exception as e:
print("Select mesh object")
+
+def update_show_wire(self, context):
+ try:
+ global global_show_wire
+ global_show_wire = bpy.context.scene.bsurfaces.SURFSK_show_wire
+ global global_mesh_object
+ bpy.data.objects[global_mesh_object].show_wire = global_show_wire
+ except Exception as e:
+ print("Select mesh object")
+
+def update_shade_smooth(self, context):
+ try:
+ global global_shade_smooth
+ global_shade_smooth = bpy.context.scene.bsurfaces.SURFSK_shade_smooth
+
+ contex_mode = bpy.context.mode
+
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
+
+ bpy.ops.object.select_all(action='DESELECT')
+ global global_mesh_object
+ global_mesh_object = bpy.context.scene.bsurfaces.SURFSK_mesh.name
+ bpy.data.objects[global_mesh_object].select_set(True)
+
+ if global_shade_smooth:
+ bpy.ops.object.shade_smooth()
+ else:
+ bpy.ops.object.shade_flat()
+
+ if contex_mode == "EDIT_MESH":
+ bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
+
+ except Exception as e:
+ print("Select mesh object")
class BsurfPreferences(AddonPreferences):
@@ -4441,7 +4592,7 @@ class BsurfacesProps(PropertyGroup):
)
SURFSK_mesh_color: FloatVectorProperty(
name="Mesh color",
- default=(1.0, 0.0, 0.0, 1.0),
+ default=(1.0, 0.0, 0.0, 0.3),
size=4,
subtype="COLOR",
min=0,
@@ -4462,6 +4613,18 @@ class BsurfacesProps(PropertyGroup):
default=False,
update=update_in_front,
)
+ SURFSK_show_wire: BoolProperty(
+ name="Show wire",
+ description="Add the object’s wireframe over solid drawing",
+ default=False,
+ update=update_show_wire,
+ )
+ SURFSK_shade_smooth: BoolProperty(
+ name="Shade smooth",
+ description="Render and display faces smooth, using interpolated Vertex Normals",
+ default=False,
+ update=update_shade_smooth,
+ )
classes = (
GPENCIL_OT_SURFSK_init,
@@ -4470,7 +4633,10 @@ classes = (
GPENCIL_OT_SURFSK_edit_surface,
GPENCIL_OT_SURFSK_add_strokes,
GPENCIL_OT_SURFSK_edit_strokes,
+ GPENCIL_OT_SURFSK_strokes_to_curves,
+ GPENCIL_OT_SURFSK_annotation_to_curves,
GPENCIL_OT_SURFSK_add_annotation,
+ GPENCIL_OT_SURFSK_edit_curve,
CURVE_OT_SURFSK_reorder_splines,
CURVE_OT_SURFSK_first_points,
BsurfPreferences,
diff --git a/mesh_looptools.py b/mesh_looptools.py
index 41c1be0a..90c914cd 100644
--- a/mesh_looptools.py
+++ b/mesh_looptools.py
@@ -19,7 +19,7 @@
bl_info = {
"name": "LoopTools",
"author": "Bart Crouch",
- "version": (4, 6, 7),
+ "version": (4, 6, 9),
"blender": (2, 80, 0),
"location": "View3D > Sidebar > Edit Tab / Edit Mode Context Menu",
"warning": "",
@@ -290,8 +290,10 @@ def calculate_plane(bm_mod, loop, method="best_fit", object=False):
for i in range(itermax):
vec = vec2
vec2 = mat @ vec
- if vec2.length != 0:
- vec2 /= vec2.length
+ # Calculate length with double precision to avoid problems with `inf`
+ vec2_length = math.sqrt(vec2[0] ** 2 + vec2[1] ** 2 + vec2[2] ** 2)
+ if vec2_length != 0:
+ vec2 /= vec2_length
if vec2 == vec:
break
if vec2.length == 0:
@@ -536,6 +538,10 @@ def get_derived_bmesh(object, bm):
for mod in object.modifiers:
if mod.type != 'MIRROR':
mod.show_viewport = False
+ #leave the merge points untouched
+ if mod.type == 'MIRROR':
+ merge = mod.use_mirror_merge
+ mod.use_mirror_merge = False
# get derived mesh
bm_mod = bmesh.new()
depsgraph = bpy.context.evaluated_depsgraph_get()
@@ -546,6 +552,8 @@ def get_derived_bmesh(object, bm):
# re-enable other modifiers
for mod_name in show_viewport:
object.modifiers[mod_name].show_viewport = True
+ if mod.type == 'MIRROR':
+ mod.use_mirror_merge = merge
# no mirror modifiers, so no derived mesh necessary
else:
derived = False
diff --git a/mesh_snap_utilities_line/common_utilities.py b/mesh_snap_utilities_line/common_utilities.py
index 5a11a4c4..9c63c3f2 100644
--- a/mesh_snap_utilities_line/common_utilities.py
+++ b/mesh_snap_utilities_line/common_utilities.py
@@ -143,6 +143,8 @@ def get_snap_bm_geom(sctx, main_snap_obj, mcursor):
if l.link_loop_next.vert == tri[i] or l.link_loop_prev.vert == tri[i - 2]:
r_bm_geom = l.face
break
+ if r_loc is None:
+ r_loc = r_elem_co[0]
return r_snp_obj, r_loc, r_elem, r_elem_co, r_view_vector, r_orig, r_bm, r_bm_geom
diff --git a/mesh_snap_utilities_line/preferences.py b/mesh_snap_utilities_line/preferences.py
index 7f27507a..98a213be 100644
--- a/mesh_snap_utilities_line/preferences.py
+++ b/mesh_snap_utilities_line/preferences.py
@@ -52,7 +52,7 @@ class SnapUtilitiesPreferences(bpy.types.AddonPreferences):
default=False)
auto_constrain : BoolProperty(name="Automatic Constraint",
- description="Detects a direction to constrain depending on the position of the mouse.",
+ description="Detects a direction to constrain depending on the position of the mouse",
default=False)
incremental : FloatProperty(name="Incremental",
diff --git a/mesh_snap_utilities_line/snap_context_l/__init__.py b/mesh_snap_utilities_line/snap_context_l/__init__.py
index 9d879a82..3c09e537 100644
--- a/mesh_snap_utilities_line/snap_context_l/__init__.py
+++ b/mesh_snap_utilities_line/snap_context_l/__init__.py
@@ -331,8 +331,10 @@ class SnapContext():
if index < num_tris:
tri_verts = gpu_data.get_tri_verts(index)
tri_co = [snap_obj.mat @ Vector(v) for v in gpu_data.get_tri_co(index)]
- nor = (tri_co[1] - tri_co[0]).cross(tri_co[2] - tri_co[0])
- return _Internal.intersect_line_plane(self.last_ray[1], self.last_ray[1] + self.last_ray[0], tri_co[0], nor), tri_verts, tri_co
+ #loc = _Internal.intersect_ray_tri(*tri_co, self.last_ray[0], self.last_ray[1], False)
+ nor = (tri_co[1] - tri_co[0]).cross(tri_co[2] - tri_co[0]).normalized()
+ loc = _Internal.intersect_line_plane(self.last_ray[1], self.last_ray[1] + self.last_ray[0], tri_co[0], nor)
+ return loc, tri_verts, tri_co
index -= num_tris
diff --git a/node_wrangler.py b/node_wrangler.py
index 62eb8798..41a83969 100644
--- a/node_wrangler.py
+++ b/node_wrangler.py
@@ -1027,7 +1027,7 @@ class NWPrincipledPreferences(bpy.types.PropertyGroup):
description='Naming Components for roughness maps')
gloss: StringProperty(
name='Gloss',
- default='gloss glossy glossyness',
+ default='gloss glossy glossiness',
description='Naming Components for glossy maps')
displacement: StringProperty(
name='Displacement',
diff --git a/object_print3d_utils/__init__.py b/object_print3d_utils/__init__.py
index c654940f..d567bd77 100644
--- a/object_print3d_utils/__init__.py
+++ b/object_print3d_utils/__init__.py
@@ -21,11 +21,10 @@
bl_info = {
"name": "3D-Print Toolbox",
"author": "Campbell Barton",
- "blender": (2, 80, 0),
+ "blender": (2, 82, 0),
"location": "3D View > Sidebar",
"description": "Utilities for 3D printing",
- # TODO
- # "wiki_url": "",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/mesh/3d_print_toolbox.html",
"support": 'OFFICIAL',
"category": "Mesh",
}
@@ -36,6 +35,8 @@ if "bpy" in locals():
importlib.reload(ui)
importlib.reload(operators)
importlib.reload(mesh_helpers)
+ if "export" in locals():
+ importlib.reload(export)
else:
import math
@@ -62,7 +63,6 @@ class SceneProperties(PropertyGroup):
items=(
('STL', "STL", ""),
('PLY', "PLY", ""),
- ('WRL', "VRML2", ""),
('X3D', "X3D", ""),
('OBJ', "OBJ", ""),
),
diff --git a/object_print3d_utils/export.py b/object_print3d_utils/export.py
index a294512b..2503c2f5 100644
--- a/object_print3d_utils/export.py
+++ b/object_print3d_utils/export.py
@@ -20,23 +20,39 @@
# Export wrappers and integration with external tools.
-import os
import bpy
+def image_get(mat):
+ from bpy_extras import node_shader_utils
+
+ if mat.use_nodes:
+ mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat)
+ base_color_tex = mat_wrap.base_color_texture
+ if base_color_tex and base_color_tex.image:
+ return base_color_tex.image
+
+
def image_copy_guess(filepath, objects):
# 'filepath' is the path we are writing to.
- import shutil
- from bpy_extras import object_utils
-
image = None
+ mats = set()
+
for obj in objects:
- image = object_utils.object_image_guess(obj)
+ for slot in obj.material_slots:
+ if slot.material:
+ mats.add(slot.material)
+
+ for mat in mats:
+ image = image_get(mat)
if image is not None:
break
if image is not None:
+ import os
+ import shutil
+
imagepath = bpy.path.abspath(image.filepath, library=image.library)
if os.path.exists(imagepath):
filepath_noext = os.path.splitext(filepath)[0]
@@ -53,35 +69,18 @@ def image_copy_guess(filepath, objects):
def write_mesh(context, report_cb):
+ import os
+
scene = context.scene
- collection = context.collection
layer = context.view_layer
unit = scene.unit_settings
print_3d = scene.print_3d
- obj = layer.objects.active
-
export_format = print_3d.export_format
global_scale = unit.scale_length if (unit.system != 'NONE' and print_3d.use_apply_scale) else 1.0
path_mode = 'COPY' if print_3d.use_export_texture else 'AUTO'
-
- context_override = context.copy()
- obj_tmp = None
-
- # PLY can only export single mesh objects!
- if export_format == 'PLY':
- context_backup = context.copy()
- bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
-
- from . import mesh_helpers
- obj_tmp = mesh_helpers.object_merge(context, context_override["selected_objects"])
- context_override["active_object"] = obj_tmp
- context_override["selected_objects"] = [obj_tmp]
- else:
- if obj not in context_override["selected_objects"]:
- context_override["selected_objects"].append(obj)
-
export_path = bpy.path.abspath(print_3d.export_path)
+ obj = layer.objects.active
# Create name 'export_path/blendname-objname'
# add the filename component
@@ -119,7 +118,6 @@ def write_mesh(context, report_cb):
addon_ensure("io_mesh_stl")
filepath = bpy.path.ensure_ext(filepath, ".stl")
ret = bpy.ops.export_mesh.stl(
- context_override,
filepath=filepath,
ascii=False,
use_mesh_modifiers=True,
@@ -130,27 +128,15 @@ def write_mesh(context, report_cb):
addon_ensure("io_mesh_ply")
filepath = bpy.path.ensure_ext(filepath, ".ply")
ret = bpy.ops.export_mesh.ply(
- context_override,
filepath=filepath,
use_mesh_modifiers=True,
+ use_selection=True,
global_scale=global_scale,
)
elif export_format == 'X3D':
addon_ensure("io_scene_x3d")
filepath = bpy.path.ensure_ext(filepath, ".x3d")
ret = bpy.ops.export_scene.x3d(
- context_override,
- filepath=filepath,
- use_mesh_modifiers=True,
- use_selection=True,
- path_mode=path_mode,
- global_scale=global_scale,
- )
- elif export_format == 'WRL':
- addon_ensure("io_scene_vrml2")
- filepath = bpy.path.ensure_ext(filepath, ".wrl")
- ret = bpy.ops.export_scene.vrml2(
- context_override,
filepath=filepath,
use_mesh_modifiers=True,
use_selection=True,
@@ -161,7 +147,6 @@ def write_mesh(context, report_cb):
addon_ensure("io_scene_obj")
filepath = bpy.path.ensure_ext(filepath, ".obj")
ret = bpy.ops.export_scene.obj(
- context_override,
filepath=filepath,
use_mesh_modifiers=True,
use_selection=True,
@@ -174,20 +159,7 @@ def write_mesh(context, report_cb):
# for formats that don't support images
if export_format in {'STL', 'PLY'}:
if path_mode == 'COPY':
- image_copy_guess(filepath, context_override["selected_objects"])
-
- if obj_tmp is not None:
- obj = obj_tmp
- mesh = obj.data
- collection.objects.unlink(obj)
- bpy.data.objects.remove(obj)
- bpy.data.meshes.remove(mesh)
-
- # restore context
- for ob in context_backup["selected_objects"]:
- ob.select_set(True)
-
- layer.objects.active = context_backup["active_object"]
+ image_copy_guess(filepath, context.selected_objects)
if 'FINISHED' in ret:
if report_cb is not None:
diff --git a/object_print3d_utils/mesh_helpers.py b/object_print3d_utils/mesh_helpers.py
index 37170e53..165b0fc1 100644
--- a/object_print3d_utils/mesh_helpers.py
+++ b/object_print3d_utils/mesh_helpers.py
@@ -20,6 +20,7 @@
# Generic helper functions, to be used by any modules.
+
import bmesh
@@ -192,67 +193,6 @@ def bmesh_check_thick_object(obj, thickness):
return array.array('i', faces_error)
-def object_merge(context, objects):
- """Caller must remove."""
-
- import bpy
-
- def cd_remove_all_but_active(seq):
- tot = len(seq)
- if tot > 1:
- act = seq.active_index
- for i in range(tot - 1, -1, -1):
- if i != act:
- seq.remove(seq[i])
-
- scene = context.scene
- layer = context.view_layer
- scene_collection = context.layer_collection.collection
-
- # deselect all
- for obj in scene.objects:
- obj.select_set(False)
-
- # add empty object
- mesh_tmp = bpy.data.meshes.new(name="~tmp~")
- obj_tmp = bpy.data.objects.new(name="~tmp~", object_data=mesh_tmp)
- scene_collection.objects.link(obj_tmp)
- obj_tmp.select_set(True)
-
- depsgraph = context.evaluated_depsgraph_get()
-
- # loop over all meshes
- for obj in objects:
- if obj.type != 'MESH':
- continue
-
- # convert each to a mesh
- obj_eval = obj.evaluated_get(depsgraph)
- mesh_new = obj_eval.to_mesh().copy()
-
- # remove non-active uvs/vcols
- cd_remove_all_but_active(mesh_new.vertex_colors)
- cd_remove_all_but_active(mesh_new.uv_layers)
-
- # join into base mesh
- obj_new = bpy.data.objects.new(name="~tmp-new~", object_data=mesh_new)
- scene_collection.objects.link(obj_new)
- obj_new.matrix_world = obj.matrix_world
-
- override = context.copy()
- override["active_object"] = obj_tmp
- override["selected_editable_objects"] = [obj_tmp, obj_new]
-
- bpy.ops.object.join(override)
-
- bpy.data.meshes.remove(mesh_new)
- obj_eval.to_mesh_clear()
-
- layer.update()
-
- return obj_tmp
-
-
def face_is_distorted(ele, angle_distort):
no = ele.normal
angle_fn = no.angle
diff --git a/object_print3d_utils/operators.py b/object_print3d_utils/operators.py
index e82846f3..3df5e0f7 100644
--- a/object_print3d_utils/operators.py
+++ b/object_print3d_utils/operators.py
@@ -20,6 +20,7 @@
# All Operator
+
import math
import bpy
diff --git a/object_print3d_utils/ui.py b/object_print3d_utils/ui.py
index 353060fe..af3aa0df 100644
--- a/object_print3d_utils/ui.py
+++ b/object_print3d_utils/ui.py
@@ -20,6 +20,7 @@
# Interface for this addon.
+
from bpy.types import Panel
import bmesh
diff --git a/object_scatter/operator.py b/object_scatter/operator.py
index 742eec95..a01ca765 100644
--- a/object_scatter/operator.py
+++ b/object_scatter/operator.py
@@ -318,6 +318,8 @@ def scatter_from_source_point(bvhtree, point, seed, settings):
assert location is not None
normal.normalize()
+ up_direction = normal if settings.use_normal_rotation else Vector((0, 0, 1))
+
# Scale
min_scale = settings.scale * (1 - settings.random_scale)
max_scale = settings.scale
@@ -328,9 +330,9 @@ def scatter_from_source_point(bvhtree, point, seed, settings):
# Rotation
z_rotation = Euler((0, 0, random_uniform(sub_seed(seed, 3), 0, 2 * math.pi))).to_matrix()
- normal_rotation = normal.to_track_quat('Z', 'X').to_matrix()
+ up_rotation = up_direction.to_track_quat('Z', 'X').to_matrix()
local_rotation = random_euler(sub_seed(seed, 3), settings.rotation).to_matrix()
- rotation = local_rotation @ normal_rotation @ z_rotation
+ rotation = local_rotation @ up_rotation @ z_rotation
return Matrix.Translation(location) @ rotation.to_4x4() @ scale_matrix(scale)
diff --git a/object_scatter/ui.py b/object_scatter/ui.py
index d62b8e38..b021bf3d 100644
--- a/object_scatter/ui.py
+++ b/object_scatter/ui.py
@@ -22,6 +22,7 @@ import math
from collections import namedtuple
from bpy.props import (
+ BoolProperty,
IntProperty,
FloatProperty,
PointerProperty
@@ -30,7 +31,7 @@ from bpy.props import (
ScatterSettings = namedtuple("ScatterSettings",
["seed", "density", "radius", "scale", "random_scale",
- "rotation", "normal_offset"])
+ "rotation", "normal_offset", "use_normal_rotation"])
class ObjectScatterProperties(bpy.types.PropertyGroup):
seed: IntProperty(
@@ -94,6 +95,12 @@ class ObjectScatterProperties(bpy.types.PropertyGroup):
description="Distance from the surface",
)
+ use_normal_rotation: BoolProperty(
+ name="Use Normal Rotation",
+ default=True,
+ description="Rotate the instances according to the surface normals",
+ )
+
def to_settings(self):
return ScatterSettings(
seed=self.seed,
@@ -103,6 +110,7 @@ class ObjectScatterProperties(bpy.types.PropertyGroup):
random_scale=self.random_scale_percentage / 100,
rotation=self.rotation,
normal_offset=self.normal_offset,
+ use_normal_rotation=self.use_normal_rotation,
)
@@ -125,6 +133,7 @@ class ObjectScatterPanel(bpy.types.Panel):
col.prop(scatter, "scale", slider=True)
col.prop(scatter, "random_scale_percentage", text="Randomness", slider=True)
+ layout.prop(scatter, "use_normal_rotation")
layout.prop(scatter, "rotation", slider=True)
layout.prop(scatter, "normal_offset", text="Offset", slider=True)
layout.prop(scatter, "seed")
diff --git a/render_povray/__init__.py b/render_povray/__init__.py
index 30f18340..ed6f8569 100644
--- a/render_povray/__init__.py
+++ b/render_povray/__init__.py
@@ -19,13 +19,14 @@
# <pep8 compliant>
bl_info = {
+ #coming soon: "name": "POV-3.8",
"name": "POV-3.7",
"author": "Campbell Barton, Maurice Raybaud, Leonid Desyatkov, "
"Bastien Montagne, Constantin Rahn, Silvio Falcinelli",
"version": (0, 1, 0),
"blender": (2, 80, 0),
- "location": "Render > Engine > POV-Ray 3.7",
- "description": "POV-Ray 3.7 integration for blender",
+ "location": "Render > Engine > Persistence Of Vision",
+ "description": "POV-Ray integration for blender",
"wiki_url": "https://archive.blender.org/wiki/index.php/"
"Extensions:2.6/Py/Scripts/Render/POV-Ray/",
"category": "Render",
@@ -68,6 +69,28 @@ else:
def string_strip_hyphen(name):
return name.replace("-", "")
+
+def active_texture_name_from_uilist(self,context):
+ mat = context.scene.view_layers["View Layer"].objects.active.active_material
+ index = mat.pov.active_texture_index
+ name = mat.pov_texture_slots[index].name
+ newname = mat.pov_texture_slots[index].texture
+ tex = bpy.data.textures[name]
+ tex.name = newname
+ mat.pov_texture_slots[index].name = newname
+
+
+def active_texture_name_from_search(self,context):
+ mat = context.scene.view_layers["View Layer"].objects.active.active_material
+ index = mat.pov.active_texture_index
+ name = mat.pov_texture_slots[index].texture_search
+ try:
+ tex = bpy.data.textures[name]
+ mat.pov_texture_slots[index].name = name
+ mat.pov_texture_slots[index].texture = name
+ except:
+ pass
+
###############################################################################
# Scene POV properties.
###############################################################################
@@ -557,6 +580,11 @@ class RenderPovSettingsScene(PropertyGroup):
# Material POV properties.
###############################################################################
class MaterialTextureSlot(PropertyGroup):
+ bl_idname="povray_texture_slots",
+ bl_description="Texture_slots from Blender-2.79",
+
+ texture : StringProperty(update=active_texture_name_from_uilist)
+ texture_search : StringProperty(update=active_texture_name_from_search)
alpha_factor: FloatProperty(
name="Alpha",
@@ -4293,6 +4321,29 @@ for i in range(18): # length of world texture slots
world.texture_slots.add()
'''
+class MATERIAL_TEXTURE_SLOTS_UL_layerlist(bpy.types.UIList):
+# texture_slots:
+ index: bpy.props.IntProperty(name='index')
+ #foo = random prop
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ ob = data
+ slot = item
+ #ma = slot.name
+ # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ # You should always start your row layout by a label (icon + text), or a non-embossed text field,
+ # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
+ # We use icon_value of label, as our given icon is an integer value, not an enum ID.
+ # Note "data" names should never be translated!
+ if slot:
+ layout.prop(item, "texture", text="", emboss=False, icon='TEXTURE')
+ else:
+ layout.label(text="New", translate=False, icon_value=icon)
+ # 'GRID' layout type should be as compact as possible (typically a single icon!).
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
###############################################################################
# Text POV properties.
@@ -4344,6 +4395,7 @@ classes = (
RenderPovSettingsCamera,
RenderPovSettingsLight,
RenderPovSettingsWorld,
+ MATERIAL_TEXTURE_SLOTS_UL_layerlist,
MaterialTextureSlot,
WorldTextureSlot,
RenderPovSettingsMaterial,
@@ -4397,7 +4449,7 @@ def register():
bpy.types.Camera.pov = PointerProperty(type=RenderPovSettingsCamera)
bpy.types.Light.pov = PointerProperty(type=RenderPovSettingsLight)
bpy.types.World.pov = PointerProperty(type=RenderPovSettingsWorld)
- bpy.types.Material.texture_slots = CollectionProperty(type = MaterialTextureSlot)
+ bpy.types.Material.pov_texture_slots = CollectionProperty(type=MaterialTextureSlot)
bpy.types.World.texture_slots = CollectionProperty(type = WorldTextureSlot)
bpy.types.Text.pov = PointerProperty(type=RenderPovSettingsText)
@@ -4415,7 +4467,8 @@ def unregister():
del bpy.types.Object.pov
del bpy.types.Camera.pov
del bpy.types.Light.pov
- del bpy.types.World.pov
+ del bpy.types.World.pov
+ del bpy.types.Material.pov_texture_slots
del bpy.types.Text.pov
nodeitems_utils.unregister_node_categories("POVRAYNODES")
diff --git a/render_povray/render.py b/render_povray/render.py
index 56251988..7fd8fca5 100644
--- a/render_povray/render.py
+++ b/render_povray/render.py
@@ -3784,6 +3784,7 @@ def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_imag
class PovrayRender(bpy.types.RenderEngine):
bl_idname = 'POVRAY_RENDER'
bl_label = "Persitence Of Vision"
+ bl_use_shading_nodes_custom = False
DELAY = 0.5
@staticmethod
diff --git a/render_povray/ui.py b/render_povray/ui.py
index 5f415727..038c632f 100644
--- a/render_povray/ui.py
+++ b/render_povray/ui.py
@@ -214,6 +214,15 @@ for member in dir(properties_particle): # add all "particle" panels from blende
pass
del properties_particle
+# Example of wrapping every class 'as is'
+from bl_ui import properties_output
+for member in dir(properties_output):
+ subclass = getattr(properties_output, member)
+ try:
+ subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ except:
+ pass
+del properties_output
class POV_WORLD_MT_presets(bpy.types.Menu):
bl_label = "World Presets"
@@ -313,7 +322,7 @@ def pov_context_tex_datablock(context):
if idblock:
return active_node_mat(idblock)
- idblock = context.lamp
+ idblock = context.light
if idblock:
return idblock
@@ -1724,7 +1733,7 @@ class MATERIAL_PT_strand(MaterialButtonsPanel, Panel):
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES)
+ return mat and (mat.pov.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -1785,6 +1794,27 @@ class TEXTURE_MT_specials(bpy.types.Menu):
layout.operator("texture.slot_copy", icon='COPYDOWN')
layout.operator("texture.slot_paste", icon='PASTEDOWN')
+class TEXTURE_UL_texture_slots(bpy.types.UIList):
+ COMPAT_ENGINES = {'POVRAY_RENDER'}
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ ob = data
+ slot = item
+ #ma = slot.name
+ # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ # You should always start your row layout by a label (icon + text), or a non-embossed text field,
+ # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
+ # We use icon_value of label, as our given icon is an integer value, not an enum ID.
+ # Note "data" names should never be translated!
+ if slot:
+ layout.prop(item, "texture", text="", emboss=False, icon='TEXTURE')
+ else:
+ layout.label(text="New", translate=False, icon_value=icon)
+ # 'GRID' layout type should be as compact as possible (typically a single icon!).
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+'''
class MATERIAL_TEXTURE_SLOTS_UL_List(UIList):
"""Texture Slots UIList."""
@@ -1806,7 +1836,7 @@ class MATERIAL_TEXTURE_SLOTS_UL_List(UIList):
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label("", icon = custom_icon)
-
+'''
class WORLD_TEXTURE_SLOTS_UL_List(UIList):
"""Texture Slots UIList."""
@@ -1837,6 +1867,7 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
+ return (engine in cls.COMPAT_ENGINES)
# if not (hasattr(context, "texture_slot") or hasattr(context, "texture_node")):
# return False
return ((context.material or
@@ -1851,7 +1882,30 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
-
+
+ scene = context.scene
+ layout.prop(scene, "texture_context", expand=True)
+ if scene.texture_context == 'MATERIAL':
+ mat = context.scene.view_layers["View Layer"].objects.active.active_material
+ row = layout.row()
+ row.template_list("MATERIAL_TEXTURE_SLOTS_UL_layerlist", "", mat, "pov_texture_slots", mat.pov, "active_texture_index")
+ col = row.column(align=True)
+ col.operator("pov.textureslotadd",icon='ADD',text='')
+ col.operator("pov.textureslotremove",icon='REMOVE',text='')
+ col.separator()
+
+ if mat.pov_texture_slots:
+ index = mat.pov.active_texture_index
+ slot = mat.pov_texture_slots[index]
+ povtex = slot.name
+ tex = bpy.data.textures[povtex]
+ col.prop(tex,'use_fake_user',text = '')
+ layout.label(text='Find texture:')
+ layout.prop_search(slot,'texture_search',bpy.data,'textures',text='')
+ # else:
+ # for i in range(18): # length of material texture slots
+ # mat.pov_texture_slots.add()
+'''
slot = getattr(context, "texture_slot", None)
node = getattr(context, "texture_node", None)
space = context.space_data
@@ -1956,7 +2010,7 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
split.prop(slot, "output_node", text="")
else:
split.label(text="Type:")
-
+'''
class TEXTURE_PT_colors(TextureButtonsPanel, Panel):
bl_label = "Colors"
bl_options = {'DEFAULT_CLOSED'}
@@ -1992,12 +2046,46 @@ class TEXTURE_PT_colors(TextureButtonsPanel, Panel):
# Texture Slot Panels #
+class POV_OT_texture_slot_add(Operator):
+ bl_idname = "pov.textureslotadd"
+ bl_label = "Add"
+ bl_description = "Add texture_slot"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {'POVRAY_RENDER'}
+
+ def execute(self,context):
+
+ tex = bpy.data.textures.new(name = 'Texture',type = 'IMAGE')
+ tex.use_fake_user = True
+ ob = context.scene.view_layers["View Layer"].objects.active
+ slot = ob.active_material.pov_texture_slots.add()
+ slot.name = tex.name
+ slot.texture = tex.name
+
+ return {'FINISHED'}
+
+
+class POV_OT_texture_slot_remove(Operator):
+ bl_idname = "pov.textureslotremove"
+ bl_label = "Remove"
+ bl_description = "Remove texture_slot"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {'POVRAY_RENDER'}
+
+ def execute(self,context):
+ pass
+ # tex = bpy.data.textures.new()
+ # tex_slot = context.object.active_material.pov_texture_slots.add()
+ # tex_slot.name = tex.name
+
+ return {'FINISHED'}
+
class TextureSlotPanel(TextureButtonsPanel):
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
- if not hasattr(context, "texture_slot"):
+ if not hasattr(context, "pov_texture_slot"):
return False
engine = context.scene.render.engine
@@ -2030,7 +2118,7 @@ class TEXTURE_PT_povray_preview(TextureButtonsPanel, Panel):
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
- if not hasattr(context, "texture_slot"):
+ if not hasattr(context, "pov_texture_slot"):
return False
tex=context.texture
mat=context.material
@@ -2038,8 +2126,8 @@ class TEXTURE_PT_povray_preview(TextureButtonsPanel, Panel):
def draw(self, context):
tex = context.texture
- slot = getattr(context, "texture_slot", None)
- idblock = context_tex_datablock(context)
+ slot = getattr(context, "pov_texture_slot", None)
+ idblock = pov_context_tex_datablock(context)
layout = self.layout
# if idblock:
# layout.template_preview(tex, parent=idblock, slot=slot)
@@ -2263,11 +2351,11 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
@classmethod
def poll(cls, context):
- idblock = context_tex_datablock(context)
+ idblock = pov_context_tex_datablock(context)
if isinstance(idblock, Brush):
return False
- if not getattr(context, "texture_slot", None):
+ if not getattr(context, "pov_texture_slot", None):
return False
engine = context.scene.render.engine
@@ -2277,9 +2365,11 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
layout = self.layout
- idblock = context_tex_datablock(context)
+ idblock = pov_context_tex_datablock(context)
- tex = context.texture_slot
+ # tex = context.pov_texture_slot
+ mat = context.material
+ tex = bpy.data.textures[mat.pov_texture_slots[mat.pov.active_texture_index]]
def factor_but(layout, toggle, factor, name):
row = layout.row(align=True)
@@ -2290,7 +2380,7 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
return sub # XXX, temp. use_map_normal needs to override.
if isinstance(idblock, Material):
- if idblock.type in {'SURFACE', 'WIRE'}:
+ if idblock.pov.type in {'SURFACE', 'WIRE'}:
split = layout.split()
col = split.column()
@@ -2324,7 +2414,7 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
# ~ sub = col.column()
# ~ sub.active = tex.use_map_translucency or tex.map_emit or tex.map_alpha or tex.map_raymir or tex.map_hardness or tex.map_ambient or tex.map_specularity or tex.map_reflection or tex.map_mirror
#~ sub.prop(tex, "default_value", text="Amount", slider=True)
- elif idblock.type == 'HALO':
+ elif idblock.pov.type == 'HALO':
layout.label(text="Halo:")
split = layout.split()
@@ -2337,7 +2427,7 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
factor_but(col, "use_map_raymir", "raymir_factor", "Size")
factor_but(col, "use_map_hardness", "hardness_factor", "Hardness")
factor_but(col, "use_map_translucency", "translucency_factor", "Add")
- elif idblock.type == 'VOLUME':
+ elif idblock.pov.type == 'VOLUME':
layout.label(text="Volume:")
split = layout.split()
@@ -3234,7 +3324,13 @@ classes = (
TEXT_OT_povray_insert,
TEXT_MT_insert,
TEXT_PT_povray_custom_code,
- TEXT_MT_templates_pov
+ TEXT_MT_templates_pov,
+ # TEXTURE_PT_context,
+ #TEXTURE_PT_POV_povray_texture_slots,
+ TEXTURE_UL_texture_slots,
+ POV_OT_texture_slot_add,
+ POV_OT_texture_slot_remove,
+ TEXTURE_PT_influence
)
diff --git a/rigify/__init__.py b/rigify/__init__.py
index 803c19c0..8363d037 100644
--- a/rigify/__init__.py
+++ b/rigify/__init__.py
@@ -20,7 +20,7 @@
bl_info = {
"name": "Rigify",
- "version": (0, 6, 0),
+ "version": (0, 6, 1),
"author": "Nathan Vegdahl, Lucio Rossi, Ivan Cappiello, Alexander Gavrilov",
"blender": (2, 81, 0),
"description": "Automatic rigging from building-block components",
@@ -520,7 +520,7 @@ def register():
IDStore.rigify_types = CollectionProperty(type=RigifyName)
IDStore.rigify_active_type = IntProperty(name="Rigify Active Type", description="The selected rig type")
- IDStore.rigify_advanced_generation = BoolProperty(name="Advanced Options",
+ bpy.types.Armature.rigify_advanced_generation = BoolProperty(name="Advanced Options",
description="Enables/disables advanced options for Rigify rig generation",
default=False)
@@ -528,27 +528,26 @@ def register():
if self.rigify_generate_mode == 'new':
self.rigify_force_widget_update = False
- IDStore.rigify_generate_mode = EnumProperty(name="Rigify Generate Rig Mode",
+ bpy.types.Armature.rigify_generate_mode = EnumProperty(name="Rigify Generate Rig Mode",
description="'Generate Rig' mode. In 'overwrite' mode the features of the target rig will be updated as defined by the metarig. In 'new' mode a new rig will be created as defined by the metarig. Current mode",
update=update_mode,
items=( ('overwrite', 'overwrite', ''),
('new', 'new', '')))
- IDStore.rigify_force_widget_update = BoolProperty(name="Force Widget Update",
+ bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Force Widget Update",
description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created",
default=False)
- IDStore.rigify_target_rigs = CollectionProperty(type=RigifyName)
- IDStore.rigify_target_rig = StringProperty(name="Rigify Target Rig",
+ 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",
- default="")
+ poll=lambda self, obj: obj.type == 'ARMATURE' and obj.data is not self)
- IDStore.rigify_rig_uis = CollectionProperty(type=RigifyName)
- IDStore.rigify_rig_ui = StringProperty(name="Rigify Target Rig UI",
- description="Defines the UI to overwrite. It should always be the same as the target rig. If unset, 'rig_ui.py' will be used",
- default="")
+ bpy.types.Armature.rigify_rig_ui = PointerProperty(type=bpy.types.Text,
+ name="Rigify Target Rig UI",
+ description="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used")
- IDStore.rigify_rig_basename = StringProperty(name="Rigify Rig Name",
+ 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="")
@@ -602,19 +601,17 @@ def unregister():
del ArmStore.rigify_colors_index
del ArmStore.rigify_colors_lock
del ArmStore.rigify_theme_to_add
+ del ArmStore.rigify_advanced_generation
+ del ArmStore.rigify_generate_mode
+ del ArmStore.rigify_force_widget_update
+ del ArmStore.rigify_target_rig
+ del ArmStore.rigify_rig_ui
+ del ArmStore.rigify_rig_basename
IDStore = bpy.types.WindowManager
del IDStore.rigify_collection
del IDStore.rigify_types
del IDStore.rigify_active_type
- del IDStore.rigify_advanced_generation
- del IDStore.rigify_generate_mode
- del IDStore.rigify_force_widget_update
- del IDStore.rigify_target_rig
- del IDStore.rigify_target_rigs
- del IDStore.rigify_rig_uis
- del IDStore.rigify_rig_ui
- del IDStore.rigify_rig_basename
del IDStore.rigify_transfer_only_selected
# Classes.
diff --git a/rigify/base_rig.py b/rigify/base_rig.py
index 6b01c43a..b7a5d08c 100644
--- a/rigify/base_rig.py
+++ b/rigify/base_rig.py
@@ -258,6 +258,16 @@ class RigUtility(BoneUtilityMixin, MechanismUtilityMixin):
self.owner.register_new_bone(new_name, old_name)
+class RigComponent(GenerateCallbackHost, RigUtility):
+ """Base class for utility classes that generate part of a rig using callbacks."""
+ def __init__(self, owner):
+ super().__init__(owner)
+
+ self.owner.rigify_sub_objects = objects = self.owner.rigify_sub_objects or []
+
+ objects.append(self)
+
+
#=============================================
# Rig Stage Decorators
#=============================================
diff --git a/rigify/generate.py b/rigify/generate.py
index 0d02c5c2..8e3ca0f0 100644
--- a/rigify/generate.py
+++ b/rigify/generate.py
@@ -69,36 +69,45 @@ class Generator(base_generate.BaseGenerator):
def __create_rig_object(self):
scene = self.scene
id_store = self.id_store
+ meta_data = self.metarig.data
# Check if the generated rig already exists, so we can
# regenerate in the same object. If not, create a new
# object to generate the rig in.
print("Fetch rig.")
- if id_store.rigify_generate_mode == 'overwrite':
- name = id_store.rigify_target_rig or "rig"
- try:
+ self.rig_new_name = name = meta_data.rigify_rig_basename or "rig"
+
+ obj = None
+
+ if meta_data.rigify_generate_mode == 'overwrite':
+ obj = meta_data.rigify_target_rig
+
+ if not obj and name in scene.objects:
obj = scene.objects[name]
- self.rig_old_name = name
- obj.name = self.rig_new_name or name
+
+ if obj:
+ self.rig_old_name = obj.name
+
+ obj.name = name
+ obj.data.name = obj.name
rig_collections = filter_layer_collections_by_object(self.usable_collections, obj)
self.layer_collection = (rig_collections + [self.layer_collection])[0]
self.collection = self.layer_collection.collection
- except KeyError:
- self.rig_old_name = name
- name = self.rig_new_name or name
- obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
- obj.display_type = 'WIRE'
- self.collection.objects.link(obj)
- else:
- name = self.rig_new_name or "rig"
- obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) # in case name 'rig' exists it will be rig.001
+ elif name in bpy.data.objects:
+ obj = bpy.data.objects[name]
+
+ if not obj:
+ obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
obj.display_type = 'WIRE'
self.collection.objects.link(obj)
- id_store.rigify_target_rig = obj.name
+ elif obj.name not in self.collection.objects: # rig exists but was deleted
+ self.collection.objects.link(obj)
+
+ meta_data.rigify_target_rig = obj
obj.data.pose_position = 'POSE'
self.obj = obj
@@ -114,8 +123,8 @@ class Generator(base_generate.BaseGenerator):
self.widget_collection = ensure_widget_collection(context)
# Remove wgts if force update is set
- wgts_group_name = "WGTS_" + (self.rig_old_name or obj.name)
- if wgts_group_name in scene.objects and id_store.rigify_force_widget_update:
+ wgts_group_name = "WGTS_" + (self.rig_old_name or self.obj.name)
+ if wgts_group_name in scene.objects and self.metarig.data.rigify_force_widget_update:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for wgt in bpy.data.objects[wgts_group_name].children:
@@ -327,9 +336,6 @@ class Generator(base_generate.BaseGenerator):
#------------------------------------------
# Create/find the rig object and set it up
- if id_store.rigify_rig_basename:
- self.rig_new_name = id_store.rigify_rig_basename + "_rig"
-
obj = self.__create_rig_object()
# Get rid of anim data in case the rig already existed
diff --git a/rigify/metarigs/Animals/bird.py b/rigify/metarigs/Animals/bird.py
index a3331c29..eee6b38b 100644
--- a/rigify/metarigs/Animals/bird.py
+++ b/rigify/metarigs/Animals/bird.py
@@ -165,47 +165,19 @@ def create(obj):
bones = {}
- bone = arm.edit_bones.new('spine')
- bone.head[:] = -0.0000, 0.1371, 0.0894
- bone.tail[:] = -0.0000, 0.1039, 0.0907
+ bone = arm.edit_bones.new('spine.003')
+ bone.head[:] = -0.0000, 0.0451, 0.0845
+ bone.tail[:] = -0.0000, 0.0192, 0.0888
bone.roll = 0.0000
bone.use_connect = False
- bones['spine'] = bone.name
- bone = arm.edit_bones.new('spine.001')
- bone.head[:] = -0.0000, 0.1039, 0.0907
+ bones['spine.003'] = bone.name
+ bone = arm.edit_bones.new('spine.002')
+ bone.head[:] = -0.0000, 0.0451, 0.0845
bone.tail[:] = -0.0000, 0.0757, 0.0880
bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine']]
- bones['spine.001'] = bone.name
- bone = arm.edit_bones.new('t_feather.L')
- bone.head[:] = 0.0112, 0.1017, 0.0907
- bone.tail[:] = 0.0167, 0.1345, 0.0894
- bone.roll = 0.0032
- bone.use_connect = False
- bone.parent = arm.edit_bones[bones['spine']]
- bones['t_feather.L'] = bone.name
- bone = arm.edit_bones.new('t_feather.R')
- bone.head[:] = -0.0112, 0.1017, 0.0907
- bone.tail[:] = -0.0167, 0.1345, 0.0894
- bone.roll = -0.0032
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['spine']]
- bones['t_feather.R'] = bone.name
- bone = arm.edit_bones.new('spine.002')
- bone.head[:] = -0.0000, 0.0757, 0.0880
- bone.tail[:] = -0.0000, 0.0451, 0.0845
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.001']]
+ bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.002'] = bone.name
- bone = arm.edit_bones.new('spine.003')
- bone.head[:] = -0.0000, 0.0451, 0.0845
- bone.tail[:] = -0.0000, 0.0192, 0.0888
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.002']]
- bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.004')
bone.head[:] = -0.0000, 0.0192, 0.0888
bone.tail[:] = -0.0000, -0.0106, 0.0979
@@ -213,6 +185,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.004'] = bone.name
+ bone = arm.edit_bones.new('spine.001')
+ bone.head[:] = -0.0000, 0.0757, 0.0880
+ bone.tail[:] = -0.0000, 0.1039, 0.0907
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.002']]
+ bones['spine.001'] = bone.name
bone = arm.edit_bones.new('spine.005')
bone.head[:] = -0.0000, -0.0106, 0.0979
bone.tail[:] = -0.0000, -0.0298, 0.1158
@@ -248,6 +227,13 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.004']]
bones['thigh.R'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = -0.0000, 0.1039, 0.0907
+ bone.tail[:] = -0.0000, 0.1371, 0.0894
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.001']]
+ bones['spine'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
bone.head[:] = 0.0014, -0.0217, 0.0893
bone.tail[:] = 0.0076, -0.0020, 0.1179
@@ -283,6 +269,20 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['thigh.R']]
bones['shin.R'] = bone.name
+ bone = arm.edit_bones.new('t_feather.L')
+ bone.head[:] = 0.0112, 0.1017, 0.0907
+ bone.tail[:] = 0.0167, 0.1345, 0.0894
+ bone.roll = 0.0032
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['t_feather.L'] = bone.name
+ bone = arm.edit_bones.new('t_feather.R')
+ bone.head[:] = -0.0112, 0.1017, 0.0907
+ bone.tail[:] = -0.0167, 0.1345, 0.0894
+ bone.roll = -0.0032
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['t_feather.R'] = bone.name
bone = arm.edit_bones.new('Wing.L')
bone.head[:] = 0.0089, 0.0141, 0.1157
bone.tail[:] = 0.0485, 0.0107, 0.1163
@@ -294,7 +294,7 @@ def create(obj):
bone.head[:] = -0.0000, -0.0417, 0.1348
bone.tail[:] = -0.0000, -0.0458, 0.1429
bone.roll = 0.0001
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.006']]
bones['neck.001'] = bone.name
bone = arm.edit_bones.new('Wing.R')
@@ -691,8 +691,8 @@ def create(obj):
bones['tongue.003.L'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
- pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone = obj.pose.bones[bones['spine.003']]
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -700,70 +700,38 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.use_tail = True
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.tail_pos = 3
+ pbone.rigify_parameters.pivot_pos = 1
except AttributeError:
pass
try:
- pbone.rigify_parameters.pivot_pos = 4
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.neck_pos = 8
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.001']]
- pbone.rigify_type = ''
+ pbone = obj.pose.bones[bones['spine.002']]
+ pbone.rigify_type = 'spines.basic_tail'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['t_feather.L']]
- pbone.rigify_type = 'basic.super_copy'
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.make_widget = False
+ pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['t_feather.R']]
- pbone.rigify_type = 'basic.super_copy'
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.make_widget = False
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.002']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.003']]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ pbone = obj.pose.bones[bones['spine.004']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -771,7 +739,7 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.004']]
+ pbone = obj.pose.bones[bones['spine.001']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -859,6 +827,14 @@ def create(obj):
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['spine']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [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, False, False, False, False, False]
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -907,6 +883,30 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['t_feather.L']]
+ pbone.rigify_type = 'basic.super_copy'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.make_widget = False
+ except AttributeError:
+ pass
+ pbone = obj.pose.bones[bones['t_feather.R']]
+ pbone.rigify_type = 'basic.super_copy'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.make_widget = False
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['Wing.L']]
pbone.rigify_type = 'limbs.simple_tentacle'
pbone.lock_location = (False, False, False)
@@ -914,20 +914,31 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.rigify_parameters.copy_rotation_axes = [False, False, False]
pbone.bone.layers = [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, False]
try:
+ pbone.rigify_parameters.copy_rotation_axes = [False, False, False]
+ except AttributeError:
+ pass
+ try:
pbone.rigify_parameters.tweak_layers = [False, 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]
except AttributeError:
pass
pbone = obj.pose.bones[bones['neck.001']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['Wing.R']]
pbone.rigify_type = 'limbs.simple_tentacle'
pbone.lock_location = (False, False, False)
@@ -935,9 +946,12 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.rigify_parameters.copy_rotation_axes = [False, False, False]
pbone.bone.layers = [False, False, 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]
try:
+ pbone.rigify_parameters.copy_rotation_axes = [False, False, False]
+ except AttributeError:
+ pass
+ try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, 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]
except AttributeError:
pass
@@ -1488,9 +1502,12 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 7, 10, 13, 16, 21, 24]) for x in range(32)]
+ return bones
+
if __name__ == "__main__":
create(bpy.context.active_object)
diff --git a/rigify/metarigs/Animals/cat.py b/rigify/metarigs/Animals/cat.py
index a065c432..66321116 100644
--- a/rigify/metarigs/Animals/cat.py
+++ b/rigify/metarigs/Animals/cat.py
@@ -165,40 +165,19 @@ def create(obj):
bones = {}
- bone = arm.edit_bones.new('tail.004')
- bone.head[:] = -0.0000, 0.5531, 0.2488
- bone.tail[:] = -0.0000, 0.4543, 0.2321
- bone.roll = 0.0000
- bone.use_connect = False
- bones['tail.004'] = bone.name
- bone = arm.edit_bones.new('tail.003')
- bone.head[:] = -0.0000, 0.4543, 0.2321
- bone.tail[:] = -0.0000, 0.3513, 0.2284
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['tail.004']]
- bones['tail.003'] = bone.name
- bone = arm.edit_bones.new('tail.002')
- bone.head[:] = -0.0000, 0.3513, 0.2284
- bone.tail[:] = -0.0000, 0.2460, 0.2324
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['tail.003']]
- bones['tail.002'] = bone.name
- bone = arm.edit_bones.new('tail.001')
- bone.head[:] = -0.0000, 0.2460, 0.2324
- bone.tail[:] = 0.0000, 0.1499, 0.2500
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['tail.002']]
- bones['tail.001'] = bone.name
bone = arm.edit_bones.new('spine')
bone.head[:] = 0.0000, 0.1499, 0.2500
bone.tail[:] = 0.0000, 0.0769, 0.2272
bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['tail.001']]
+ bone.use_connect = False
bones['spine'] = bone.name
+ bone = arm.edit_bones.new('tail.001')
+ bone.head[:] = 0.0000, 0.1499, 0.2500
+ bone.tail[:] = -0.0000, 0.2460, 0.2324
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['tail.001'] = bone.name
bone = arm.edit_bones.new('spine.001')
bone.head[:] = 0.0000, 0.0769, 0.2272
bone.tail[:] = 0.0000, 0.0180, 0.2240
@@ -227,6 +206,13 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine']]
bones['pelvis.C'] = bone.name
+ bone = arm.edit_bones.new('tail.002')
+ bone.head[:] = -0.0000, 0.2460, 0.2324
+ bone.tail[:] = -0.0000, 0.3513, 0.2284
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['tail.001']]
+ bones['tail.002'] = bone.name
bone = arm.edit_bones.new('spine.002')
bone.head[:] = 0.0000, 0.0180, 0.2240
bone.tail[:] = 0.0000, -0.0513, 0.2271
@@ -248,6 +234,13 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['pelvis.R']]
bones['thigh.R'] = bone.name
+ bone = arm.edit_bones.new('tail.003')
+ bone.head[:] = -0.0000, 0.3513, 0.2284
+ bone.tail[:] = -0.0000, 0.4543, 0.2321
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['tail.002']]
+ bones['tail.003'] = bone.name
bone = arm.edit_bones.new('spine.003')
bone.head[:] = 0.0000, -0.0513, 0.2271
bone.tail[:] = 0.0000, -0.1571, 0.2355
@@ -276,11 +269,18 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['thigh.R']]
bones['shin.R'] = bone.name
+ bone = arm.edit_bones.new('tail.004')
+ bone.head[:] = -0.0000, 0.4543, 0.2321
+ bone.tail[:] = -0.0000, 0.5531, 0.2488
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['tail.003']]
+ bones['tail.004'] = bone.name
bone = arm.edit_bones.new('spine.004')
bone.head[:] = 0.0000, -0.1571, 0.2355
bone.tail[:] = 0.0000, -0.1736, 0.2395
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.004'] = bone.name
bone = arm.edit_bones.new('Breast.C')
@@ -1384,52 +1384,24 @@ def create(obj):
bones['brow.T.R.003'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
- pbone = obj.pose.bones[bones['tail.004']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone = obj.pose.bones[bones['spine']]
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- try:
- pbone.rigify_parameters.use_tail = True
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.pivot_pos = 6
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.neck_pos = 9
- except AttributeError:
- pass
+ pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.tail_pos = 4
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.copy_rotation_axes = [True, True, True]
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['tail.003']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['tail.002']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['tail.001']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.basic_tail'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -1437,23 +1409,15 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 5
+ pbone.rigify_parameters.copy_rotation_axes = [True, True, True]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.connect_chain = True
except AttributeError:
pass
pbone = obj.pose.bones[bones['spine.001']]
@@ -1504,6 +1468,14 @@ def create(obj):
pbone.rigify_parameters.make_control = False
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['tail.002']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.002']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -1556,6 +1528,14 @@ def create(obj):
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['tail.003']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.003']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -1588,14 +1568,26 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.004']]
+ pbone = obj.pose.bones[bones['tail.004']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine.004']]
+ pbone.rigify_type = 'spines.super_head'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['Breast.C']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -2983,9 +2975,12 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 5, 7, 10, 13, 16, 19]) for x in range(32)]
+ return bones
+
if __name__ == "__main__":
create(bpy.context.active_object)
diff --git a/rigify/metarigs/Animals/horse.py b/rigify/metarigs/Animals/horse.py
index 358a9ceb..6c94a19d 100644
--- a/rigify/metarigs/Animals/horse.py
+++ b/rigify/metarigs/Animals/horse.py
@@ -163,60 +163,38 @@ def create(obj):
arm.rigify_layers[28].selset = False
arm.rigify_layers[28].group = 1
-
bones = {}
- bone = arm.edit_bones.new('spine')
- bone.head[:] = -0.0000, 1.7610, 1.1153
- bone.tail[:] = -0.0000, 1.5754, 1.1088
- bone.roll = -0.0000
- bone.use_connect = False
- bones['spine'] = bone.name
- bone = arm.edit_bones.new('spine.001')
- bone.head[:] = -0.0000, 1.5754, 1.1088
- bone.tail[:] = -0.0000, 1.3779, 1.1589
- bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine']]
- bones['spine.001'] = bone.name
- bone = arm.edit_bones.new('spine.002')
- bone.head[:] = -0.0000, 1.3779, 1.1589
- bone.tail[:] = -0.0000, 1.1423, 1.3128
- bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.001']]
- bones['spine.002'] = bone.name
- bone = arm.edit_bones.new('spine.003')
- bone.head[:] = -0.0000, 1.1423, 1.3128
- bone.tail[:] = -0.0000, 1.0291, 1.4191
- bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.002']]
- bones['spine.003'] = bone.name
- bone = arm.edit_bones.new('spine.004')
- bone.head[:] = -0.0000, 1.0291, 1.4191
- bone.tail[:] = -0.0000, 0.9228, 1.4526
- bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.003']]
- bones['spine.004'] = bone.name
bone = arm.edit_bones.new('spine.005')
bone.head[:] = -0.0000, 0.9228, 1.4526
bone.tail[:] = -0.0000, 0.6989, 1.4910
bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.004']]
+ bone.use_connect = False
bones['spine.005'] = bone.name
+ bone = arm.edit_bones.new('spine.004')
+ bone.head[:] = -0.0000, 0.9228, 1.4526
+ bone.tail[:] = -0.0000, 1.0291, 1.4191
+ bone.roll = -0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine.005']]
+ bones['spine.004'] = bone.name
bone = arm.edit_bones.new('spine.006')
bone.head[:] = -0.0000, 0.6989, 1.4910
- bone.tail[:] = -0.0000, 0.3824, 1.3801
+ bone.tail[:] = 0.0000, 0.3824, 1.3801
bone.roll = -0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.005']]
bones['spine.006'] = bone.name
+ bone = arm.edit_bones.new('spine.003')
+ bone.head[:] = -0.0000, 1.0291, 1.4191
+ bone.tail[:] = -0.0000, 1.1423, 1.3128
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.004']]
+ bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.007')
- bone.head[:] = -0.0000, 0.3824, 1.3801
- bone.tail[:] = -0.0000, 0.1316, 1.3086
+ bone.head[:] = 0.0000, 0.3824, 1.3801
+ bone.tail[:] = 0.0000, 0.1316, 1.3086
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.006']]
@@ -256,9 +234,16 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.006']]
bones['pelvis'] = bone.name
+ bone = arm.edit_bones.new('spine.002')
+ bone.head[:] = -0.0000, 1.1423, 1.3128
+ bone.tail[:] = -0.0000, 1.3779, 1.1589
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.003']]
+ bones['spine.002'] = bone.name
bone = arm.edit_bones.new('spine.008')
- bone.head[:] = -0.0000, 0.1316, 1.3086
- bone.tail[:] = -0.0000, -0.1712, 1.2964
+ bone.head[:] = 0.0000, 0.1316, 1.3086
+ bone.tail[:] = 0.0000, -0.1712, 1.2964
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.007']]
@@ -277,9 +262,16 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['thigh.R']]
bones['shin.R'] = bone.name
+ bone = arm.edit_bones.new('spine.001')
+ bone.head[:] = -0.0000, 1.3779, 1.1589
+ bone.tail[:] = -0.0000, 1.5754, 1.1088
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.002']]
+ bones['spine.001'] = bone.name
bone = arm.edit_bones.new('spine.009')
- bone.head[:] = -0.0000, -0.1712, 1.2964
- bone.tail[:] = -0.0000, -0.4908, 1.3031
+ bone.head[:] = 0.0000, -0.1712, 1.2964
+ bone.tail[:] = 0.0000, -0.4908, 1.3031
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.008']]
@@ -305,9 +297,16 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['shin.R']]
bones['foot.R'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = -0.0000, 1.5754, 1.1088
+ bone.tail[:] = -0.0000, 1.7610, 1.1153
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.001']]
+ bones['spine'] = bone.name
bone = arm.edit_bones.new('spine.010')
- bone.head[:] = -0.0000, -0.4908, 1.3031
- bone.tail[:] = -0.0000, -0.7593, 1.3786
+ bone.head[:] = 0.0000, -0.4908, 1.3031
+ bone.tail[:] = 0.0000, -0.7593, 1.3786
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.009']]
@@ -362,10 +361,10 @@ def create(obj):
bone.parent = arm.edit_bones[bones['foot.R']]
bones['r_toe.R'] = bone.name
bone = arm.edit_bones.new('spine.011')
- bone.head[:] = -0.0000, -0.7593, 1.3786
- bone.tail[:] = -0.0000, -0.9004, 1.5475
+ bone.head[:] = 0.0000, -0.7593, 1.3786
+ bone.tail[:] = 0.0000, -0.9004, 1.5475
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.010']]
bones['spine.011'] = bone.name
bone = arm.edit_bones.new('hair_base.05')
@@ -629,8 +628,8 @@ def create(obj):
bones['jaw.001'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
- pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone = obj.pose.bones[bones['spine.005']]
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -638,7 +637,7 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 12
+ pbone.rigify_parameters.pivot_pos = 3
except AttributeError:
pass
try:
@@ -646,37 +645,37 @@ def create(obj):
except AttributeError:
pass
try:
- pbone.rigify_parameters.use_tail = True
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['spine.004']]
+ pbone.rigify_type = 'spines.basic_tail'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.pivot_pos = 8
+ pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tail_pos = 5
+ pbone.rigify_parameters.connect_chain = True
except AttributeError:
pass
try:
- pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.001']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.002']]
+ pbone = obj.pose.bones[bones['spine.006']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [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, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.003']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -685,30 +684,6 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.005']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.006']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.007']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -805,6 +780,14 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine.002']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.008']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -829,6 +812,14 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine.001']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.009']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -861,6 +852,14 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.010']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -934,13 +933,21 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.011']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['hair_base.05']]
pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
@@ -1364,9 +1371,12 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 4, 7, 10, 13, 16, 19, 21]) for x in range(32)]
+ return bones
+
if __name__ == "__main__":
create(bpy.context.active_object)
diff --git a/rigify/metarigs/Animals/shark.py b/rigify/metarigs/Animals/shark.py
index 1ee1d0d9..8b72a965 100644
--- a/rigify/metarigs/Animals/shark.py
+++ b/rigify/metarigs/Animals/shark.py
@@ -165,40 +165,40 @@ def create(obj):
bones = {}
- bone = arm.edit_bones.new('spine')
- bone.head[:] = -0.0000, 1.3362, 0.4776
- bone.tail[:] = -0.0000, 1.0816, 0.4540
- bone.roll = 0.0000
+ bone = arm.edit_bones.new('spine.003')
+ bone.head[:] = -0.0000, 0.3182, 0.4031
+ bone.tail[:] = -0.0000, 0.0152, 0.3904
+ bone.roll = 0.0001
bone.use_connect = False
- bones['spine'] = bone.name
- bone = arm.edit_bones.new('spine.001')
- bone.head[:] = -0.0000, 1.0816, 0.4540
+ bones['spine.003'] = bone.name
+ bone = arm.edit_bones.new('spine.002')
+ bone.head[:] = -0.0000, 0.3182, 0.4031
bone.tail[:] = -0.0000, 0.7152, 0.4305
bone.roll = -0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine']]
- bones['spine.001'] = bone.name
- bone = arm.edit_bones.new('back_fin.T.Bk')
- bone.head[:] = 0.0000, 1.2501, 0.5345
- bone.tail[:] = 0.0000, 1.5211, 0.7594
- bone.roll = 0.0000
- bone.use_connect = False
- bone.parent = arm.edit_bones[bones['spine']]
- bones['back_fin.T.Bk'] = bone.name
- bone = arm.edit_bones.new('back_fin.B.Bk')
- bone.head[:] = 0.0000, 1.2305, 0.4158
- bone.tail[:] = 0.0000, 1.3289, 0.2452
- bone.roll = 0.0000
bone.use_connect = False
- bone.parent = arm.edit_bones[bones['spine']]
- bones['back_fin.B.Bk'] = bone.name
- bone = arm.edit_bones.new('spine.002')
+ bone.parent = arm.edit_bones[bones['spine.003']]
+ bones['spine.002'] = bone.name
+ bone = arm.edit_bones.new('spine.001')
bone.head[:] = -0.0000, 0.7152, 0.4305
- bone.tail[:] = -0.0000, 0.3182, 0.4031
+ bone.tail[:] = -0.0000, 1.0816, 0.4540
bone.roll = -0.0000
bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.002']]
+ bones['spine.001'] = bone.name
+ bone = arm.edit_bones.new('spine.008')
+ bone.head[:] = -0.0000, 0.0152, 0.3904
+ bone.tail[:] = 0.0000, -0.3259, 0.3967
+ bone.roll = 0.0001
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.003']]
+ bones['spine.008'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = -0.0000, 1.0816, 0.4540
+ bone.tail[:] = -0.0000, 1.3362, 0.4776
+ bone.roll = 0.0000
+ bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.001']]
- bones['spine.002'] = bone.name
+ bones['spine'] = bone.name
bone = arm.edit_bones.new('mid_fin.Top')
bone.head[:] = 0.0000, 0.7296, 0.5396
bone.tail[:] = 0.0000, 0.7709, 0.6351
@@ -213,41 +213,6 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.001']]
bones['mid_fin.Bot'] = bone.name
- bone = arm.edit_bones.new('back_fin.T.001.Bk')
- bone.head[:] = 0.0000, 1.5211, 0.7594
- bone.tail[:] = 0.0000, 1.7667, 0.9633
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['back_fin.T.Bk']]
- bones['back_fin.T.001.Bk'] = bone.name
- bone = arm.edit_bones.new('back_fin.B.001.Bk')
- bone.head[:] = 0.0000, 1.3289, 0.2452
- bone.tail[:] = 0.0000, 1.3818, 0.1513
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['back_fin.B.Bk']]
- bones['back_fin.B.001.Bk'] = bone.name
- bone = arm.edit_bones.new('spine.003')
- bone.head[:] = -0.0000, 0.3182, 0.4031
- bone.tail[:] = -0.0000, 0.0152, 0.3904
- bone.roll = 0.0001
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.002']]
- bones['spine.003'] = bone.name
- bone = arm.edit_bones.new('back_fin.T.002.Bk')
- bone.head[:] = 0.0000, 1.7667, 0.9633
- bone.tail[:] = 0.0000, 1.9489, 1.1145
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['back_fin.T.001.Bk']]
- bones['back_fin.T.002.Bk'] = bone.name
- bone = arm.edit_bones.new('spine.008')
- bone.head[:] = -0.0000, 0.0152, 0.3904
- bone.tail[:] = 0.0000, -0.3259, 0.3967
- bone.roll = 0.0001
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.003']]
- bones['spine.008'] = bone.name
bone = arm.edit_bones.new('spine.004')
bone.head[:] = 0.0000, -0.3259, 0.3967
bone.tail[:] = 0.0000, -0.5947, 0.4044
@@ -269,6 +234,20 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.008']]
bones['chest_fin.Bot.R'] = bone.name
+ bone = arm.edit_bones.new('back_fin.T.Bk')
+ bone.head[:] = 0.0000, 1.2501, 0.5345
+ bone.tail[:] = 0.0000, 1.5211, 0.7594
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['back_fin.T.Bk'] = bone.name
+ bone = arm.edit_bones.new('back_fin.B.Bk')
+ bone.head[:] = 0.0000, 1.2305, 0.4158
+ bone.tail[:] = 0.0000, 1.3289, 0.2452
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['back_fin.B.Bk'] = bone.name
bone = arm.edit_bones.new('spine.005')
bone.head[:] = 0.0000, -0.5947, 0.4044
bone.tail[:] = 0.0000, -1.2084, 0.4328
@@ -283,11 +262,25 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.004']]
bones['top_fin'] = bone.name
+ bone = arm.edit_bones.new('back_fin.T.001.Bk')
+ bone.head[:] = 0.0000, 1.5211, 0.7594
+ bone.tail[:] = 0.0000, 1.7667, 0.9633
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['back_fin.T.Bk']]
+ bones['back_fin.T.001.Bk'] = bone.name
+ bone = arm.edit_bones.new('back_fin.B.001.Bk')
+ bone.head[:] = 0.0000, 1.3289, 0.2452
+ bone.tail[:] = 0.0000, 1.3818, 0.1513
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['back_fin.B.Bk']]
+ bones['back_fin.B.001.Bk'] = bone.name
bone = arm.edit_bones.new('spine.006')
bone.head[:] = 0.0000, -1.2084, 0.4328
bone.tail[:] = 0.0000, -1.5634, 0.4275
bone.roll = -0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.005']]
bones['spine.006'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
@@ -311,6 +304,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['top_fin']]
bones['top_fin.001'] = bone.name
+ bone = arm.edit_bones.new('back_fin.T.002.Bk')
+ bone.head[:] = 0.0000, 1.7667, 0.9633
+ bone.tail[:] = 0.0000, 1.9489, 1.1145
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['back_fin.T.001.Bk']]
+ bones['back_fin.T.002.Bk'] = bone.name
bone = arm.edit_bones.new('spine.007')
bone.head[:] = 0.0000, -1.5634, 0.4275
bone.tail[:] = 0.0000, -2.0661, 0.4364
@@ -411,8 +411,8 @@ def create(obj):
bones['jaw.003.R'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
- pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone = obj.pose.bones[bones['spine.002']]
+ pbone.rigify_type = 'spines.basic_tail'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -420,70 +420,34 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 8
- except AttributeError:
- pass
- try:
pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tail_pos = 3
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.pivot_pos = 5
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.use_tail = True
+ pbone.rigify_parameters.connect_chain = True
except AttributeError:
pass
try:
pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.001']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False]
- pbone = obj.pose.bones[bones['back_fin.T.Bk']]
- pbone.rigify_type = 'limbs.super_finger'
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, 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]
- try:
- pbone.rigify_parameters.tweak_layers = [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, False]
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.primary_rotation_axis = "Z"
- except AttributeError:
- pass
- pbone = obj.pose.bones[bones['back_fin.B.Bk']]
- pbone.rigify_type = 'limbs.super_finger'
+ pbone = obj.pose.bones[bones['spine.003']]
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, 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]
+ pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.tweak_layers = [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, False]
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.primary_rotation_axis = "Z"
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.002']]
+ pbone = obj.pose.bones[bones['spine.001']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -491,23 +455,23 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False]
- pbone = obj.pose.bones[bones['mid_fin.Top']]
+ pbone = obj.pose.bones[bones['spine.008']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, 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]
- pbone = obj.pose.bones[bones['mid_fin.Bot']]
+ pbone.bone.layers = [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, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False]
- pbone = obj.pose.bones[bones['back_fin.T.001.Bk']]
+ pbone.bone.layers = [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, False, False, False]
+ pbone = obj.pose.bones[bones['mid_fin.Top']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -515,15 +479,15 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, 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]
- pbone = obj.pose.bones[bones['back_fin.B.001.Bk']]
+ pbone = obj.pose.bones[bones['mid_fin.Bot']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, 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]
- pbone = obj.pose.bones[bones['spine.003']]
+ pbone.bone.layers = [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, False, False]
+ pbone = obj.pose.bones[bones['spine.004']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -531,32 +495,32 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['back_fin.T.002.Bk']]
- pbone.rigify_type = ''
+ pbone = obj.pose.bones[bones['chest_fin.Bot.L']]
+ pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, 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]
- pbone = obj.pose.bones[bones['spine.008']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
+ pbone = obj.pose.bones[bones['chest_fin.Bot.R']]
+ pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['chest_fin.Bot.L']]
- pbone.rigify_type = 'basic.super_copy'
+ pbone.bone.layers = [False, False, 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]
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False]
+ except AttributeError:
+ pass
+ pbone = obj.pose.bones[bones['back_fin.T.Bk']]
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -564,11 +528,15 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, 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]
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.tweak_layers = [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, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['chest_fin.Bot.R']]
- pbone.rigify_type = 'basic.super_copy'
+ try:
+ pbone.rigify_parameters.primary_rotation_axis = "Z"
+ except AttributeError:
+ pass
+ pbone = obj.pose.bones[bones['back_fin.B.Bk']]
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -576,7 +544,11 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, 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]
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False]
+ pbone.rigify_parameters.tweak_layers = [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, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.primary_rotation_axis = "Z"
except AttributeError:
pass
pbone = obj.pose.bones[bones['spine.005']]
@@ -599,14 +571,38 @@ def create(obj):
pbone.rigify_parameters.tweak_layers = [False, False, False, 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]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.006']]
+ pbone = obj.pose.bones[bones['back_fin.T.001.Bk']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, 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]
+ pbone = obj.pose.bones[bones['back_fin.B.001.Bk']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, 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]
+ pbone = obj.pose.bones[bones['spine.006']]
+ pbone.rigify_type = 'spines.super_head'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -639,6 +635,14 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, 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]
+ pbone = obj.pose.bones[bones['back_fin.T.002.Bk']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, 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]
pbone = obj.pose.bones[bones['spine.007']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -786,9 +790,12 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 5, 6, 8, 10]) for x in range(32)]
+ return bones
+
if __name__ == "__main__":
create(bpy.context.active_object)
diff --git a/rigify/metarigs/Animals/wolf.py b/rigify/metarigs/Animals/wolf.py
index 4cccf085..f6150c54 100644
--- a/rigify/metarigs/Animals/wolf.py
+++ b/rigify/metarigs/Animals/wolf.py
@@ -165,40 +165,19 @@ def create(obj):
bones = {}
- bone = arm.edit_bones.new('spine')
- bone.head[:] = 0.0000, 1.1044, 0.7633
- bone.tail[:] = 0.0000, 0.9624, 0.7412
- bone.roll = 0.0000
- bone.use_connect = False
- bones['spine'] = bone.name
- bone = arm.edit_bones.new('spine.001')
- bone.head[:] = 0.0000, 0.9624, 0.7412
- bone.tail[:] = 0.0000, 0.7755, 0.7418
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine']]
- bones['spine.001'] = bone.name
- bone = arm.edit_bones.new('spine.002')
- bone.head[:] = 0.0000, 0.7755, 0.7418
- bone.tail[:] = 0.0000, 0.5547, 0.7568
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.001']]
- bones['spine.002'] = bone.name
- bone = arm.edit_bones.new('spine.003')
- bone.head[:] = 0.0000, 0.5547, 0.7568
- bone.tail[:] = 0.0000, 0.4418, 0.7954
- bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.002']]
- bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.004')
bone.head[:] = 0.0000, 0.4418, 0.7954
bone.tail[:] = 0.0000, 0.3546, 0.8059
bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.003']]
+ bone.use_connect = False
bones['spine.004'] = bone.name
+ bone = arm.edit_bones.new('spine.003')
+ bone.head[:] = 0.0000, 0.4418, 0.7954
+ bone.tail[:] = 0.0000, 0.5547, 0.7568
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine.004']]
+ bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.005')
bone.head[:] = 0.0000, 0.3546, 0.8059
bone.tail[:] = 0.0000, 0.1803, 0.7782
@@ -206,6 +185,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.004']]
bones['spine.005'] = bone.name
+ bone = arm.edit_bones.new('spine.002')
+ bone.head[:] = 0.0000, 0.5547, 0.7568
+ bone.tail[:] = 0.0000, 0.7755, 0.7418
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.003']]
+ bones['spine.002'] = bone.name
bone = arm.edit_bones.new('spine.006')
bone.head[:] = 0.0000, 0.1803, 0.7782
bone.tail[:] = 0.0000, 0.0319, 0.7731
@@ -241,6 +227,13 @@ def create(obj):
bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.005']]
bones['thigh.R'] = bone.name
+ bone = arm.edit_bones.new('spine.001')
+ bone.head[:] = 0.0000, 0.7755, 0.7418
+ bone.tail[:] = 0.0000, 0.9624, 0.7412
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.002']]
+ bones['spine.001'] = bone.name
bone = arm.edit_bones.new('spine.007')
bone.head[:] = 0.0000, 0.0319, 0.7731
bone.tail[:] = 0.0000, -0.0980, 0.7945
@@ -262,6 +255,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['thigh.R']]
bones['shin.R'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = 0.0000, 0.9624, 0.7412
+ bone.tail[:] = 0.0000, 1.1044, 0.7633
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine.001']]
+ bones['spine'] = bone.name
bone = arm.edit_bones.new('spine.008')
bone.head[:] = 0.0000, -0.0980, 0.7945
bone.tail[:] = 0.0000, -0.3618, 0.8375
@@ -287,7 +287,7 @@ def create(obj):
bone.head[:] = 0.0000, -0.3618, 0.8375
bone.tail[:] = 0.0000, -0.4253, 0.8585
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.008']]
bones['spine.009'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
@@ -1496,63 +1496,63 @@ def create(obj):
bones['brow.T.R.003'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
- pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone = obj.pose.bones[bones['spine.004']]
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.pivot_pos = 4
except AttributeError:
pass
try:
- pbone.rigify_parameters.use_tail = True
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tail_pos = 4
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['spine.003']]
+ pbone.rigify_type = 'spines.basic_tail'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
try:
- pbone.rigify_parameters.pivot_pos = 8
+ pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
except AttributeError:
pass
try:
- pbone.rigify_parameters.neck_pos = 10
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
+ pbone.rigify_parameters.connect_chain = True
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.001']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.002']]
+ pbone = obj.pose.bones[bones['spine.005']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.tweak_extra_layers = False
+ pbone.rigify_parameters.neck_pos = 5
except AttributeError:
pass
try:
- pbone.rigify_parameters.tweak_layers = [False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False]
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
- pbone = obj.pose.bones[bones['spine.003']]
+ pbone = obj.pose.bones[bones['spine.002']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -1560,28 +1560,8 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
- pbone = obj.pose.bones[bones['spine.005']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 5
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.tweak_layers = [False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['spine.006']]
@@ -1656,6 +1636,14 @@ def create(obj):
pbone.rigify_parameters.limb_type = "paw"
except AttributeError:
pass
+ pbone = obj.pose.bones[bones['spine.001']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.007']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -1680,6 +1668,14 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['spine']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.008']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -1705,13 +1701,21 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.009']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -3219,9 +3223,12 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 4, 5, 7, 10, 13, 16, 19]) for x in range(32)]
+ return bones
+
if __name__ == "__main__":
create(bpy.context.active_object)
diff --git a/rigify/metarigs/Basic/basic_human.py b/rigify/metarigs/Basic/basic_human.py
index 01367a7b..01017cf4 100644
--- a/rigify/metarigs/Basic/basic_human.py
+++ b/rigify/metarigs/Basic/basic_human.py
@@ -252,7 +252,7 @@ def create(obj):
bone.head[:] = 0.0000, 0.0114, 1.6582
bone.tail[:] = 0.0000, -0.0130, 1.7197
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.004'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
@@ -370,7 +370,7 @@ def create(obj):
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -378,11 +378,11 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 5
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['spine.001']]
@@ -506,13 +506,21 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -676,6 +684,7 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [3, 7, 10, 13, 16]) for x in range(32)]
diff --git a/rigify/metarigs/Basic/basic_quadruped.py b/rigify/metarigs/Basic/basic_quadruped.py
index 5aa9f657..5282df25 100644
--- a/rigify/metarigs/Basic/basic_quadruped.py
+++ b/rigify/metarigs/Basic/basic_quadruped.py
@@ -165,40 +165,40 @@ def create(obj):
bones = {}
- bone = arm.edit_bones.new('spine')
- bone.head[:] = 0.0000, 1.1044, 0.7633
- bone.tail[:] = 0.0000, 0.9624, 0.7412
+ bone = arm.edit_bones.new('spine.004')
+ bone.head[:] = 0.0000, 0.4418, 0.7954
+ bone.tail[:] = 0.0000, 0.3546, 0.8059
bone.roll = 0.0000
bone.use_connect = False
- bones['spine'] = bone.name
- bone = arm.edit_bones.new('spine.001')
- bone.head[:] = 0.0000, 0.9624, 0.7412
- bone.tail[:] = 0.0000, 0.7755, 0.7418
+ bones['spine.004'] = bone.name
+ bone = arm.edit_bones.new('spine.003')
+ bone.head[:] = 0.0000, 0.4418, 0.7954
+ bone.tail[:] = 0.0000, 0.5547, 0.7568
bone.roll = 0.0000
- bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine']]
- bones['spine.001'] = bone.name
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['spine.004']]
+ bones['spine.003'] = bone.name
bone = arm.edit_bones.new('spine.002')
- bone.head[:] = 0.0000, 0.7755, 0.7418
- bone.tail[:] = 0.0000, 0.5547, 0.7568
+ bone.head[:] = 0.0000, 0.5547, 0.7568
+ bone.tail[:] = 0.0000, 0.7755, 0.7418
bone.roll = 0.0000
bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.001']]
+ bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.002'] = bone.name
- bone = arm.edit_bones.new('spine.003')
- bone.head[:] = 0.0000, 0.5547, 0.7568
- bone.tail[:] = 0.0000, 0.4418, 0.7954
+ bone = arm.edit_bones.new('spine.001')
+ bone.head[:] = 0.0000, 0.7755, 0.7418
+ bone.tail[:] = 0.0000, 0.9624, 0.7412
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['spine.002']]
- bones['spine.003'] = bone.name
- bone = arm.edit_bones.new('spine.004')
- bone.head[:] = 0.0000, 0.4418, 0.7954
- bone.tail[:] = 0.0000, 0.3546, 0.8059
+ bones['spine.001'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = 0.0000, 0.9624, 0.7412
+ bone.tail[:] = 0.0000, 1.1044, 0.7633
bone.roll = 0.0000
bone.use_connect = True
- bone.parent = arm.edit_bones[bones['spine.003']]
- bones['spine.004'] = bone.name
+ bone.parent = arm.edit_bones[bones['spine.001']]
+ bones['spine'] = bone.name
bone = arm.edit_bones.new('spine.005')
bone.head[:] = 0.0000, 0.3546, 0.8059
bone.tail[:] = 0.0000, 0.1803, 0.7782
@@ -287,7 +287,7 @@ def create(obj):
bone.head[:] = 0.0000, -0.3618, 0.8375
bone.tail[:] = 0.0000, -0.4253, 0.8585
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.008']]
bones['spine.009'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
@@ -405,37 +405,13 @@ def create(obj):
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.use_tail = True
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.tail_pos = 4
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.pivot_pos = 8
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.neck_pos = 10
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.copy_rotation_axes = [True, False, True]
- except AttributeError:
- pass
pbone = obj.pose.bones[bones['spine.001']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -452,30 +428,42 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
- try:
- pbone.rigify_parameters.tweak_extra_layers = False
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.tweak_layers = [False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False]
- except AttributeError:
- pass
pbone = obj.pose.bones[bones['spine.003']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.basic_tail'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.pivot_pos = 4
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['spine.005']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -484,14 +472,6 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
- try:
- pbone.rigify_parameters.neck_pos = 5
- except AttributeError:
- pass
- try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
- except AttributeError:
- pass
pbone = obj.pose.bones[bones['spine.006']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -613,13 +593,21 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.009']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -811,6 +799,7 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [3, 4, 7, 10, 13, 16, 19]) for x in range(32)]
diff --git a/rigify/metarigs/human.py b/rigify/metarigs/human.py
index bbba8eed..e4279982 100644
--- a/rigify/metarigs/human.py
+++ b/rigify/metarigs/human.py
@@ -70,10 +70,10 @@ def create(obj):
arm.rigify_layers[5].row = 5
arm.rigify_layers[5].selset = False
arm.rigify_layers[5].group = 6
- arm.rigify_layers[6].name = "Fingers (Tweak)"
+ arm.rigify_layers[6].name = "Fingers (Detail)"
arm.rigify_layers[6].row = 6
arm.rigify_layers[6].selset = False
- arm.rigify_layers[6].group = 4
+ arm.rigify_layers[6].group = 5
arm.rigify_layers[7].name = "Arm.L (IK)"
arm.rigify_layers[7].row = 7
arm.rigify_layers[7].selset = False
@@ -252,7 +252,7 @@ def create(obj):
bone.head[:] = 0.0000, 0.0114, 1.6582
bone.tail[:] = 0.0000, -0.0130, 1.7197
bone.roll = 0.0000
- bone.use_connect = True
+ bone.use_connect = False
bone.parent = arm.edit_bones[bones['spine.003']]
bones['spine.004'] = bone.name
bone = arm.edit_bones.new('shoulder.L')
@@ -1280,7 +1280,7 @@ def create(obj):
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['spine']]
- pbone.rigify_type = 'spines.super_spine'
+ pbone.rigify_type = 'spines.basic_spine'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -1288,11 +1288,11 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
try:
- pbone.rigify_parameters.neck_pos = 5
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
except AttributeError:
pass
try:
- pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ pbone.rigify_parameters.fk_layers = [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, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['spine.001']]
@@ -1416,13 +1416,21 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.004']]
- pbone.rigify_type = ''
+ pbone.rigify_type = 'spines.super_head'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [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, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.connect_chain = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.tweak_layers = [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, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.super_copy'
pbone.lock_location = (False, False, False)
@@ -1956,7 +1964,7 @@ def create(obj):
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['f_index.01.L']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -1972,7 +1980,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['thumb.01.L']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -1988,7 +1996,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_middle.01.L']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2004,7 +2012,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_ring.01.L']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2020,7 +2028,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_pinky.01.L']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2036,7 +2044,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_index.01.R']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2052,7 +2060,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['thumb.01.R']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2068,7 +2076,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_middle.01.R']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2084,7 +2092,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_ring.01.R']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2100,7 +2108,7 @@ def create(obj):
except AttributeError:
pass
pbone = obj.pose.bones[bones['f_pinky.01.R']]
- pbone.rigify_type = 'limbs.simple_tentacle'
+ pbone.rigify_type = 'limbs.super_finger'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
@@ -2710,6 +2718,7 @@ def create(obj):
bone.select = True
bone.select_head = True
bone.select_tail = True
+ bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
arm.layers = [(x in [0, 3, 5, 7, 10, 13, 16]) for x in range(32)]
diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py
index d7c2e870..bb5a9cbd 100644
--- a/rigify/rig_ui_template.py
+++ b/rigify/rig_ui_template.py
@@ -36,6 +36,7 @@ UI_IMPORTS = [
'import math',
'import json',
'import collections',
+ 'import traceback',
'from math import pi',
'from bpy.props import StringProperty',
'from mathutils import Euler, Matrix, Quaternion, Vector',
@@ -1147,7 +1148,6 @@ class ScriptGenerator(base_generate.GeneratorPlugin):
def finalize(self):
metarig = self.generator.metarig
- id_store = self.generator.id_store
rig_id = self.generator.rig_id
vis_layers = self.obj.data.layers
@@ -1162,23 +1162,27 @@ class ScriptGenerator(base_generate.GeneratorPlugin):
layer_layout += [(l.name, l.row)]
# Generate the UI script
- if id_store.rigify_generate_mode == 'overwrite':
- rig_ui_name = id_store.rigify_rig_ui or 'rig_ui.py'
+ if metarig.data.rigify_rig_basename:
+ rig_ui_name = metarig.data.rigify_rig_basename + '_rig_ui.py'
else:
rig_ui_name = 'rig_ui.py'
- if id_store.rigify_generate_mode == 'overwrite' and rig_ui_name in bpy.data.texts.keys():
- script = bpy.data.texts[rig_ui_name]
- script.clear()
- else:
- script = bpy.data.texts.new("rig_ui.py")
+ script = None
+
+ if metarig.data.rigify_generate_mode == 'overwrite':
+ script = metarig.data.rigify_rig_ui
+
+ if not script and rig_ui_name in bpy.data.texts:
+ script = bpy.data.texts[rig_ui_name]
+
+ if script:
+ script.clear()
+ script.name = rig_ui_name
- rig_ui_old_name = ""
- if id_store.rigify_rig_basename:
- rig_ui_old_name = script.name
- script.name = id_store.rigify_rig_basename + "_rig_ui.py"
+ if script is None:
+ script = bpy.data.texts.new(rig_ui_name)
- id_store.rigify_rig_ui = script.name
+ metarig.data.rigify_rig_ui = script
for s in OrderedDict.fromkeys(self.ui_imports):
script.write(s + "\n")
diff --git a/rigify/rigs/basic/copy_chain.py b/rigify/rigs/basic/copy_chain.py
index 5145d735..a3920ced 100644
--- a/rigify/rigs/basic/copy_chain.py
+++ b/rigify/rigs/basic/copy_chain.py
@@ -22,9 +22,10 @@ import bpy
from ..chain_rigs import SimpleChainRig
+from ...utils.layers import DEF_LAYER
from ...utils.errors import MetarigError
from ...utils.rig import connected_children_names
-from ...utils.naming import strip_org, make_deformer_name
+from ...utils.naming import make_derived_name
from ...utils.widgets_basic import create_bone_widget
from ...base_rig import BaseRig, stage
@@ -41,7 +42,12 @@ class Rig(SimpleChainRig):
""" Gather and validate data about the rig.
"""
self.make_controls = self.params.make_controls
- self.make_deforms = self.params.make_deforms
+
+ deform = self.params.make_deforms
+ rename = self.params.rename_to_deform
+
+ self.make_deforms = deform and not rename
+ self.rename_deforms = deform and rename
##############################
# Control chain
@@ -92,6 +98,20 @@ class Rig(SimpleChainRig):
if self.make_deforms:
super().rig_deform_chain()
+ ##############################
+ # Rename To Deform
+
+ def finalize(self):
+ if self.rename_deform:
+ new_names = [ self.rename_bone(name, make_derived_name(name, 'def')) for name in self.bones.org ]
+
+ for name in new_names:
+ bone = self.get_bone(name).bone
+ bone.use_deform = True
+ bone.layers = DEF_LAYER
+
+ ##############################
+ # Parameter UI
@classmethod
def add_parameters(self, params):
@@ -101,6 +121,11 @@ class Rig(SimpleChainRig):
params.make_controls = bpy.props.BoolProperty(name="Controls", default=True, description="Create control bones for the copy")
params.make_deforms = bpy.props.BoolProperty(name="Deform", default=True, description="Create deform bones for the copy")
+ params.rename_to_deform = bpy.props.BoolProperty(
+ name = "Rename To Deform",
+ default = False,
+ description = "Rename the original bone itself to use as deform bone (advanced feature)"
+ )
@classmethod
def parameters_ui(self, layout, params):
@@ -111,6 +136,10 @@ class Rig(SimpleChainRig):
r = layout.row()
r.prop(params, "make_deforms")
+ if params.make_deforms:
+ r = layout.row()
+ r.prop(params, "rename_to_deform")
+
def create_sample(obj):
""" Create a sample metarig for this rig type.
diff --git a/rigify/rigs/basic/pivot.py b/rigify/rigs/basic/pivot.py
new file mode 100644
index 00000000..e5d31659
--- /dev/null
+++ b/rigify/rigs/basic/pivot.py
@@ -0,0 +1,236 @@
+#====================== BEGIN GPL LICENSE BLOCK ======================
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+
+from ...base_rig import BaseRig
+
+from ...utils.naming import make_derived_name
+from ...utils.bones import set_bone_widget_transform
+from ...utils.widgets_basic import create_cube_widget, create_pivot_widget
+from ...utils.switch_parent import SwitchParentBuilder
+
+
+class Rig(BaseRig):
+ """ A rig providing a rotation pivot control that can be moved. """
+ def find_org_bones(self, pose_bone):
+ return pose_bone.name
+
+
+ def initialize(self):
+ self.make_control = self.params.make_extra_control
+ self.make_pivot = self.params.make_control or not self.make_control
+ self.make_deform = self.params.make_extra_deform
+
+
+ def generate_bones(self):
+ org = self.bones.org
+
+ if self.make_control:
+ self.bones.ctrl.master = name = self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True)
+
+ if self.make_pivot:
+ self.bones.ctrl.pivot = self.copy_bone(org, make_derived_name(org, 'ctrl', '_pivot'))
+
+ if self.params.make_parent_switch:
+ self.build_parent_switch(name)
+
+ if self.params.register_parent:
+ self.register_parent(name, self.get_parent_tags())
+
+ else:
+ self.bones.ctrl.pivot = self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True)
+
+ if self.make_deform:
+ self.bones.deform = self.copy_bone(org, make_derived_name(org, 'def'), bbone=True)
+
+
+ def build_parent_switch(self, master_name):
+ pbuilder = SwitchParentBuilder(self.generator)
+
+ org_parent = self.get_bone_parent(self.bones.org)
+ parents = [org_parent] if org_parent else []
+
+ pbuilder.build_child(
+ self, master_name,
+ context_rig=self.rigify_parent, allow_self=True,
+ prop_name="Parent ({})".format(master_name),
+ extra_parents=parents, select_parent=org_parent,
+ controls=lambda: self.bones.ctrl.flatten()
+ )
+
+ def get_parent_tags(self):
+ tags = {t.strip() for t in self.params.register_parent_tags.split(',')}
+
+ if self.params.make_parent_switch:
+ tags.add('child')
+
+ tags.discard('')
+ return tags
+
+ def register_parent(self, master_name, tags):
+ pbuilder = SwitchParentBuilder(self.generator)
+
+ inject = self.rigify_parent if 'injected' in tags else None
+
+ pbuilder.register_parent(
+ self, self.bones.org, name=master_name,
+ inject_into=inject, tags=tags
+ )
+
+
+ def parent_bones(self):
+ ctrl = self.bones.ctrl
+
+ if self.make_pivot:
+ if self.make_control:
+ self.set_bone_parent(ctrl.pivot, ctrl.master, use_connect=False)
+
+ self.set_bone_parent(self.bones.org, ctrl.pivot, use_connect=False)
+
+ else:
+ self.set_bone_parent(self.bones.org, ctrl.master, use_connect=False)
+
+ if self.make_deform:
+ self.set_bone_parent(self.bones.deform, self.bones.org, use_connect=False)
+
+
+ def configure_bones(self):
+ if self.make_control:
+ self.copy_bone_properties(self.bones.org, self.bones.ctrl.master)
+
+ else:
+ self.copy_bone_properties(self.bones.org, self.bones.ctrl.pivot)
+
+
+ def rig_bones(self):
+ if self.make_pivot:
+ self.make_constraint(
+ self.bones.org, 'COPY_LOCATION', self.bones.ctrl.pivot,
+ space='LOCAL', invert_xyz=(True,)*3
+ )
+
+
+ def generate_widgets(self):
+ if self.make_pivot:
+ create_pivot_widget(self.obj, self.bones.ctrl.pivot, square=True, axis_size=2.0)
+
+ if self.make_control:
+ set_bone_widget_transform(self.obj, self.bones.ctrl.master, self.bones.org)
+
+ create_cube_widget(self.obj, self.bones.ctrl.master, radius=0.5)
+
+
+ @classmethod
+ def add_parameters(self, params):
+ params.make_control = bpy.props.BoolProperty(
+ name = "Control",
+ default = True,
+ description = "Create a control bone for the copy"
+ )
+
+ params.make_parent_switch = bpy.props.BoolProperty(
+ name = "Switchable Parent",
+ default = False,
+ description = "Allow switching the parent of the master control"
+ )
+
+ params.register_parent = bpy.props.BoolProperty(
+ name = "Register Parent",
+ default = False,
+ description = "Register the control as a switchable parent candidate"
+ )
+
+ params.register_parent_tags = bpy.props.StringProperty(
+ name = "Parent Tags",
+ default = "",
+ description = "Comma-separated tags to use for the registered parent"
+ )
+
+ params.make_extra_control = bpy.props.BoolProperty(
+ name = "Extra Control",
+ default = False,
+ description = "Create an optional control"
+ )
+
+ params.make_extra_deform = bpy.props.BoolProperty(
+ name = "Extra Deform",
+ default = False,
+ description = "Create an optional deform bone"
+ )
+
+
+ @classmethod
+ def parameters_ui(self, layout, params):
+ r = layout.row()
+ r.prop(params, "make_extra_control", text="Master Control")
+
+ if params.make_extra_control:
+ layout.prop(params, "make_parent_switch")
+ layout.prop(params, "register_parent")
+
+ r = layout.row()
+ r.active = params.register_parent
+ r.prop(params, "register_parent_tags", text="Tags")
+
+ layout.prop(params, "make_control", text="Pivot Control")
+
+ r = layout.row()
+ r.prop(params, "make_extra_deform", text="Deform Bone")
+
+
+def create_sample(obj):
+ """ Create a sample metarig for this rig type.
+ """
+ # generated by rigify.utils.write_metarig
+ bpy.ops.object.mode_set(mode='EDIT')
+ arm = obj.data
+
+ bones = {}
+
+ bone = arm.edit_bones.new('pivot')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.0000, 0.5000, 0.0000
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bones['pivot'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['pivot']]
+ pbone.rigify_type = 'basic.pivot'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+
+ bpy.ops.object.mode_set(mode='EDIT')
+ for bone in arm.edit_bones:
+ bone.select = False
+ bone.select_head = False
+ bone.select_tail = False
+ for b in bones:
+ bone = arm.edit_bones[bones[b]]
+ bone.select = True
+ bone.select_head = True
+ bone.select_tail = True
+ arm.edit_bones.active = bone
+
+ return bones
diff --git a/rigify/rigs/basic/super_copy.py b/rigify/rigs/basic/super_copy.py
index 5abbf22e..f55dce68 100644
--- a/rigify/rigs/basic/super_copy.py
+++ b/rigify/rigs/basic/super_copy.py
@@ -22,9 +22,12 @@ import bpy
from ...base_rig import BaseRig
+from ...utils.layers import DEF_LAYER
from ...utils.naming import strip_org, make_deformer_name
from ...utils.widgets_basic import create_bone_widget, create_circle_widget
+from itertools import repeat
+
class Rig(BaseRig):
""" A "copy" rig. All it does is duplicate the original bone and
@@ -43,7 +46,14 @@ class Rig(BaseRig):
self.make_control = self.params.make_control
self.make_widget = self.params.make_widget
- self.make_deform = self.params.make_deform
+
+ deform = self.params.make_deform
+ rename = self.params.rename_to_deform
+
+ self.make_deform = deform and not rename
+ self.rename_deform = deform and rename
+
+ self.relink = self.params.relink_constraints
def generate_bones(self):
@@ -64,6 +74,18 @@ class Rig(BaseRig):
if self.make_deform:
self.set_bone_parent(bones.deform, bones.org, use_connect=False)
+ if self.relink:
+ self.generator.disable_auto_parent(bones.org)
+
+ parent_spec = self.params.parent_bone
+ if parent_spec:
+ old_parent = self.get_bone_parent(bones.org)
+ new_parent = self.find_relink_target(parent_spec, old_parent or '') or None
+ self.set_bone_parent(bones.org, new_parent)
+
+ if self.make_control:
+ self.set_bone_parent(bones.ctrl, new_parent)
+
def configure_bones(self):
bones = self.bones
@@ -75,9 +97,53 @@ class Rig(BaseRig):
def rig_bones(self):
bones = self.bones
+ if self.relink:
+ for con in self.get_bone(bones.org).constraints:
+ parts = con.name.split('@')
+
+ if len(parts) > 1:
+ self.relink_constraint(con, parts[1:])
+
if self.make_control:
# Constrain the original bone.
- self.make_constraint(bones.org, 'COPY_TRANSFORMS', bones.ctrl)
+ self.make_constraint(bones.org, 'COPY_TRANSFORMS', bones.ctrl, insert_index=0)
+
+ def relink_constraint(self, con, specs):
+ if con.type == 'ARMATURE':
+ if len(specs) == 1:
+ specs = repeat(specs[0])
+ elif len(specs) != len(con.specs):
+ self.report_error("Constraint {} actually has {} targets", con.name, len(con.targets))
+
+ for tgt, spec in zip(con.targets, specs):
+ tgt.subtarget = self.find_relink_target(spec, tgt.subtarget)
+
+ else:
+ if len(specs) > 1:
+ self.report_error("Only the Armature constraint can have multiple '@' targets: {}", con.name)
+
+ con.subtarget = self.find_relink_target(specs[0], con.subtarget)
+
+ def find_relink_target(self, spec, old_target):
+ if spec == '':
+ return old_target
+ elif spec in {'DEF', 'MCH'}:
+ spec = spec + '-' + strip_org(old_target)
+
+ if spec not in self.obj.pose.bones:
+ # Hack: allow referring to copy rigs using Rename To Deform as DEF
+ if old_target.startswith('ORG-') and spec == make_deformer_name(strip_org(old_target)):
+ from . import copy_chain
+
+ owner = self.generator.bone_owners.get(old_target)
+
+ if ((isinstance(owner, Rig) and owner.rename_deform) or
+ (isinstance(owner, copy_chain.Rig) and owner.rename_deforms)):
+ return old_target
+
+ self.report_error("Cannot find bone '{}' for relinking", spec)
+
+ return spec
def generate_widgets(self):
@@ -91,6 +157,15 @@ class Rig(BaseRig):
create_bone_widget(self.obj, bones.ctrl)
+ def finalize(self):
+ if self.rename_deform:
+ new_name = self.rename_bone(self.bones.org, make_deformer_name(self.org_name))
+
+ bone = self.get_bone(new_name).bone
+ bone.use_deform = True
+ bone.layers = DEF_LAYER
+
+
@classmethod
def add_parameters(self, params):
""" Add the parameters of this rig type to the
@@ -114,6 +189,24 @@ class Rig(BaseRig):
description = "Create a deform bone for the copy"
)
+ params.rename_to_deform = bpy.props.BoolProperty(
+ name = "Rename To Deform",
+ default = False,
+ description = "Rename the original bone itself to use as deform bone (advanced feature)"
+ )
+
+ params.relink_constraints = bpy.props.BoolProperty(
+ name = "Relink Constraints",
+ default = False,
+ description = "For constraints with names formed like 'base@bonename', use the part after '@' as the new subtarget after all bones are created. Use '@DEF' or '@MCH' to simply prepend the prefix"
+ )
+
+ params.parent_bone = bpy.props.StringProperty(
+ name = "Parent",
+ default = "",
+ description = "Replace the parent with a different bone after all bones are created. Using simply DEF or MCH will prepend the prefix instead"
+ )
+
@classmethod
def parameters_ui(self, layout, params):
@@ -127,6 +220,17 @@ class Rig(BaseRig):
r = layout.row()
r.prop(params, "make_deform")
+ if params.make_deform:
+ r = layout.row()
+ r.prop(params, "rename_to_deform")
+
+ r = layout.row()
+ r.prop(params, "relink_constraints")
+
+ if params.relink_constraints:
+ r = layout.row()
+ r.prop(params, "parent_bone")
+
def create_sample(obj):
""" Create a sample metarig for this rig type.
diff --git a/rigify/rigs/chain_rigs.py b/rigify/rigs/chain_rigs.py
index 3f53cd69..fc070eb1 100644
--- a/rigify/rigs/chain_rigs.py
+++ b/rigify/rigs/chain_rigs.py
@@ -82,10 +82,10 @@ class SimpleChainRig(BaseRig):
@stage.generate_widgets
def make_control_widgets(self):
- for ctrl in self.bones.ctrl.fk:
- self.make_control_widget(ctrl)
+ for args in zip(count(0), self.bones.ctrl.fk):
+ self.make_control_widget(*args)
- def make_control_widget(self, ctrl):
+ def make_control_widget(self, i, ctrl):
create_bone_widget(self.obj, ctrl)
##############################
diff --git a/rigify/rigs/experimental/super_chain.py b/rigify/rigs/experimental/super_chain.py
index df0facdf..3f7ca5d6 100644
--- a/rigify/rigs/experimental/super_chain.py
+++ b/rigify/rigs/experimental/super_chain.py
@@ -714,10 +714,10 @@ def add_parameters(params):
)
params.bbones = bpy.props.IntProperty(
- name='bbone segments',
- default=10,
- min=1,
- description='Number of segments'
+ name = 'B-Bone Segments',
+ default = 10,
+ min = 1,
+ description = 'Number of B-Bone segments'
)
params.wgt_offset = bpy.props.FloatProperty(
diff --git a/rigify/rigs/limbs/arm.py b/rigify/rigs/limbs/arm.py
index 98a3c50f..e79edc5c 100644
--- a/rigify/rigs/limbs/arm.py
+++ b/rigify/rigs/limbs/arm.py
@@ -21,12 +21,15 @@
import bpy
from itertools import count
+from mathutils import Matrix
-from ...utils.bones import BoneDict, compute_chain_x_axis, align_bone_x_axis, align_bone_z_axis
+from ...utils.bones import put_bone, compute_chain_x_axis, align_bone_x_axis, align_bone_z_axis
from ...utils.naming import make_derived_name
from ...utils.misc import map_list
+from ...utils.widgets import adjust_widget_transform_mesh
from ..widgets import create_hand_widget
+from ...utils.widgets_basic import create_circle_widget
from ...base_rig import stage
@@ -42,6 +45,8 @@ class Rig(BaseLimbRig):
super().initialize()
+ self.make_wrist_pivot = self.params.make_ik_wrist_pivot
+
def prepare_bones(self):
orgs = self.bones.org.main
@@ -62,16 +67,76 @@ class Rig(BaseLimbRig):
def register_switch_parents(self, pbuilder):
super().register_switch_parents(pbuilder)
- pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True)
+ pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True, tags={'limb_end'})
def make_ik_ctrl_widget(self, ctrl):
create_hand_widget(self.obj, ctrl)
####################################################
+ # Palm Pivot
+
+ def get_ik_input_bone(self):
+ if self.make_wrist_pivot:
+ return self.bones.mch.ik_wrist
+ else:
+ return self.get_ik_control_output()
+
+ def get_extra_ik_controls(self):
+ controls = super().get_extra_ik_controls()
+ if self.make_wrist_pivot:
+ controls += [self.bones.ctrl.ik_wrist]
+ return controls
+
+ @stage.generate_bones
+ def make_wrist_pivot_control(self):
+ if self.make_wrist_pivot:
+ org = self.bones.org.main[2]
+ self.bones.ctrl.ik_wrist = self.make_wrist_pivot_bone(org)
+ self.bones.mch.ik_wrist = self.copy_bone(org, make_derived_name(org, 'mch', '_ik_wrist'), scale=0.25)
+
+ def make_wrist_pivot_bone(self, org):
+ name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_ik_wrist'), scale=0.5)
+ put_bone(self.obj, name, self.get_bone(org).tail)
+ return name
+
+ @stage.parent_bones
+ def parent_wrist_pivot_control(self):
+ if self.make_wrist_pivot:
+ ctrl = self.bones.ctrl.ik_wrist
+ self.set_bone_parent(ctrl, self.get_ik_control_output())
+ self.set_bone_parent(self.bones.mch.ik_wrist, ctrl)
+
+ @stage.generate_widgets
+ def make_wrist_pivot_widget(self):
+ if self.make_wrist_pivot:
+ ctrl = self.bones.ctrl.ik_wrist
+
+ if self.main_axis == 'x':
+ obj = create_circle_widget(self.obj, ctrl, head_tail=-0.3, head_tail_x=0.5)
+ else:
+ obj = create_circle_widget(self.obj, ctrl, head_tail=0.5, head_tail_x=-0.3)
+
+ if obj:
+ org_bone = self.get_bone(self.bones.org.main[2])
+ offset = org_bone.head - self.get_bone(ctrl).head
+ adjust_widget_transform_mesh(obj, Matrix.Translation(offset))
+
+ ####################################################
# Settings
@classmethod
+ def add_parameters(self, params):
+ super().add_parameters(params)
+
+ params.make_ik_wrist_pivot = bpy.props.BoolProperty(
+ name="IK Wrist Pivot", default=False,
+ description="Make an extra IK hand control pivoting around the tip of the hand"
+ )
+
+ @classmethod
def parameters_ui(self, layout, params):
+ layout.prop(params, "make_ik_wrist_pivot")
+
super().parameters_ui(layout, params, 'Hand')
diff --git a/rigify/rigs/limbs/leg.py b/rigify/rigs/limbs/leg.py
index dbdd20cb..b409d009 100644
--- a/rigify/rigs/limbs/leg.py
+++ b/rigify/rigs/limbs/leg.py
@@ -22,13 +22,14 @@ import bpy
import math
from itertools import count
-from mathutils import Vector
+from mathutils import Vector, Matrix
from ...utils.rig import is_rig_base_bone
from ...utils.bones import align_chain_x_axis, align_bone_x_axis, align_bone_y_axis, align_bone_z_axis
from ...utils.bones import align_bone_to_axis, flip_bone, put_bone, align_bone_orientation
from ...utils.naming import make_derived_name
-from ...utils.misc import map_list
+from ...utils.misc import map_list, matrix_from_axis_roll, matrix_from_axis_pair
+from ...utils.widgets import adjust_widget_transform_mesh
from ..widgets import create_foot_widget, create_ballsocket_widget
@@ -62,10 +63,18 @@ class Rig(BaseLimbRig):
super().initialize()
+ self.pivot_type = self.params.foot_pivot_type
+ self.heel_euler_order = 'ZXY' if self.main_axis == 'x' else 'XZY'
+
+ assert self.pivot_type in {'ANKLE', 'TOE', 'ANKLE_TOE'}
+
def prepare_bones(self):
orgs = self.bones.org.main
+ foot = self.get_bone(orgs[2])
- foot_x = self.vector_without_z(self.get_bone(orgs[2]).y_axis).cross((0, 0, -1))
+ ik_y_axis = (0, 1, 0)
+ foot_y_axis = -self.vector_without_z(foot.y_axis)
+ foot_x = foot_y_axis.cross((0, 0, 1))
if self.params.rotation_axis == 'automatic':
align_chain_x_axis(self.obj, orgs[0:2])
@@ -84,6 +93,12 @@ class Rig(BaseLimbRig):
align_bone_z_axis(self.obj, orgs[2], foot_x)
align_bone_z_axis(self.obj, orgs[3], -foot_x)
+ else:
+ ik_y_axis = foot_y_axis
+
+ # Orientation of the IK main and roll control bones
+ self.ik_matrix = matrix_from_axis_roll(ik_y_axis, 0)
+ self.roll_matrix = matrix_from_axis_pair(ik_y_axis, foot_x, self.main_axis)
####################################################
# EXTRA BONES
@@ -92,6 +107,8 @@ class Rig(BaseLimbRig):
# heel:
# Heel location marker bone
# ctrl:
+ # ik_spin:
+ # Toe spin control.
# heel:
# Foot roll control
# mch:
@@ -104,31 +121,71 @@ class Rig(BaseLimbRig):
# IK controls
def get_extra_ik_controls(self):
- return [self.bones.ctrl.heel]
+ controls = super().get_extra_ik_controls() + [self.bones.ctrl.heel]
+ if self.pivot_type == 'ANKLE_TOE':
+ controls += [self.bones.ctrl.ik_spin]
+ return controls
def make_ik_control_bone(self, orgs):
name = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik'))
-
- if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity:
- align_bone_to_axis(self.obj, name, 'y', flip=True)
-
+ if self.pivot_type == 'TOE':
+ put_bone(self.obj, name, self.get_bone(name).tail, matrix=self.ik_matrix)
else:
- flip_bone(self.obj, name)
-
- bone = self.get_bone(name)
- bone.tail[2] = bone.head[2]
- bone.roll = 0
-
+ put_bone(self.obj, name, None, matrix=self.ik_matrix)
return name
+ def build_ik_pivot(self, ik_name, **args):
+ heel_bone = self.get_bone(self.bones.org.heel)
+ args = {
+ 'position': (heel_bone.head + heel_bone.tail)/2,
+ **args
+ }
+ return super().build_ik_pivot(ik_name, **args)
+
def register_switch_parents(self, pbuilder):
super().register_switch_parents(pbuilder)
- pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True)
+ pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True, tags={'limb_end'})
def make_ik_ctrl_widget(self, ctrl):
- create_foot_widget(self.obj, ctrl)
+ obj = create_foot_widget(self.obj, ctrl)
+ if self.pivot_type != 'TOE':
+ ctrl = self.get_bone(ctrl)
+ org = self.get_bone(self.bones.org.main[2])
+ offset = org.tail - (ctrl.custom_shape_transform or ctrl).head
+ adjust_widget_transform_mesh(obj, Matrix.Translation(offset))
+
+ ####################################################
+ # IK pivot controls
+
+ def get_ik_pivot_output(self):
+ if self.pivot_type == 'ANKLE_TOE':
+ return self.bones.ctrl.ik_spin
+ else:
+ return self.get_ik_control_output()
+
+ @stage.generate_bones
+ def make_ik_pivot_controls(self):
+ if self.pivot_type == 'ANKLE_TOE':
+ self.bones.ctrl.ik_spin = self.make_ik_spin_bone(self.bones.org.main)
+
+ def make_ik_spin_bone(self, orgs):
+ name = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_spin_ik'))
+ put_bone(self.obj, name, self.get_bone(orgs[3]).head, matrix=self.ik_matrix, scale=0.5)
+ return name
+
+ @stage.parent_bones
+ def parent_ik_pivot_controls(self):
+ if self.pivot_type == 'ANKLE_TOE':
+ self.set_bone_parent(self.bones.ctrl.ik_spin, self.get_ik_control_output())
+
+ @stage.generate_widgets
+ def make_ik_spin_control_widget(self):
+ if self.pivot_type == 'ANKLE_TOE':
+ obj = create_ballsocket_widget(self.obj, self.bones.ctrl.ik_spin, size=0.75)
+ rotfix = Matrix.Rotation(math.pi/2, 4, self.main_axis.upper())
+ adjust_widget_transform_mesh(obj, rotfix, local=True)
####################################################
# Heel control
@@ -136,25 +193,19 @@ class Rig(BaseLimbRig):
@stage.generate_bones
def make_heel_control_bone(self):
org = self.bones.org.main[2]
- name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_heel_ik'), scale=1/2)
+ name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_heel_ik'))
+ put_bone(self.obj, name, None, matrix=self.roll_matrix, scale=0.5)
self.bones.ctrl.heel = name
- self.align_roll_bone(org, name, -self.vector_without_z(self.get_bone(org).vector))
-
@stage.parent_bones
def parent_heel_control_bone(self):
- self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.ik)
+ self.set_bone_parent(self.bones.ctrl.heel, self.get_ik_pivot_output(), inherit_scale='AVERAGE')
@stage.configure_bones
def configure_heel_control_bone(self):
bone = self.get_bone(self.bones.ctrl.heel)
bone.lock_location = True, True, True
-
- if self.main_axis == 'x':
- bone.lock_rotation = False, False, True
- else:
- bone.lock_rotation = True, False, False
-
+ bone.rotation_mode = self.heel_euler_order
bone.lock_scale = True, True, True
@stage.generate_widgets
@@ -173,34 +224,27 @@ class Rig(BaseLimbRig):
def make_roll_mch_bones(self, foot, toe, heel):
foot_bone = self.get_bone(foot)
heel_bone = self.get_bone(heel)
- axis = -self.vector_without_z(foot_bone.vector)
-
- roll1 = self.copy_bone(foot, make_derived_name(heel, 'mch', '_roll1'))
- flip_bone(self.obj, roll1)
- self.align_roll_bone(foot, roll1, -foot_bone.vector)
+ heel_middle = (heel_bone.head + heel_bone.tail) / 2
- roll2 = self.copy_bone(toe, make_derived_name(heel, 'mch', '_roll2'), scale=1/4)
-
- put_bone(self.obj, roll2, (heel_bone.head + heel_bone.tail) / 2)
- self.align_roll_bone(foot, roll2, -axis)
+ result = self.copy_bone(foot, make_derived_name(foot, 'mch', '_roll'), scale=0.25)
+ roll1 = self.copy_bone(toe, make_derived_name(heel, 'mch', '_roll1'), scale=0.3)
+ roll2 = self.copy_bone(toe, make_derived_name(heel, 'mch', '_roll2'), scale=0.3)
rock1 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock1'))
-
- align_bone_to_axis(self.obj, rock1, 'y', roll=0, length=heel_bone.length/2, flip=True)
- align_bone_y_axis(self.obj, rock1, axis)
-
rock2 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock2'))
- align_bone_to_axis(self.obj, rock2, 'y', roll=0, length=heel_bone.length/2)
- align_bone_y_axis(self.obj, rock2, axis)
+ put_bone(self.obj, roll1, None, matrix=self.roll_matrix)
+ put_bone(self.obj, roll2, heel_middle, matrix=self.roll_matrix)
+ put_bone(self.obj, rock1, heel_bone.tail, matrix=self.roll_matrix, scale=0.5)
+ put_bone(self.obj, rock2, heel_bone.head, matrix=self.roll_matrix, scale=0.5)
- return [ rock2, rock1, roll2, roll1 ]
+ return [ rock2, rock1, roll2, roll1, result ]
@stage.parent_bones
def parent_roll_mch_chain(self):
chain = self.bones.mch.heel
- self.set_bone_parent(chain[0], self.bones.ctrl.ik)
+ self.set_bone_parent(chain[0], self.get_ik_pivot_output())
self.parent_bone_chain(chain)
@stage.rig_bones
@@ -208,28 +252,37 @@ class Rig(BaseLimbRig):
self.rig_roll_mch_bones(self.bones.mch.heel, self.bones.ctrl.heel, self.bones.org.heel)
def rig_roll_mch_bones(self, chain, heel, org_heel):
- rock2, rock1, roll2, roll1 = chain
+ rock2, rock1, roll2, roll1, result = chain
+
+ # This order is required for correct working of the constraints
+ for bone in chain:
+ self.get_bone(bone).rotation_mode = self.heel_euler_order
- self.make_constraint(roll1, 'COPY_ROTATION', heel, space='LOCAL')
- self.make_constraint(roll2, 'COPY_ROTATION', heel, space='LOCAL')
+ self.make_constraint(roll1, 'COPY_ROTATION', heel, space='POSE')
if self.main_axis == 'x':
- self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_x=True, max_x=DEG_360, space='LOCAL')
- self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_x=-DEG_360, space='LOCAL')
+ self.make_constraint(roll2, 'COPY_ROTATION', heel, space='LOCAL', use_xyz=(True, False, False))
+ self.make_constraint(roll2, 'LIMIT_ROTATION', min_x=-DEG_360, space='LOCAL')
else:
- self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_z=True, max_z=DEG_360, space='LOCAL')
- self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_z=-DEG_360, space='LOCAL')
+ self.make_constraint(roll2, 'COPY_ROTATION', heel, space='LOCAL', use_xyz=(False, False, True))
+ self.make_constraint(roll2, 'LIMIT_ROTATION', min_z=-DEG_360, space='LOCAL')
direction = self.get_main_axis(self.get_bone(heel)).dot(self.get_bone(org_heel).vector)
if direction < 0:
rock2, rock1 = rock1, rock2
- self.make_constraint(rock1, 'COPY_ROTATION', heel, space='LOCAL')
- self.make_constraint(rock2, 'COPY_ROTATION', heel, space='LOCAL')
+ self.make_constraint(
+ rock1, 'COPY_ROTATION', heel, space='LOCAL',
+ use_xyz=(False, True, False),
+ )
+ self.make_constraint(
+ rock2, 'COPY_ROTATION', heel, space='LOCAL',
+ use_xyz=(False, True, False),
+ )
- self.make_constraint(rock1, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, max_y=DEG_360, space='LOCAL')
- self.make_constraint(rock2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_y=-DEG_360, space='LOCAL')
+ self.make_constraint(rock1, 'LIMIT_ROTATION', max_y=DEG_360, space='LOCAL')
+ self.make_constraint(rock2, 'LIMIT_ROTATION', min_y=-DEG_360, space='LOCAL')
####################################################
@@ -237,7 +290,7 @@ class Rig(BaseLimbRig):
def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
if i == 3:
- align_bone_orientation(self.obj, parent_mch, self.bones.mch.heel[-2])
+ align_bone_orientation(self.obj, parent_mch, self.bones.mch.heel[2])
self.set_bone_parent(parent_mch, prev_org, use_connect=True)
@@ -246,7 +299,7 @@ class Rig(BaseLimbRig):
def rig_fk_parent_bone(self, i, parent_mch, org):
if i == 3:
- con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.heel[-2])
+ con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.heel[2])
self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0])
@@ -257,8 +310,6 @@ class Rig(BaseLimbRig):
####################################################
# IK system MCH
- ik_input_head_tail = 1.0
-
def get_ik_input_bone(self):
return self.bones.mch.heel[-1]
@@ -266,14 +317,35 @@ class Rig(BaseLimbRig):
def parent_ik_mch_chain(self):
super().parent_ik_mch_chain()
- self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel)
+ self.set_bone_parent(self.bones.mch.ik_target, self.bones.mch.heel[-1])
####################################################
# Settings
@classmethod
+ def add_parameters(self, params):
+ super().add_parameters(params)
+
+ items = [
+ ('ANKLE', 'Ankle',
+ 'The foots pivots at the ankle'),
+ ('TOE', 'Toe',
+ 'The foot pivots around the base of the toe'),
+ ('ANKLE_TOE', 'Ankle and Toe',
+ 'The foots pivots at the ankle, with extra toe pivot'),
+ ]
+
+ params.foot_pivot_type = bpy.props.EnumProperty(
+ items = items,
+ name = "Foot Pivot",
+ default = 'ANKLE_TOE'
+ )
+
+ @classmethod
def parameters_ui(self, layout, params):
+ layout.prop(params, 'foot_pivot_type')
+
super().parameters_ui(layout, params, 'Foot')
diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py
index 81079c05..f1eb8639 100644
--- a/rigify/rigs/limbs/limb_rigs.py
+++ b/rigify/rigs/limbs/limb_rigs.py
@@ -23,12 +23,12 @@ import json
from ...utils.animation import add_generic_snap_fk_to_ik, add_fk_ik_snap_buttons
from ...utils.rig import connected_children_names
-from ...utils.bones import BoneDict, put_bone, compute_chain_x_axis, align_bone_orientation
-from ...utils.bones import align_bone_x_axis, align_bone_y_axis, align_bone_z_axis
+from ...utils.bones import BoneDict, put_bone, compute_chain_x_axis, align_bone_orientation, set_bone_widget_transform
from ...utils.naming import strip_org, make_derived_name
from ...utils.layers import ControlLayersOption
from ...utils.misc import pairwise_nozip, padnone, map_list
from ...utils.switch_parent import SwitchParentBuilder
+from ...utils.components import CustomPivotControl
from ...base_rig import stage, BaseRig
@@ -64,6 +64,7 @@ class BaseLimbRig(BaseRig):
self.segments = self.params.segments
self.bbone_segments = self.params.bbones
+ self.use_ik_pivot = self.params.make_custom_pivot
rot_axis = self.params.rotation_axis
@@ -124,15 +125,6 @@ class BaseLimbRig(BaseRig):
bone = self.get_bone(org)
return bone.head + bone.vector * (seg / self.segments)
- def align_roll_bone(self, org, name, y_axis):
- if y_axis:
- align_bone_y_axis(self.obj, name, y_axis)
-
- if self.main_axis == 'x':
- align_bone_x_axis(self.obj, name, self.get_bone(org).x_axis)
- else:
- align_bone_z_axis(self.obj, name, self.get_bone(org).z_axis)
-
@staticmethod
def vector_without_z(vector):
return Vector((vector[0], vector[1], 0))
@@ -154,6 +146,8 @@ class BaseLimbRig(BaseRig):
# IK controls
# ik_vispole
# IK pole visualization.
+ # ik_pivot
+ # Custom IK pivot (optional).
# mch:
# master:
# Parent of the master control.
@@ -161,6 +155,8 @@ class BaseLimbRig(BaseRig):
# FK follow behavior.
# fk[]:
# FK chain parents (or None)
+ # ik_pivot
+ # Custom IK pivot result (optional).
# ik_stretch
# IK stretch switch implementation.
# ik_target
@@ -328,22 +324,30 @@ class BaseLimbRig(BaseRig):
# IK controls
def get_extra_ik_controls(self):
- return []
+ if self.component_ik_pivot:
+ return [self.component_ik_pivot.control]
+ else:
+ return []
def get_all_ik_controls(self):
ctrl = self.bones.ctrl
- return [ctrl.ik_base, ctrl.ik_pole, ctrl.ik] + self.get_extra_ik_controls()
+ controls = [ctrl.ik_base, ctrl.ik_pole, ctrl.ik]
+ return controls + self.get_extra_ik_controls()
@stage.generate_bones
def make_ik_controls(self):
orgs = self.bones.org.main
- self.bones.ctrl.ik_base = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik'))
+ self.bones.ctrl.ik_base = self.make_ik_base_bone(orgs)
self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs)
- self.bones.ctrl.ik = self.make_ik_control_bone(orgs)
+ self.bones.ctrl.ik = ik_name = self.make_ik_control_bone(orgs)
+ self.component_ik_pivot = self.build_ik_pivot(ik_name)
self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
+ def make_ik_base_bone(self, orgs):
+ return self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik'))
+
def make_ik_pole_bone(self, orgs):
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik_target'))
@@ -357,10 +361,25 @@ class BaseLimbRig(BaseRig):
def make_ik_control_bone(self, orgs):
return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik'))
+ def build_ik_pivot(self, ik_name, **args):
+ if self.use_ik_pivot:
+ return CustomPivotControl(self, 'ik_pivot', ik_name, parent=ik_name, **args)
+
+ def get_ik_control_output(self):
+ if self.component_ik_pivot:
+ return self.component_ik_pivot.output
+ else:
+ return self.bones.ctrl.ik
+
def register_switch_parents(self, pbuilder):
if self.rig_parent_bone:
pbuilder.register_parent(self, self.rig_parent_bone)
+ pbuilder.register_parent(
+ self, self.get_ik_control_output, name=self.bones.ctrl.ik,
+ exclude_self=True, tags={'limb_ik', 'child'},
+ )
+
def build_ik_parent_switch(self, pbuilder):
ctrl = self.bones.ctrl
@@ -398,9 +417,13 @@ class BaseLimbRig(BaseRig):
@stage.generate_widgets
def make_ik_control_widgets(self):
- self.make_ik_base_widget(self.bones.ctrl.ik_base)
- self.make_ik_pole_widget(self.bones.ctrl.ik_pole)
- self.make_ik_ctrl_widget(self.bones.ctrl.ik)
+ ctrl = self.bones.ctrl
+
+ set_bone_widget_transform(self.obj, ctrl.ik, self.get_ik_control_output())
+
+ self.make_ik_base_widget(ctrl.ik_base)
+ self.make_ik_pole_widget(ctrl.ik_pole)
+ self.make_ik_ctrl_widget(ctrl.ik)
def make_ik_base_widget(self, ctrl):
if self.main_axis == 'x':
@@ -453,7 +476,7 @@ class BaseLimbRig(BaseRig):
ik_input_head_tail = 0.0
def get_ik_input_bone(self):
- return self.bones.ctrl.ik
+ return self.get_ik_control_output()
def get_ik_output_chain(self):
return [self.bones.ctrl.ik_base, self.bones.mch.ik_end, self.bones.mch.ik_target]
@@ -670,7 +693,13 @@ class BaseLimbRig(BaseRig):
@stage.parent_bones
def parent_tweak_mch_chain(self):
- for mch, entry in zip(self.bones.mch.tweak, self.segment_table_tweak):
+ for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak):
+ self.parent_tweak_mch_bone(*args)
+
+ def parent_tweak_mch_bone(self, i, mch, entry):
+ if i == 0:
+ self.set_bone_parent(mch, self.rig_parent_bone, inherit_scale='FIX_SHEAR')
+ else:
self.set_bone_parent(mch, entry.org)
@stage.rig_bones
@@ -694,6 +723,10 @@ class BaseLimbRig(BaseRig):
elif entry.seg_idx is not None:
self.make_constraint(tweak, 'COPY_SCALE', 'root', use_make_uniform=True)
+ if i == 0:
+ self.make_constraint(tweak, 'COPY_LOCATION', entry.org)
+ self.make_constraint(tweak, 'DAMPED_TRACK', entry.org, head_tail=1)
+
####################################################
# Deform chain
@@ -786,17 +819,23 @@ class BaseLimbRig(BaseRig):
)
params.segments = bpy.props.IntProperty(
- name = 'limb segments',
+ name = 'Limb Segments',
default = 2,
min = 1,
- description = 'Number of segments'
+ description = 'Number of limb segments'
)
params.bbones = bpy.props.IntProperty(
- name = 'bbone segments',
+ name = 'B-Bone Segments',
default = 10,
min = 1,
- description = 'Number of segments'
+ description = 'Number of B-Bone segments'
+ )
+
+ params.make_custom_pivot = bpy.props.BoolProperty(
+ name = "Custom Pivot Control",
+ default = False,
+ description = "Create a rotation pivot control that can be repositioned arbitrarily"
)
# Setting up extra layers for the FK and tweak
@@ -820,6 +859,8 @@ class BaseLimbRig(BaseRig):
r = layout.row()
r.prop(params, "bbones")
+ layout.prop(params, 'make_custom_pivot', text="Custom IK Pivot")
+
ControlLayersOption.FK.parameters_ui(layout, params)
ControlLayersOption.TWEAK.parameters_ui(layout, params)
@@ -843,8 +884,6 @@ class RigifyLimbIk2FkBase:
ctrl_bones: StringProperty(name="IK Controls")
extra_ctrls: StringProperty(name="Extra IK Controls")
- keyflags = None
-
def init_execute(self, context):
if self.fk_bones:
self.fk_bone_list = json.loads(self.fk_bones)
@@ -921,13 +960,11 @@ class RigifyLimbIk2FkBase:
class POSE_OT_rigify_limb_ik2fk(RigifyLimbIk2FkBase, RigifySingleUpdateMixin, bpy.types.Operator):
bl_idname = "pose.rigify_limb_ik2fk_" + rig_id
bl_label = "Snap IK->FK"
- bl_options = {'UNDO', 'INTERNAL'}
bl_description = "Snap the IK chain to FK result"
class POSE_OT_rigify_limb_ik2fk_bake(RigifyLimbIk2FkBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
bl_idname = "pose.rigify_limb_ik2fk_bake_" + rig_id
bl_label = "Apply Snap IK->FK To Keyframes"
- bl_options = {'UNDO', 'INTERNAL'}
bl_description = "Snap the IK chain keyframes to FK result"
def execute_scan_curves(self, context, obj):
@@ -968,8 +1005,6 @@ SCRIPT_UTILITIES_OP_TOGGLE_POLE = SCRIPT_UTILITIES_OP_SNAP_IK_FK + ['''
class RigifyLimbTogglePoleBase(RigifyLimbIk2FkBase):
use_pole: bpy.props.BoolProperty(name="Use Pole Vector")
- keyflags_switch = None
-
def save_frame_state(self, context, obj):
return get_chain_transform_matrices(obj, self.ik_bone_list)
@@ -1007,13 +1042,11 @@ class RigifyLimbTogglePoleBase(RigifyLimbIk2FkBase):
class POSE_OT_rigify_limb_toggle_pole(RigifyLimbTogglePoleBase, RigifySingleUpdateMixin, bpy.types.Operator):
bl_idname = "pose.rigify_limb_toggle_pole_" + rig_id
bl_label = "Toggle Pole"
- bl_options = {'UNDO', 'INTERNAL'}
bl_description = "Switch the IK chain between pole and rotation"
class POSE_OT_rigify_limb_toggle_pole_bake(RigifyLimbTogglePoleBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
bl_idname = "pose.rigify_limb_toggle_pole_bake_" + rig_id
bl_label = "Apply Toggle Pole To Keyframes"
- bl_options = {'UNDO', 'INTERNAL'}
bl_description = "Switch the IK chain between pole and rotation over a frame range"
def execute_scan_curves(self, context, obj):
diff --git a/rigify/rigs/limbs/paw.py b/rigify/rigs/limbs/paw.py
index f8cb1f9f..28374eec 100644
--- a/rigify/rigs/limbs/paw.py
+++ b/rigify/rigs/limbs/paw.py
@@ -84,7 +84,7 @@ class Rig(BaseLimbRig):
# IK controls
def get_extra_ik_controls(self):
- return [self.bones.ctrl.heel]
+ return super().get_extra_ik_controls() + [self.bones.ctrl.heel]
def make_ik_control_bone(self, orgs):
name = self.copy_bone(orgs[3], make_derived_name(orgs[2], 'ctrl', '_ik'))
@@ -107,7 +107,7 @@ class Rig(BaseLimbRig):
def register_switch_parents(self, pbuilder):
super().register_switch_parents(pbuilder)
- pbuilder.register_parent(self, self.bones.org.main[3], exclude_self=True)
+ pbuilder.register_parent(self, self.bones.org.main[3], exclude_self=True, tags={'limb_end'})
def make_ik_ctrl_widget(self, ctrl):
create_foot_widget(self.obj, ctrl)
@@ -126,7 +126,7 @@ class Rig(BaseLimbRig):
@stage.parent_bones
def parent_heel_control_bone(self):
- self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.ik)
+ self.set_bone_parent(self.bones.ctrl.heel, self.get_ik_control_output())
@stage.configure_bones
def configure_heel_control_bone(self):
@@ -150,7 +150,7 @@ class Rig(BaseLimbRig):
def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
if i == 3:
self.set_bone_parent(parent_mch, prev_org, use_connect=True)
- self.set_bone_parent(self.bones.mch.toe_socket, self.bones.ctrl.ik)
+ self.set_bone_parent(self.bones.mch.toe_socket, self.get_ik_control_output())
else:
super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org)
diff --git a/rigify/rigs/limbs/simple_tentacle.py b/rigify/rigs/limbs/simple_tentacle.py
index 10bdd2b5..25d26e86 100644
--- a/rigify/rigs/limbs/simple_tentacle.py
+++ b/rigify/rigs/limbs/simple_tentacle.py
@@ -78,7 +78,7 @@ class Rig(TweakChainRig):
)
# Widgets
- def make_control_widget(self, ctrl):
+ def make_control_widget(self, i, ctrl):
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5)
diff --git a/rigify/rigs/limbs/super_finger.py b/rigify/rigs/limbs/super_finger.py
index 1a9171a7..0b3fcd8a 100644
--- a/rigify/rigs/limbs/super_finger.py
+++ b/rigify/rigs/limbs/super_finger.py
@@ -24,11 +24,12 @@ import re
from itertools import count
from ...utils.errors import MetarigError
-from ...utils.bones import flip_bone, align_chain_x_axis
+from ...utils.bones import put_bone, flip_bone, align_chain_x_axis, set_bone_widget_transform
from ...utils.naming import make_derived_name
from ...utils.widgets import create_widget
from ...utils.widgets_basic import create_circle_widget
from ...utils.misc import map_list
+from ...utils.layers import ControlLayersOption
from ...base_rig import stage
@@ -40,7 +41,7 @@ class Rig(SimpleChainRig):
def initialize(self):
super().initialize()
- self.bbone_segments = 8
+ self.bbone_segments = self.params.bbones
self.first_parent = self.get_bone_parent(self.bones.org[0])
def prepare_bones(self):
@@ -116,6 +117,8 @@ class Rig(SimpleChainRig):
for args in zip(count(0), self.bones.ctrl.fk, self.bones.org + [None]):
self.configure_control_bone(*args)
+ ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.fk)
+
def configure_control_bone(self, i, ctrl, org):
if org:
self.copy_bone_properties(org, ctrl)
@@ -125,11 +128,13 @@ class Rig(SimpleChainRig):
bone.lock_rotation = (True, True, True)
bone.lock_scale = (True, True, True)
- def make_control_widget(self, ctrl):
+ def make_control_widget(self, i, ctrl):
if ctrl == self.bones.ctrl.fk[-1]:
# Tip control
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.0)
else:
+ set_bone_widget_transform(self.obj, ctrl, self.bones.org[i])
+
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5)
##############################
@@ -234,11 +239,12 @@ class Rig(SimpleChainRig):
def configure_master_properties(self):
master = self.bones.ctrl.master
- self.make_property(master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect")
+ if self.bbone_segments > 1:
+ self.make_property(master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect")
- # Create UI
- panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten())
- panel.custom_prop(master, 'finger_curve', text="Curvature", slider=True)
+ # Create UI
+ panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten())
+ panel.custom_prop(master, 'finger_curve', text="Curvature", slider=True)
def rig_deform_bone(self, i, deform, org):
master = self.bones.ctrl.master
@@ -246,8 +252,9 @@ class Rig(SimpleChainRig):
self.make_constraint(deform, 'COPY_TRANSFORMS', org)
- self.make_driver(bone.bone, 'bbone_easein', variables=[(master, 'finger_curve')])
- self.make_driver(bone.bone, 'bbone_easeout', variables=[(master, 'finger_curve')])
+ if self.bbone_segments > 1:
+ self.make_driver(bone.bone, 'bbone_easein', variables=[(master, 'finger_curve')])
+ self.make_driver(bone.bone, 'bbone_easeout', variables=[(master, 'finger_curve')])
###############
# OPTIONS
@@ -261,6 +268,15 @@ class Rig(SimpleChainRig):
('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')]
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic')
+ params.bbones = bpy.props.IntProperty(
+ name = 'B-Bone Segments',
+ default = 10,
+ min = 1,
+ description = 'Number of B-Bone segments'
+ )
+
+ ControlLayersOption.TWEAK.add_parameters(params)
+
@classmethod
def parameters_ui(self, layout, params):
""" Create the ui for the rig parameters.
@@ -269,6 +285,10 @@ class Rig(SimpleChainRig):
r.label(text="Bend rotation axis:")
r.prop(params, "primary_rotation_axis", text="")
+ layout.prop(params, 'bbones')
+
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
+
def create_sample(obj):
# generated by rigify.utils.write_metarig
@@ -321,10 +341,6 @@ def create_sample(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
try:
- pbone.rigify_parameters.separate_extra_layers = True
- except AttributeError:
- pass
- try:
pbone.rigify_parameters.extra_layers = [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, False, False, False]
except AttributeError:
pass
diff --git a/rigify/rigs/limbs/super_palm.py b/rigify/rigs/limbs/super_palm.py
index 8bcbabf8..9c03b2fe 100644
--- a/rigify/rigs/limbs/super_palm.py
+++ b/rigify/rigs/limbs/super_palm.py
@@ -24,6 +24,7 @@ import re
from math import cos, pi
from itertools import count, repeat
+from rigify.utils.rig import is_rig_base_bone
from rigify.utils.naming import strip_org, make_derived_name
from rigify.utils.widgets import create_widget
from rigify.utils.misc import map_list
@@ -43,7 +44,7 @@ def bone_siblings(obj, bone):
bones = []
for b in parent.children:
- if b.name != bone:
+ if b.name != bone and not is_rig_base_bone(obj, b.name):
bones += [b.name]
return bones
diff --git a/rigify/rigs/spines/basic_spine.py b/rigify/rigs/spines/basic_spine.py
index 269889cf..c2905463 100644
--- a/rigify/rigs/spines/basic_spine.py
+++ b/rigify/rigs/spines/basic_spine.py
@@ -19,13 +19,16 @@
# <pep8 compliant>
import bpy
+import math
from itertools import count, repeat
+from mathutils import Matrix
from ...utils.errors import MetarigError
from ...utils.layers import ControlLayersOption
-from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name
-from ...utils.bones import BoneDict, put_bone, align_bone_to_axis
+from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name, make_derived_name
+from ...utils.bones import BoneDict, put_bone, align_bone_to_axis, align_bone_orientation, set_bone_widget_transform
+from ...utils.widgets import adjust_widget_transform_mesh
from ...utils.widgets_basic import create_circle_widget
from ...utils.misc import map_list
@@ -43,6 +46,7 @@ class Rig(BaseSpineRig):
# Check if user provided the pivot position
self.pivot_pos = self.params.pivot_pos
+ self.use_fk = self.params.make_fk_controls
if not (0 < self.pivot_pos < len(self.bones.org)):
self.raise_error("Please specify a valid pivot bone position.")
@@ -55,6 +59,9 @@ class Rig(BaseSpineRig):
# ctrl:
# master, hips, chest:
# Main controls.
+ # fk:
+ # chest[], hips[]:
+ # FK controls.
# tweak[]:
# Tweak control chain.
# mch:
@@ -73,19 +80,15 @@ class Rig(BaseSpineRig):
####################################################
# Master control bone
- @stage.generate_bones
- def make_master_control(self):
- super().make_master_control()
-
- # Put the main control in the middle of the hip bone
- base_bone = self.get_bone(self.bones.org[0])
- put_bone(self.obj, self.bones.ctrl.master, (base_bone.head + base_bone.tail) / 2)
+ def get_master_control_pos(self, orgs):
+ base_bone = self.get_bone(orgs[0])
+ return (base_bone.head + base_bone.tail) / 2
####################################################
# Main control bones
@stage.generate_bones
- def make_control_chain(self):
+ def make_end_control_bones(self):
orgs = self.bones.org
pivot = self.pivot_pos
@@ -103,33 +106,90 @@ class Rig(BaseSpineRig):
return name
@stage.parent_bones
- def parent_control_chain(self):
+ def parent_end_control_bones(self):
ctrl = self.bones.ctrl
- self.set_bone_parent(ctrl.hips, ctrl.master)
- self.set_bone_parent(ctrl.chest, ctrl.master)
-
- @stage.configure_bones
- def configure_control_chain(self):
- pass
+ pivot = self.get_master_control_output()
+ self.set_bone_parent(ctrl.hips, pivot)
+ self.set_bone_parent(ctrl.chest, pivot)
@stage.generate_widgets
- def make_control_widgets(self):
+ def make_end_control_widgets(self):
ctrl = self.bones.ctrl
mch = self.bones.mch
- self.make_control_widget(ctrl.hips, mch.wgt_hips)
- self.make_control_widget(ctrl.chest, mch.wgt_chest)
+ self.make_end_control_widget(ctrl.hips, mch.wgt_hips)
+ self.make_end_control_widget(ctrl.chest, mch.wgt_chest)
+
+ def make_end_control_widget(self, ctrl, wgt_mch):
+ shape_bone = self.get_bone(wgt_mch)
+ is_horizontal = abs(shape_bone.z_axis.normalized().y) < 0.7
- def make_control_widget(self, ctrl, wgt_mch):
- self.get_bone(ctrl).custom_shape_transform = self.get_bone(wgt_mch)
+ set_bone_widget_transform(self.obj, ctrl, wgt_mch)
- create_circle_widget(
+ obj = create_circle_widget(
self.obj, ctrl,
- radius=1.0,
+ radius=1.2 if is_horizontal else 1.1,
head_tail=0.0,
head_tail_x=1.0,
with_line=False,
)
+ if is_horizontal:
+ # Tilt the widget toward the ground for horizontal (animal) spines
+ angle = math.copysign(28, shape_bone.x_axis.x)
+ rotmat = Matrix.Rotation(math.radians(angle), 4, 'X')
+ adjust_widget_transform_mesh(obj, rotmat, local=True)
+
+ ####################################################
+ # FK control bones
+
+ @stage.generate_bones
+ def make_control_chain(self):
+ if self.use_fk:
+ orgs = self.bones.org
+ self.bones.ctrl.fk = self.fk_result = BoneDict(
+ hips = map_list(self.make_control_bone, count(0), orgs[0:self.pivot_pos], repeat(True)),
+ chest = map_list(self.make_control_bone, count(self.pivot_pos), orgs[self.pivot_pos:], repeat(False)),
+ )
+
+ def make_control_bone(self, i, org, is_hip):
+ name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_fk'), parent=False)
+ if is_hip:
+ put_bone(self.obj, name, self.get_bone(name).tail)
+ return name
+
+ @stage.parent_bones
+ def parent_control_chain(self):
+ if self.use_fk:
+ chain = self.bones.mch.chain
+ fk = self.bones.ctrl.fk
+ for child, parent in zip(fk.hips, chain.hips):
+ self.set_bone_parent(child, parent)
+ for child, parent in zip(fk.chest, chain.chest):
+ self.set_bone_parent(child, parent)
+
+ @stage.configure_bones
+ def configure_control_chain(self):
+ if self.use_fk:
+ fk = self.bones.ctrl.fk
+ for args in zip(count(0), fk.hips + fk.chest, self.bones.org):
+ self.configure_control_bone(*args)
+
+ ControlLayersOption.FK.assign_rig(self, fk.hips + fk.chest)
+
+ @stage.generate_widgets
+ def make_control_widgets(self):
+ if self.use_fk:
+ fk = self.bones.ctrl.fk
+ for ctrl in fk.hips:
+ self.make_control_widget(ctrl, True)
+ for ctrl in fk.chest:
+ self.make_control_widget(ctrl, False)
+
+ def make_control_widget(self, ctrl, is_hip):
+ obj = create_circle_widget(self.obj, ctrl, radius=1.0, head_tail=0.5)
+ if is_hip:
+ adjust_widget_transform_mesh(obj, Matrix.Diagonal((1, -1, 1, 1)), local=True)
+
####################################################
# MCH bones associated with main controls
@@ -153,16 +213,16 @@ class Rig(BaseSpineRig):
@stage.parent_bones
def parent_mch_control_bones(self):
mch = self.bones.mch
- self.set_bone_parent(mch.pivot, mch.chain.chest[0])
- self.set_bone_parent(mch.wgt_hips, mch.chain.hips[0])
- self.set_bone_parent(mch.wgt_chest, mch.chain.chest[-1])
+ fk = self.fk_result
+ self.set_bone_parent(mch.pivot, fk.chest[0])
+ self.set_bone_parent(mch.wgt_hips, fk.hips[0])
+ self.set_bone_parent(mch.wgt_chest, fk.chest[-1])
+ align_bone_orientation(self.obj, mch.pivot, fk.hips[-1])
@stage.rig_bones
def rig_mch_control_bones(self):
mch = self.bones.mch
- # Is it actually intending to compute average of these, or is this really intentional?
- # This effectively adds rotations of the hip and chest controls.
- self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', mch.chain.hips[-1], space='LOCAL')
+ self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', self.fk_result.hips[-1], influence=0.5)
####################################################
# MCH chain for distributing hip & chest transform
@@ -174,6 +234,8 @@ class Rig(BaseSpineRig):
hips = map_list(self.make_mch_bone, orgs[0:self.pivot_pos], repeat(True)),
chest = map_list(self.make_mch_bone, orgs[self.pivot_pos:], repeat(False)),
)
+ if not self.use_fk:
+ self.fk_result = self.bones.mch.chain
def make_mch_bone(self, org, is_hip):
name = self.copy_bone(org, make_mechanism_name(strip_org(org)), parent=False)
@@ -182,10 +244,13 @@ class Rig(BaseSpineRig):
@stage.parent_bones
def parent_mch_chain(self):
- master = self.bones.ctrl.master
+ master = self.get_master_control_output()
chain = self.bones.mch.chain
- self.parent_bone_chain([master, *reversed(chain.hips)])
- self.parent_bone_chain([master, *chain.chest])
+ fk = self.fk_result
+ for child, parent in zip(reversed(chain.hips), [master, *reversed(fk.hips)]):
+ self.set_bone_parent(child, parent)
+ for child, parent in zip(chain.chest, [master, *fk.chest]):
+ self.set_bone_parent(child, parent)
@stage.rig_bones
def rig_mch_chain(self):
@@ -205,7 +270,7 @@ class Rig(BaseSpineRig):
@stage.parent_bones
def parent_tweak_chain(self):
mch = self.bones.mch
- chain = mch.chain
+ chain = self.fk_result
parents = [chain.hips[0], *chain.hips[0:-1], mch.pivot, *chain.chest[1:], chain.chest[-1]]
for args in zip(self.bones.ctrl.tweak, parents):
self.set_bone_parent(*args)
@@ -224,6 +289,13 @@ class Rig(BaseSpineRig):
super().add_parameters(params)
+ params.make_fk_controls = bpy.props.BoolProperty(
+ name="FK Controls", default=True,
+ description="Generate an FK control chain"
+ )
+
+ ControlLayersOption.FK.add_parameters(params)
+
@classmethod
def parameters_ui(self, layout, params):
r = layout.row()
@@ -231,6 +303,11 @@ class Rig(BaseSpineRig):
super().parameters_ui(layout, params)
+ layout.prop(params, 'make_fk_controls')
+
+ if params.make_fk_controls:
+ ControlLayersOption.FK.parameters_ui(layout, params)
+
def create_sample(obj):
# generated by rigify.utils.write_metarig
diff --git a/rigify/rigs/spines/basic_tail.py b/rigify/rigs/spines/basic_tail.py
index 845c3ca3..be054e7d 100644
--- a/rigify/rigs/spines/basic_tail.py
+++ b/rigify/rigs/spines/basic_tail.py
@@ -23,7 +23,8 @@ import bpy
from itertools import count
from ...utils.naming import strip_org, make_derived_name
-from ...utils.bones import put_bone, flip_bone, is_same_position, connect_bbone_chain_handles, align_bone_orientation
+from ...utils.bones import put_bone, flip_bone, is_same_position, connect_bbone_chain_handles
+from ...utils.bones import align_bone_orientation, set_bone_widget_transform
from ...utils.widgets_basic import create_circle_widget
from ...utils.layers import ControlLayersOption
from ...utils.misc import map_list
@@ -69,7 +70,7 @@ class Rig(BaseHeadTailRig):
@stage.generate_widgets
def make_master_control_widget(self):
bone = self.bones.ctrl.master
- self.get_bone(bone).custom_shape_transform = self.get_bone(self.bones.ctrl.tweak[-1])
+ set_bone_widget_transform(self.obj, bone, self.bones.ctrl.tweak[-1])
create_ballsocket_widget(self.obj, bone, size=0.7)
####################################################
@@ -94,7 +95,7 @@ class Rig(BaseHeadTailRig):
)
# Widgets
- def make_control_widget(self, ctrl):
+ def make_control_widget(self, i, ctrl):
create_circle_widget(self.obj, ctrl, radius=0.5, head_tail=0.75)
####################################################
diff --git a/rigify/rigs/spines/spine_rigs.py b/rigify/rigs/spines/spine_rigs.py
index 6628289f..070a6bd3 100644
--- a/rigify/rigs/spines/spine_rigs.py
+++ b/rigify/rigs/spines/spine_rigs.py
@@ -24,9 +24,10 @@ from itertools import count
from ...utils.layers import ControlLayersOption
from ...utils.naming import make_derived_name
-from ...utils.bones import align_bone_orientation, align_bone_to_axis
+from ...utils.bones import align_bone_orientation, align_bone_to_axis, put_bone, set_bone_widget_transform
from ...utils.widgets_basic import create_cube_widget
from ...utils.switch_parent import SwitchParentBuilder
+from ...utils.components import CustomPivotControl
from ...base_rig import stage
@@ -44,6 +45,7 @@ class BaseSpineRig(TweakChainRig):
if len(self.bones.org) < 3:
self.raise_error("Input to rig type must be a chain of 3 or more bones.")
+ self.use_torso_pivot = self.params.make_custom_pivot
self.length = sum([self.get_bone(b).length for b in self.bones.org])
####################################################
@@ -52,6 +54,11 @@ class BaseSpineRig(TweakChainRig):
# ctrl:
# master
# Main control.
+ # master_pivot
+ # Custom pivot under the master control.
+ # mch:
+ # master_pivot
+ # Final output of the custom pivot.
#
####################################################
@@ -60,17 +67,43 @@ class BaseSpineRig(TweakChainRig):
@stage.generate_bones
def make_master_control(self):
- self.bones.ctrl.master = name = self.copy_bone(self.bones.org[0], 'torso')
+ self.bones.ctrl.master = name = self.make_master_control_bone(self.bones.org)
+ self.component_torso_pivot = self.build_master_pivot(name)
+ self.build_parent_switch(name)
+
+ def get_master_control_pos(self, orgs):
+ return self.get_bone(orgs[0]).head
+
+ def make_master_control_bone(self, orgs):
+ name = self.copy_bone(orgs[0], 'torso')
+ put_bone(self.obj, name, self.get_master_control_pos(orgs))
align_bone_to_axis(self.obj, name, 'y', length=self.length * 0.6)
+ return name
- self.build_parent_switch(name)
+ def build_master_pivot(self, master_name, **args):
+ if self.use_torso_pivot:
+ return CustomPivotControl(
+ self, 'master_pivot', master_name, parent=master_name, **args
+ )
+
+ def get_master_control_output(self):
+ if self.component_torso_pivot:
+ return self.component_torso_pivot.output
+ else:
+ return self.bones.ctrl.master
def build_parent_switch(self, master_name):
pbuilder = SwitchParentBuilder(self.generator)
- pbuilder.register_parent(self, master_name, name='Torso')
+
+ org_parent = self.get_bone_parent(self.bones.org[0])
+ parents = [org_parent] if org_parent else []
+
+ pbuilder.register_parent(self, self.get_master_control_output, name='Torso', tags={'torso', 'child'})
+
pbuilder.build_child(
self, master_name, exclude_self=True,
+ extra_parents=parents, select_parent=org_parent,
prop_id='torso_parent', prop_name='Torso Parent',
controls=lambda: self.bones.flatten('ctrl'),
)
@@ -78,8 +111,8 @@ class BaseSpineRig(TweakChainRig):
self.register_parent_bones(pbuilder)
def register_parent_bones(self, pbuilder):
- pbuilder.register_parent(self, self.bones.org[0], name='Hips', exclude_self=True)
- pbuilder.register_parent(self, self.bones.org[-1], name='Chest', exclude_self=True)
+ pbuilder.register_parent(self, self.bones.org[0], name='Hips', exclude_self=True, tags={'hips'})
+ pbuilder.register_parent(self, self.bones.org[-1], name='Chest', exclude_self=True, tags={'chest'})
@stage.parent_bones
def parent_master_control(self):
@@ -91,10 +124,9 @@ class BaseSpineRig(TweakChainRig):
@stage.generate_widgets
def make_master_control_widget(self):
- create_cube_widget(
- self.obj, self.bones.ctrl.master,
- radius=0.5,
- )
+ master = self.bones.ctrl.master
+ set_bone_widget_transform(self.obj, master, self.get_master_control_output())
+ create_cube_widget(self.obj, master, radius=0.5)
####################################################
# Tweak bones
@@ -117,11 +149,19 @@ class BaseSpineRig(TweakChainRig):
@classmethod
def add_parameters(self, params):
+ params.make_custom_pivot = bpy.props.BoolProperty(
+ name = "Custom Pivot Control",
+ default = False,
+ description = "Create a rotation pivot control that can be repositioned arbitrarily"
+ )
+
# Setting up extra layers for the FK and tweak
ControlLayersOption.TWEAK.add_parameters(params)
@classmethod
def parameters_ui(self, layout, params):
+ layout.prop(params, 'make_custom_pivot')
+
ControlLayersOption.TWEAK.parameters_ui(layout, params)
diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py
index 9b85e5b5..15f011f7 100644
--- a/rigify/rigs/spines/super_head.py
+++ b/rigify/rigs/spines/super_head.py
@@ -322,7 +322,10 @@ class Rig(BaseHeadTailRig):
def register_parent_bones(self):
rig = self.rigify_parent or self
builder = SwitchParentBuilder(self.generator)
- builder.register_parent(rig, self.bones.org[-1], name='Head', exclude_self=True)
+ builder.register_parent(
+ self, self.bones.org[-1], name='Head',
+ inject_into=rig, exclude_self=True, tags={'head'},
+ )
@stage.configure_bones
def configure_bbone_chain(self):
diff --git a/rigify/rigs/spines/super_spine.py b/rigify/rigs/spines/super_spine.py
index 5ed1588e..86021c84 100644
--- a/rigify/rigs/spines/super_spine.py
+++ b/rigify/rigs/spines/super_spine.py
@@ -81,7 +81,7 @@ class Rig(SubstitutionRig, BoneUtilityMixin):
bpy.ops.object.mode_set(mode='OBJECT')
# Create the parts
- self.assign_params(spine_orgs[0], params_copy, pivot_pos=pivot_pos)
+ self.assign_params(spine_orgs[0], params_copy, pivot_pos=pivot_pos, make_fk_controls=False)
result = [ self.instantiate_rig(basic_spine.Rig, spine_orgs[0]) ]
@@ -133,6 +133,8 @@ def add_parameters(params):
def parameters_ui(layout, params):
""" Create the ui for the rig parameters."""
+ layout.label(text="Note: this combined rig is deprecated.", icon='INFO')
+
r = layout.row(align=True)
r.prop(params, "use_head", toggle=True, text="Head")
r.prop(params, "use_tail", toggle=True, text="Tail")
diff --git a/rigify/ui.py b/rigify/ui.py
index caac09ab..005aed80 100644
--- a/rigify/ui.py
+++ b/rigify/ui.py
@@ -77,6 +77,7 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
id_store = C.window_manager
if obj.mode in {'POSE', 'OBJECT'}:
+ armature_id_store = C.object.data
WARNING = "Warning: Some features may change after generation"
show_warning = False
@@ -127,7 +128,7 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
row.enabled = enable_generate_and_advanced
- if id_store.rigify_advanced_generation:
+ if armature_id_store.rigify_advanced_generation:
icon = 'UNLOCKED'
else:
icon = 'LOCKED'
@@ -135,12 +136,12 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
col = layout.column()
col.enabled = enable_generate_and_advanced
row = col.row()
- row.prop(id_store, "rigify_advanced_generation", toggle=True, icon=icon)
+ row.prop(armature_id_store, "rigify_advanced_generation", toggle=True, icon=icon)
- if id_store.rigify_advanced_generation:
+ if armature_id_store.rigify_advanced_generation:
row = col.row(align=True)
- row.prop(id_store, "rigify_generate_mode", expand=True)
+ row.prop(armature_id_store, "rigify_generate_mode", expand=True)
main_row = col.row(align=True).split(factor=0.3)
col1 = main_row.column()
@@ -148,41 +149,25 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
col1.label(text="Rig Name")
row = col1.row()
row.label(text="Target Rig")
- row.enabled = (id_store.rigify_generate_mode == "overwrite")
+ row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col1.row()
row.label(text="Target UI")
- row.enabled = (id_store.rigify_generate_mode == "overwrite")
+ row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col2.row(align=True)
- row.prop(id_store, "rigify_rig_basename", text="", icon="SORTALPHA")
+ row.prop(armature_id_store, "rigify_rig_basename", text="", icon="SORTALPHA")
row = col2.row(align=True)
- for i in range(0, len(id_store.rigify_target_rigs)):
- id_store.rigify_target_rigs.remove(0)
-
- for ob in context.scene.objects:
- if type(ob.data) == bpy.types.Armature and "rig_id" in ob.data:
- id_store.rigify_target_rigs.add()
- id_store.rigify_target_rigs[-1].name = ob.name
-
- row.prop_search(id_store, "rigify_target_rig", id_store, "rigify_target_rigs", text="",
- icon='OUTLINER_OB_ARMATURE')
- row.enabled = (id_store.rigify_generate_mode == "overwrite")
-
- for i in range(0, len(id_store.rigify_rig_uis)):
- id_store.rigify_rig_uis.remove(0)
-
- for t in bpy.data.texts:
- id_store.rigify_rig_uis.add()
- id_store.rigify_rig_uis[-1].name = t.name
+ row.prop(armature_id_store, "rigify_target_rig", text="")
+ row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col2.row()
- row.prop_search(id_store, "rigify_rig_ui", id_store, "rigify_rig_uis", text="", icon='TEXT')
- row.enabled = (id_store.rigify_generate_mode == "overwrite")
+ row.prop(armature_id_store, "rigify_rig_ui", text="", icon='TEXT')
+ row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col.row()
- row.prop(id_store, "rigify_force_widget_update")
- if id_store.rigify_generate_mode == 'new':
+ row.prop(armature_id_store, "rigify_force_widget_update")
+ if armature_id_store.rigify_generate_mode == 'new':
row.enabled = False
elif obj.mode == 'EDIT':
diff --git a/rigify/utils/animation.py b/rigify/utils/animation.py
index 62042923..1355a0b6 100644
--- a/rigify/utils/animation.py
+++ b/rigify/utils/animation.py
@@ -377,20 +377,24 @@ TRANSFORM_PROPS_ROTATION = frozenset(['rotation_euler', 'rotation_quaternion', '
TRANSFORM_PROPS_SCALE = frozenset(['scale'])
TRANSFORM_PROPS_ALL = frozenset(TRANSFORM_PROPS_LOCATION | TRANSFORM_PROPS_ROTATION | TRANSFORM_PROPS_SCALE)
-class ActionCurveTable(object):
+def transform_props_with_locks(lock_location, lock_rotation, lock_scale):
+ props = set()
+ if not lock_location:
+ props |= TRANSFORM_PROPS_LOCATION
+ if not lock_rotation:
+ props |= TRANSFORM_PROPS_ROTATION
+ if not lock_scale:
+ props |= TRANSFORM_PROPS_SCALE
+ return props
+
+class FCurveTable(object):
"Table for efficient lookup of FCurves by properties."
- def __init__(self, action):
- from collections import defaultdict
- self.action = find_action(action)
- self.curve_map = defaultdict(dict)
- self.index_action()
+ def __init__(self):
+ self.curve_map = collections.defaultdict(dict)
- def index_action(self):
- if not self.action:
- return
-
- for curve in self.action.fcurves:
+ def index_curves(self, curves):
+ for curve in curves:
index = curve.array_index
if index < 0:
index = 0
@@ -412,6 +416,24 @@ class ActionCurveTable(object):
def get_custom_prop_curves(self, ptr, prop):
return self.get_prop_curves(ptr, rna_idprop_quote_path(prop))
+
+class ActionCurveTable(FCurveTable):
+ "Table for efficient lookup of Action FCurves by properties."
+
+ def __init__(self, action):
+ super().__init__()
+ self.action = find_action(action)
+ if self.action:
+ self.index_curves(self.action.fcurves)
+
+class DriverCurveTable(FCurveTable):
+ "Table for efficient lookup of Driver FCurves by properties."
+
+ def __init__(self, object):
+ super().__init__()
+ self.anim_data = object.animation_data
+ if self.anim_data:
+ self.index_curves(self.anim_data.drivers)
''']
exec(SCRIPT_UTILITIES_CURVES[-1])
@@ -495,7 +517,23 @@ SCRIPT_UTILITIES_BAKE = SCRIPT_UTILITIES_KEYING + SCRIPT_UTILITIES_CURVES + ['''
# Keyframe baking operator framework ##
#######################################
-class RigifyBakeKeyframesMixin:
+class RigifyOperatorMixinBase:
+ bl_options = {'UNDO', 'INTERNAL'}
+
+ def init_invoke(self, context):
+ "Override to initialize the operator before invoke."
+
+ def init_execute(self, context):
+ "Override to initialize the operator before execute."
+
+ def before_save_state(self, context, rig):
+ "Override to prepare for saving state."
+
+ def after_save_state(self, context, rig):
+ "Override to undo before_save_state."
+
+
+class RigifyBakeKeyframesMixin(RigifyOperatorMixinBase):
"""Basic framework for an operator that updates a set of keyed frames."""
# Utilities
@@ -566,6 +604,7 @@ class RigifyBakeKeyframesMixin:
self.bake_state = dict()
self.keyflags = get_keying_flags(context)
+ self.keyflags_switch = None
if context.window_manager.rigify_transfer_use_all_keys:
self.bake_add_curve_frames(self.bake_curve_table.curve_map)
@@ -604,9 +643,15 @@ class RigifyBakeKeyframesMixin:
scene = context.scene
saved_state = self.bake_state
- for frame in self.bake_frames:
- scene.frame_set(frame)
- saved_state[frame] = self.save_frame_state(context, rig)
+ try:
+ self.before_save_state(context, rig)
+
+ for frame in self.bake_frames:
+ scene.frame_set(frame)
+ saved_state[frame] = self.save_frame_state(context, rig)
+
+ finally:
+ self.after_save_state(context, rig)
def bake_clean_curves_in_range(self, context, curves):
"Deletes all keys from the given curves in the bake range."
@@ -648,10 +693,6 @@ class RigifyBakeKeyframesMixin:
"Override to execute code one time before the bake apply frame scan."
pass
- def init_execute(self, context):
- "Override to initialize the operator."
- pass
-
def execute(self, context):
self.init_execute(context)
self.bake_init(context)
@@ -661,18 +702,20 @@ class RigifyBakeKeyframesMixin:
if self.report_bake_empty():
return {'CANCELLED'}
- self.bake_save_state(context)
+ try:
+ self.bake_save_state(context)
- range, range_raw = self.bake_clean_curves_in_range(context, curves)
+ range, range_raw = self.bake_clean_curves_in_range(context, curves)
- self.execute_before_apply(context, self.bake_rig, range, range_raw)
+ self.execute_before_apply(context, self.bake_rig, range, range_raw)
- self.bake_apply_state(context)
- return {'FINISHED'}
+ self.bake_apply_state(context)
- def init_invoke(self, context):
- "Override to initialize the operator."
- pass
+ except Exception as e:
+ traceback.print_exc()
+ self.report({'ERROR'}, 'Exception: ' + str(e))
+
+ return {'FINISHED'}
def invoke(self, context, event):
self.init_invoke(context)
@@ -683,22 +726,29 @@ class RigifyBakeKeyframesMixin:
return context.window_manager.invoke_confirm(self, event)
-class RigifySingleUpdateMixin:
+class RigifySingleUpdateMixin(RigifyOperatorMixinBase):
"""Basic framework for an operator that updates only the current frame."""
- def init_execute(self, context):
- pass
-
def execute(self, context):
self.init_execute(context)
obj = context.active_object
self.keyflags = get_autokey_flags(context, ignore_keyset=True)
self.keyflags_switch = add_flags_if_set(self.keyflags, {'INSERTKEY_AVAILABLE'})
- self.apply_frame_state(context, obj, self.save_frame_state(context, obj))
- return {'FINISHED'}
- def init_invoke(self, context):
- pass
+ try:
+ try:
+ self.before_save_state(context, obj)
+ state = self.save_frame_state(context, obj)
+ finally:
+ self.after_save_state(context, obj)
+
+ self.apply_frame_state(context, obj, state)
+
+ except Exception as e:
+ traceback.print_exc()
+ self.report({'ERROR'}, 'Exception: ' + str(e))
+
+ return {'FINISHED'}
def invoke(self, context, event):
self.init_invoke(context)
@@ -773,51 +823,59 @@ def add_clear_keyframes_button(panel, *, bones=[], label='', text=''):
# Generic Snap FK to IK operator ##
###################################
-SCRIPT_REGISTER_OP_SNAP_FK_IK = ['POSE_OT_rigify_generic_fk2ik', 'POSE_OT_rigify_generic_fk2ik_bake']
+SCRIPT_REGISTER_OP_SNAP = ['POSE_OT_rigify_generic_snap', 'POSE_OT_rigify_generic_snap_bake']
-SCRIPT_UTILITIES_OP_SNAP_FK_IK = ['''
-###########################
-## Generic Snap FK to IK ##
-###########################
+SCRIPT_UTILITIES_OP_SNAP = ['''
+#############################
+## Generic Snap (FK to IK) ##
+#############################
-class RigifyGenericFk2IkBase:
- fk_bones: StringProperty(name="FK Bone Chain")
- ik_bones: StringProperty(name="IK Result Bone Chain")
- ctrl_bones: StringProperty(name="IK Controls")
+class RigifyGenericSnapBase:
+ input_bones: StringProperty(name="Input Chain")
+ output_bones: StringProperty(name="Output Chain")
+ ctrl_bones: StringProperty(name="Input Controls")
+ tooltip: StringProperty(name="Tooltip", default="FK to IK")
+ locks: bpy.props.BoolVectorProperty(name="Locked", size=3, default=[False,False,False])
undo_copy_scale: bpy.props.BoolProperty(name="Undo Copy Scale", default=False)
- keyflags = None
-
def init_execute(self, context):
- self.fk_bone_list = json.loads(self.fk_bones)
- self.ik_bone_list = json.loads(self.ik_bones)
+ self.input_bone_list = json.loads(self.input_bones)
+ self.output_bone_list = json.loads(self.output_bones)
self.ctrl_bone_list = json.loads(self.ctrl_bones)
def save_frame_state(self, context, obj):
- return get_chain_transform_matrices(obj, self.ik_bone_list)
+ return get_chain_transform_matrices(obj, self.input_bone_list)
def apply_frame_state(self, context, obj, matrices):
set_chain_transforms_from_matrices(
- context, obj, self.fk_bone_list, matrices,
- undo_copy_scale=self.undo_copy_scale, keyflags=self.keyflags
+ context, obj, self.output_bone_list, matrices,
+ undo_copy_scale=self.undo_copy_scale, keyflags=self.keyflags,
+ no_loc=self.locks[0], no_rot=self.locks[1], no_scale=self.locks[2],
)
-class POSE_OT_rigify_generic_fk2ik(RigifyGenericFk2IkBase, RigifySingleUpdateMixin, bpy.types.Operator):
- bl_idname = "pose.rigify_generic_fk2ik_" + rig_id
- bl_label = "Snap FK->IK"
- bl_options = {'UNDO', 'INTERNAL'}
- bl_description = "Snap the FK chain to IK result"
+class POSE_OT_rigify_generic_snap(RigifyGenericSnapBase, RigifySingleUpdateMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_generic_snap_" + rig_id
+ bl_label = "Snap Bones"
+ bl_description = "Snap on the current frame"
-class POSE_OT_rigify_generic_fk2ik_bake(RigifyGenericFk2IkBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
- bl_idname = "pose.rigify_generic_fk2ik_bake_" + rig_id
- bl_label = "Apply Snap FK->IK To Keyframes"
- bl_options = {'UNDO', 'INTERNAL'}
- bl_description = "Snap the FK chain keyframes to IK result"
+ @classmethod
+ def description(cls, context, props):
+ return "Snap " + props.tooltip + " on the current frame"
+
+class POSE_OT_rigify_generic_snap_bake(RigifyGenericSnapBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_generic_snap_bake_" + rig_id
+ bl_label = "Apply Snap To Keyframes"
+ bl_description = "Apply snap to keyframes"
+
+ @classmethod
+ def description(cls, context, props):
+ return "Apply snap " + props.tooltip + " to keyframes"
def execute_scan_curves(self, context, obj):
+ props = transform_props_with_locks(*self.locks)
self.bake_add_bone_frames(self.ctrl_bone_list, TRANSFORM_PROPS_ALL)
- return self.bake_get_all_bone_curves(self.fk_bone_list, TRANSFORM_PROPS_ALL)
+ return self.bake_get_all_bone_curves(self.output_bone_list, props)
''']
def add_fk_ik_snap_buttons(panel, op_single, op_bake, *, label=None, rig_name='', properties=None, clear_bones=None, compact=None):
@@ -840,25 +898,37 @@ def add_fk_ik_snap_buttons(panel, op_single, op_bake, *, label=None, rig_name=''
row.operator(op_bake, text='Action', icon='ACTION_TWEAK', properties=properties)
add_clear_keyframes_button(row, bones=clear_bones, text='Clear')
-def add_generic_snap_fk_to_ik(panel, *, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], label='FK->IK', rig_name='', undo_copy_scale=False, compact=None, clear=True):
+def add_generic_snap(panel, *, output_bones=[], input_bones=[], input_ctrl_bones=[], label='Snap', rig_name='', undo_copy_scale=False, compact=None, clear=True, locks=None, tooltip=None):
panel.use_bake_settings()
- panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_FK_IK)
- panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_FK_IK)
+ panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP)
+ panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP)
op_props = {
- 'fk_bones': json.dumps(fk_bones),
- 'ik_bones': json.dumps(ik_bones),
- 'ctrl_bones': json.dumps(ik_ctrl_bones),
- 'undo_copy_scale': undo_copy_scale,
+ 'output_bones': json.dumps(output_bones),
+ 'input_bones': json.dumps(input_bones),
+ 'ctrl_bones': json.dumps(input_ctrl_bones or input_bones),
}
- clear_bones = fk_bones if clear else None
+ if undo_copy_scale:
+ op_props['undo_copy_scale'] = undo_copy_scale
+ if locks is not None:
+ op_props['locks'] = tuple(locks[0:3])
+ if tooltip is not None:
+ op_props['tooltip'] = tooltip
+
+ clear_bones = output_bones if clear else None
add_fk_ik_snap_buttons(
- panel, 'pose.rigify_generic_fk2ik_{rig_id}', 'pose.rigify_generic_fk2ik_bake_{rig_id}',
+ panel, 'pose.rigify_generic_snap_{rig_id}', 'pose.rigify_generic_snap_bake_{rig_id}',
label=label, rig_name=rig_name, properties=op_props, clear_bones=clear_bones, compact=compact,
)
+def add_generic_snap_fk_to_ik(panel, *, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], label='FK->IK', rig_name='', undo_copy_scale=False, compact=None, clear=True):
+ add_generic_snap(
+ panel, output_bones=fk_bones, input_bones=ik_bones, input_ctrl_bones=ik_ctrl_bones,
+ label=label, rig_name=rig_name, undo_copy_scale=undo_copy_scale, compact=compact, clear=clear
+ )
+
###############################
# Module register/unregister ##
###############################
diff --git a/rigify/utils/bones.py b/rigify/utils/bones.py
index 6a09cee1..854d4428 100644
--- a/rigify/utils/bones.py
+++ b/rigify/utils/bones.py
@@ -657,3 +657,17 @@ def align_bone_to_axis(obj, bone_name, axis, *, length=None, roll=0, flip=False)
bone_e.tail = bone_e.head + vec
bone_e.roll = roll
+
+
+def set_bone_widget_transform(obj, bone_name, transform_bone, use_size=True, scale=1.0):
+ assert obj.mode != 'EDIT'
+
+ bone = obj.pose.bones[bone_name]
+
+ if transform_bone and transform_bone != bone_name:
+ bone.custom_shape_transform = obj.pose.bones[transform_bone]
+ else:
+ bone.custom_shape_transform = None
+
+ bone.use_custom_shape_bone_size = use_size
+ bone.custom_shape_scale = scale
diff --git a/rigify/utils/components.py b/rigify/utils/components.py
new file mode 100644
index 00000000..5c1ebcb6
--- /dev/null
+++ b/rigify/utils/components.py
@@ -0,0 +1,87 @@
+import bpy
+
+from .naming import make_derived_name
+from .bones import put_bone, copy_bone_position, align_bone_orientation
+from .widgets_basic import create_pivot_widget
+from .misc import force_lazy
+
+from ..base_rig import RigComponent, stage
+
+
+class CustomPivotControl(RigComponent):
+ """
+ A utility that generates a pivot control with a custom position.
+
+ Generates a control bone, and a MCH output bone.
+ """
+
+ def __init__(
+ self, rig, id_name, org_bone, *,
+ name=None, parent=None, position=None, matrix=None,
+ scale=1.0, scale_mch=None,
+ move_to=None, align_to=None, snap_to=None,
+ widget_axis=1.5, widget_cap=1.0, widget_square=True,
+ ):
+ super().__init__(rig)
+
+ assert rig.generator.stage == 'generate_bones'
+
+ self.bones = rig.bones
+ self.id_name = id_name
+
+ self.parent = parent
+ self.scale = scale or 1
+ self.scale_mch = scale_mch or (self.scale * 0.7)
+ self.move_to = move_to
+ self.align_to = align_to
+ self.snap_to = snap_to
+ self.widget_axis = widget_axis
+ self.widget_cap = widget_cap
+ self.widget_square = widget_square
+
+ name = name or make_derived_name(org_bone, 'ctrl', '_pivot')
+
+ self.do_make_bones(org_bone, name, position, matrix)
+
+ @property
+ def control(self):
+ return self.ctrl
+
+ @property
+ def output(self):
+ return self.mch
+
+ def do_make_bones(self, org, name, position, matrix):
+ self.bones.ctrl[self.id_name] = self.ctrl = self.copy_bone(org, name, parent=not self.parent, scale=self.scale)
+ self.bones.mch[self.id_name] = self.mch = self.copy_bone(org, make_derived_name(name, 'mch'), scale=self.scale_mch)
+
+ if position or matrix:
+ put_bone(self.obj, self.ctrl, position, matrix=matrix)
+ put_bone(self.obj, self.mch, position, matrix=matrix)
+
+ def parent_bones(self):
+ if self.snap_to:
+ bone = force_lazy(self.snap_to)
+ copy_bone_position(self.obj, bone, self.ctrl, scale=self.scale)
+ copy_bone_position(self.obj, bone, self.mch, scale=self.scale_mch)
+
+ if self.move_to:
+ pos = self.get_bone(force_lazy(self.move_to)).head
+ put_bone(self.obj, self.ctrl, pos)
+ put_bone(self.obj, self.mch, pos)
+
+ if self.align_to:
+ self.align_to = force_lazy(self.align_to)
+ align_bone_orientation(self.obj, self.ctrl, self.align_to)
+ align_bone_orientation(self.obj, self.mch, self.align_to)
+
+ if self.parent:
+ self.set_bone_parent(self.ctrl, force_lazy(self.parent))
+
+ self.set_bone_parent(self.mch, self.ctrl)
+
+ def rig_bones(self):
+ self.make_constraint(self.mch, 'COPY_LOCATION', self.ctrl, space='LOCAL', invert_xyz=(True,)*3)
+
+ def generate_widgets(self):
+ create_pivot_widget(self.obj, self.ctrl, axis_size=self.widget_axis, cap_size=self.widget_cap, square=self.widget_square)
diff --git a/rigify/utils/layers.py b/rigify/utils/layers.py
index 7a1bcef8..0cbd41f8 100644
--- a/rigify/utils/layers.py
+++ b/rigify/utils/layers.py
@@ -71,7 +71,7 @@ class ControlLayersOption:
self.toggle_option = self.name+'_layers_extra'
self.layers_option = self.name+'_layers'
- self.toggle_name = toggle_name if toggle_name else self.toggle_option
+ self.toggle_name = toggle_name if toggle_name else "Assign " + self.name.title() + " Layers"
def get(self, params):
if getattr(params, self.toggle_option):
@@ -122,10 +122,15 @@ class ControlLayersOption:
setattr(params, self.layers_option, prop_layers)
def parameters_ui(self, layout, params):
- r = layout.row()
- r.prop(params, self.toggle_option)
- r.active = getattr(params, self.toggle_option)
+ box = layout.box()
+ box.prop(params, self.toggle_option)
+ active = getattr(params, self.toggle_option)
+
+ if not active:
+ return
+
+ r = box.row()
col = r.column(align=True)
row = col.row(align=True)
diff --git a/rigify/utils/misc.py b/rigify/utils/misc.py
index 64367bb7..20fd6a08 100644
--- a/rigify/utils/misc.py
+++ b/rigify/utils/misc.py
@@ -156,6 +156,14 @@ def map_apply(func, *inputs):
# Misc
#=============================================
+
+def force_lazy(value):
+ if callable(value):
+ return value()
+ else:
+ return value
+
+
def copy_attributes(a, b):
keys = dir(a)
for key in keys:
diff --git a/rigify/utils/rig.py b/rigify/utils/rig.py
index 8c646ab5..0c07cfe6 100644
--- a/rigify/utils/rig.py
+++ b/rigify/utils/rig.py
@@ -283,6 +283,7 @@ def write_metarig(obj, layers=False, func_name="create", groups=False):
code.append(" bone.select = True")
code.append(" bone.select_head = True")
code.append(" bone.select_tail = True")
+ code.append(" bone.bbone_x = bone.bbone_z = bone.length * 0.05")
code.append(" arm.edit_bones.active = bone")
# Set appropriate layers visible
diff --git a/rigify/utils/switch_parent.py b/rigify/utils/switch_parent.py
index c26f5e74..61721266 100644
--- a/rigify/utils/switch_parent.py
+++ b/rigify/utils/switch_parent.py
@@ -8,7 +8,7 @@ import json
from .errors import MetarigError
from .naming import strip_prefix, make_derived_name
from .mechanism import MechanismUtilityMixin
-from .misc import map_list, map_apply
+from .misc import map_list, map_apply, force_lazy
from ..base_rig import *
from ..base_generate import GeneratorPlugin
@@ -16,11 +16,6 @@ from ..base_generate import GeneratorPlugin
from collections import defaultdict
from itertools import count, repeat, chain
-def _auto_call(value):
- if callable(value):
- return value()
- else:
- return value
def _rig_is_child(rig, parent):
if parent is None:
@@ -56,7 +51,7 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
##############################
# API
- def register_parent(self, rig, bone, *, name=None, is_global=False, exclude_self=False):
+ def register_parent(self, rig, bone, *, name=None, is_global=False, exclude_self=False, inject_into=None, tags=None):
"""
Registers a bone of the specified rig as a possible parent.
@@ -66,6 +61,8 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
name Name of the parent for mouse-over hint.
is_global The parent is accessible to all rigs, instead of just children of owner.
exclude_self The parent is invisible to the owner rig itself.
+ inject_into Make this parent available to children of the specified rig.
+ tags Set of tags to use for default parent selection.
Lazy creation:
The bone parameter may be a function creating the bone on demand and
@@ -74,10 +71,19 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
assert not self.frozen
assert isinstance(bone, str) or callable(bone)
+ assert callable(bone) or _rig_is_child(rig, self.generator.bone_owners[bone])
+ assert _rig_is_child(rig, inject_into)
+
+ real_rig = rig
+
+ if inject_into and inject_into is not rig:
+ rig = inject_into
+ tags = (tags or set()) | {'injected'}
entry = {
- 'rig': rig, 'bone': bone, 'name': name,
- 'is_global': is_global, 'exclude_self': exclude_self, 'used': False,
+ 'rig': rig, 'bone': bone, 'name': name, 'tags': tags,
+ 'is_global': is_global, 'exclude_self': exclude_self,
+ 'real_rig': real_rig, 'used': False,
}
if is_global:
@@ -96,9 +102,13 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
extra_parents List of bone names or (name, user_name) pairs to use as additional parents.
use_parent_mch Create an intermediate MCH bone for the constraints and parent the child to it.
select_parent Select the specified bone instead of the last one.
+ select_tags List of parent tags to try for default selection.
ignore_global Ignore the is_global flag of potential parents.
exclude_self Ignore parents registered by the rig itself.
- context_rig Rig to use for selecting parents.
+ allow_self Ignore the 'exclude_self' setting of the parent.
+ context_rig Rig to use for selecting parents; defaults to rig.
+ no_implicit Only use parents listed as extra_parents.
+ only_selected Like no_implicit, but allow the 'default' selected parent.
prop_bone Name of the bone to add the property to.
prop_id Actual name of the control property.
@@ -160,7 +170,10 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
child_option_table = {
'extra_parents': None,
'prop_bone': None, 'prop_id': None, 'prop_name': None, 'controls': None,
- 'select_parent': None, 'ignore_global': False, 'exclude_self': False, 'context_rig': None,
+ 'select_parent': None, 'ignore_global': False,
+ 'exclude_self': False, 'allow_self': False,
+ 'context_rig': None, 'select_tags': None,
+ 'no_implicit': False, 'only_selected': False,
'ctrl_bone': None,
'no_fix_location': False, 'no_fix_rotation': False, 'no_fix_scale': False,
'copy_location': None, 'copy_rotation': None, 'copy_scale': None,
@@ -199,16 +212,23 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
parents = []
for parent in self.get_rig_parent_candidates(child_rig):
+ parent_rig = parent['rig']
+
+ # Exclude injected parents
+ if parent['real_rig'] is not parent_rig:
+ if _rig_is_child(parent_rig, child_rig):
+ continue
+
if parent['rig'] is child_rig:
- if parent['exclude_self'] or child['exclude_self']:
+ if (parent['exclude_self'] and not child['allow_self']) or child['exclude_self']:
continue
elif parent['is_global'] and not child['ignore_global']:
# Can't use parents from own children, even if global (cycle risk)
- if _rig_is_child(parent['rig'], child_rig):
+ if _rig_is_child(parent_rig, child_rig):
continue
else:
# Required to be a child of the parent's rig
- if not _rig_is_child(child_rig, parent['rig']):
+ if not _rig_is_child(child_rig, parent_rig):
continue
parent['used'] = True
@@ -219,7 +239,7 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
# Call lazy creation for parents
for parent in self.parent_list:
if parent['used']:
- parent['bone'] = _auto_call(parent['bone'])
+ parent['bone'] = force_lazy(parent['bone'])
def parent_bones(self):
for child in self.child_list:
@@ -248,34 +268,68 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
# Build the final list of parent bone names
parent_map = dict()
+ parent_tags = defaultdict(set)
for parent in child['parents']:
if parent['bone'] not in parent_map:
parent_map[parent['bone']] = parent['name']
+ if parent['tags']:
+ parent_tags[parent['bone']] |= parent['tags']
last_main_parent_bone = child['parents'][-1]['bone']
- num_main_parents = len(parent_map.items())
+ extra_parents = set()
- for parent in _auto_call(child['extra_parents'] or []):
+ for parent in force_lazy(child['extra_parents'] or []):
if not isinstance(parent, tuple):
parent = (parent, None)
+ extra_parents.add(parent[0])
if parent[0] not in parent_map:
parent_map[parent[0]] = parent[1]
+ for parent in parent_map:
+ if parent in self.child_map:
+ parent_tags[parent] |= {'child'}
+
parent_bones = list(parent_map.items())
- child['parent_bones'] = parent_bones
# Find which bone to select
- select_bone = _auto_call(child['select_parent']) or last_main_parent_bone
- select_index = num_main_parents
+ select_bone = force_lazy(child['select_parent']) or last_main_parent_bone
+ select_tags = force_lazy(child['select_tags']) or []
+
+ if child['no_implicit']:
+ assert len(extra_parents) > 0
+ parent_bones = [ item for item in parent_bones if item[0] in extra_parents ]
+ if last_main_parent_bone not in extra_parents:
+ last_main_parent_bone = parent_bones[-1][0]
+
+ for tag in select_tags:
+ tag_set = tag if isinstance(tag, set) else {tag}
+ matching = [
+ bone for (bone, _) in parent_bones
+ if not tag_set.isdisjoint(parent_tags[bone])
+ ]
+ if len(matching) > 0:
+ select_bone = matching[-1]
+ break
+
+ if select_bone not in parent_map:
+ print("RIGIFY ERROR: Can't find bone '%s' to select as default parent of '%s'\n" % (select_bone, bone))
+ select_bone = last_main_parent_bone
+
+ if child['only_selected']:
+ filter_set = { select_bone, *extra_parents }
+ parent_bones = [ item for item in parent_bones if item[0] in filter_set ]
try:
select_index = 1 + next(i for i, (bone, _) in enumerate(parent_bones) if bone == select_bone)
except StopIteration:
- print("RIGIFY ERROR: Can't find bone '%s' to select as default parent of '%s'\n" % (select_bone, bone))
+ select_index = len(parent_bones)
+ print("RIGIFY ERROR: Invalid default parent '%s' of '%s'\n" % (select_bone, bone))
+
+ child['parent_bones'] = parent_bones
# Create the controlling property
- prop_bone = child['prop_bone'] = _auto_call(child['prop_bone']) or bone
+ prop_bone = child['prop_bone'] = force_lazy(child['prop_bone']) or bone
prop_name = child['prop_name'] or child['prop_id'] or 'Parent Switch'
prop_id = child['prop_id'] = child['prop_id'] or 'parent_switch'
@@ -294,12 +348,12 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
no_fix = [ child[n] for n in ['no_fix_location', 'no_fix_rotation', 'no_fix_scale'] ]
- child['copy'] = [ _auto_call(child[n]) for n in ['copy_location', 'copy_rotation', 'copy_scale'] ]
+ child['copy'] = [ force_lazy(child[n]) for n in ['copy_location', 'copy_rotation', 'copy_scale'] ]
locks = tuple(bool(nofix or copy) for nofix, copy in zip(no_fix, child['copy']))
# Create the script for the property
- controls = _auto_call(child['controls']) or set([prop_bone, bone])
+ controls = force_lazy(child['controls']) or set([prop_bone, bone])
script = self.generator.script
panel = script.panel_with_selected_check(child['rig'], controls)
@@ -377,9 +431,6 @@ class RigifySwitchParentBase:
items=lambda s,c: RigifySwitchParentBase.parent_items
)
- keyflags = None
- keyflags_switch = None
-
def save_frame_state(self, context, obj):
return get_transform_matrix(obj, self.bone, with_constraints=False)
@@ -398,16 +449,6 @@ class RigifySwitchParentBase:
no_loc=self.locks[0], no_rot=self.locks[1], no_scale=self.locks[2]
)
- def get_bone_props(self):
- props = set()
- if not self.locks[0]:
- props |= TRANSFORM_PROPS_LOCATION
- if not self.locks[1]:
- props |= TRANSFORM_PROPS_ROTATION
- if not self.locks[2]:
- props |= TRANSFORM_PROPS_SCALE
- return props
-
def init_invoke(self, context):
pose = context.active_object.pose
@@ -440,11 +481,10 @@ class POSE_OT_rigify_switch_parent(RigifySwitchParentBase, RigifySingleUpdateMix
class POSE_OT_rigify_switch_parent_bake(RigifySwitchParentBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
bl_idname = "pose.rigify_switch_parent_bake_" + rig_id
bl_label = "Apply Switch Parent To Keyframes"
- bl_options = {'UNDO', 'INTERNAL'}
bl_description = "Switch parent over a frame range, adjusting keys to preserve the bone position and orientation"
def execute_scan_curves(self, context, obj):
- return self.bake_add_bone_frames(self.bone, self.get_bone_props())
+ return self.bake_add_bone_frames(self.bone, transform_props_with_locks(*self.locks))
def execute_before_apply(self, context, obj, range, range_raw):
self.bake_replace_custom_prop_keys_constant(self.prop_bone, self.prop_id, int(self.selected))
diff --git a/rigify/utils/widgets_basic.py b/rigify/utils/widgets_basic.py
index 2848e5bf..8aab5d7b 100644
--- a/rigify/utils/widgets_basic.py
+++ b/rigify/utils/widgets_basic.py
@@ -129,7 +129,7 @@ def create_bone_widget(rig, bone_name, r1=0.1, l1=0.0, r2=0.04, l2=1.0, bone_tra
mesh.update()
-def create_pivot_widget(rig, bone_name, axis_size=1.0, cap_size=1.0, square=False, bone_transform_name=None):
+def create_pivot_widget(rig, bone_name, axis_size=1.0, cap_size=1.0, square=True, bone_transform_name=None):
"""Creates a widget similar to Plain Axes empty, but with a cross or
a square on the end of each axis line.
"""
diff --git a/space_view3d_3d_navigation.py b/space_view3d_3d_navigation.py
index 1e1cec9d..34074ad0 100644
--- a/space_view3d_3d_navigation.py
+++ b/space_view3d_3d_navigation.py
@@ -30,8 +30,8 @@ bl_info = {
"location": "View3D > Sidebar > View Tab",
"description": "Navigate the Camera & 3D View from the Toolshelf",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/3D_interaction/3D_Navigation",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "3d_view/3d_navigation.html",
"category": "3D View",
}
diff --git a/space_view3d_brush_menus/__init__.py b/space_view3d_brush_menus/__init__.py
index 5b1245e5..1a8d677a 100644
--- a/space_view3d_brush_menus/__init__.py
+++ b/space_view3d_brush_menus/__init__.py
@@ -23,12 +23,11 @@ bl_info = {
"name": "Dynamic Brush Menus",
"description": "Fast access to brushes & tools in Sculpt and Paint Modes",
"author": "Ryan Inch (Imaginer)",
- "version": (1, 1, 7),
+ "version": (1, 1, 8),
"blender": (2, 80, 0),
"location": "Spacebar in Sculpt/Paint Modes",
"warning": '',
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/3D_interaction/Advanced_UI_Menus",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/interface/brush_menus.html",
"category": "Interface"}
diff --git a/space_view3d_brush_menus/brushes.py b/space_view3d_brush_menus/brushes.py
index 3478d575..4d99b7d5 100644
--- a/space_view3d_brush_menus/brushes.py
+++ b/space_view3d_brush_menus/brushes.py
@@ -36,6 +36,8 @@ brush_icon = {
"CLAY_STRIPS": 'BRUSH_CLAY_STRIPS',
"CREASE": 'BRUSH_CREASE',
"DRAW": 'BRUSH_SCULPT_DRAW',
+ "DRAW_SHARP": 'BRUSH_SCULPT_DRAW',
+ "ELASTIC_DEFORM": 'BRUSH_GRAB',
"FILL": 'BRUSH_FILL',
"FLATTEN": 'BRUSH_FLATTEN',
"GRAB": 'BRUSH_GRAB',
@@ -44,6 +46,7 @@ brush_icon = {
"MASK": 'BRUSH_MASK',
"NUDGE": 'BRUSH_NUDGE',
"PINCH": 'BRUSH_PINCH',
+ "POSE": 'BRUSH_GRAB',
"ROTATE": 'BRUSH_ROTATE',
"SCRAPE": 'BRUSH_SCRAPE',
"SIMPLIFY": 'BRUSH_DATA',
diff --git a/space_view3d_math_vis/__init__.py b/space_view3d_math_vis/__init__.py
index e110ac52..129a0618 100644
--- a/space_view3d_math_vis/__init__.py
+++ b/space_view3d_math_vis/__init__.py
@@ -25,8 +25,8 @@ bl_info = {
"blender": (2, 80, 0),
"location": "Properties: Scene > Math Vis Console and Python Console: Menu",
"description": "Display console defined mathutils variables in the 3D view",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/3D_interaction/Math_Viz",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "3d_view/math_vis_console.html",
"support": "OFFICIAL",
"category": "3D View",
}
diff --git a/space_view3d_stored_views/__init__.py b/space_view3d_stored_views/__init__.py
index 9e475a9c..f35d6c16 100644
--- a/space_view3d_stored_views/__init__.py
+++ b/space_view3d_stored_views/__init__.py
@@ -24,8 +24,8 @@ bl_info = {
"blender": (2, 80, 0),
"location": "View3D > Sidebar > View > Stored Views",
"warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.5/"
- "Py/Scripts/3D_interaction/stored_views",
+ "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
+ "3d_view/stored_views.html",
"category": "3D View"
}