Skip to content

Commit 8ddd922

Browse files
committed
Split and convert the super_spine rig to the new format.
Combining spine, head and tail in the same rig leads to overly complicated and inflexible code. The only reason to do that is to be able to join the B-Bone chains together, and with the new rig class structure it can be done via rig interaction. Thus the rig is split into a basic spine rig, and a head rig that can optionally attach its deform chain to the parent.
1 parent 781bad9 commit 8ddd922

File tree

2 files changed

+925
-0
lines changed

2 files changed

+925
-0
lines changed

rigs/spines/basic_spine.py

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
#====================== BEGIN GPL LICENSE BLOCK ======================
2+
#
3+
# This program is free software; you can redistribute it and/or
4+
# modify it under the terms of the GNU General Public License
5+
# as published by the Free Software Foundation; either version 2
6+
# of the License, or (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with this program; if not, write to the Free Software Foundation,
15+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16+
#
17+
#======================= END GPL LICENSE BLOCK ========================
18+
19+
# <pep8 compliant>
20+
21+
import bpy
22+
23+
from itertools import count, repeat
24+
25+
from ..chain_rigs import TweakChainRig
26+
27+
from ...utils.errors import MetarigError
28+
from ...utils.layers import ControlLayersOption
29+
from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name
30+
from ...utils.bones import BoneDict, put_bone, align_bone_to_axis
31+
from ...utils.widgets_basic import create_circle_widget, create_cube_widget
32+
from ...utils.misc import map_list, map_apply
33+
34+
from ...base_rig import *
35+
36+
37+
class Rig(TweakChainRig):
38+
"""
39+
Spine rig with fixed pivot, hip/chest controls and tweaks.
40+
"""
41+
42+
bbone_segments = 8
43+
44+
def initialize(self):
45+
if len(self.bones.org) < 3:
46+
raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 or more bones" % (strip_org(self.base_bone)))
47+
48+
# Check if user provided the pivot position
49+
self.pivot_pos = self.params.pivot_pos
50+
51+
if not (0 < self.pivot_pos < len(self.bones.org)):
52+
raise MetarigError(
53+
"RIGIFY ERROR: please specify a valid pivot bone position"
54+
)
55+
56+
self.length = sum([self.get_bone(b).length for b in self.bones.org])
57+
58+
####################################################
59+
# Main control bones
60+
61+
@stage_generate_bones
62+
def make_control_chain(self):
63+
orgs = self.bones.org
64+
ctrl = self.bones.ctrl
65+
pivot = self.pivot_pos
66+
67+
ctrl.master = self.make_torso_control_bone(orgs[pivot], 'torso')
68+
ctrl.hips = self.make_hips_control_bone(orgs[pivot-1], 'hips')
69+
ctrl.chest = self.make_chest_control_bone(orgs[pivot], 'chest')
70+
71+
def make_torso_control_bone(self, org, name):
72+
name = self.copy_bone(org, name, parent=False)
73+
align_bone_to_axis(self.obj, name, 'y', self.length * 0.6)
74+
75+
# Put the main control in the middle of the hip bone
76+
base_bone = self.get_bone(self.bones.org[0])
77+
put_bone(self.obj, name, (base_bone.head + base_bone.tail) / 2)
78+
79+
return name
80+
81+
def make_hips_control_bone(self, org, name):
82+
name = self.copy_bone(org, name, parent=False)
83+
align_bone_to_axis(self.obj, name, 'y', self.length / 4, flip=True)
84+
return name
85+
86+
def make_chest_control_bone(self, org, name):
87+
name = self.copy_bone(org, name, parent=False)
88+
align_bone_to_axis(self.obj, name, 'y', self.length / 3)
89+
return name
90+
91+
@stage_parent_bones
92+
def parent_control_chain(self):
93+
ctrl = self.bones.ctrl
94+
org_parent = self.get_bone_parent(self.bones.org[0])
95+
self.set_bone_parent(ctrl.master, org_parent)
96+
self.set_bone_parent(ctrl.hips, ctrl.master)
97+
self.set_bone_parent(ctrl.chest, ctrl.master)
98+
99+
@stage_configure_bones
100+
def configure_control_chain(self):
101+
pass
102+
103+
@stage_generate_widgets
104+
def make_control_widgets(self):
105+
ctrl = self.bones.ctrl
106+
mch = self.bones.mch
107+
self.make_master_widget(ctrl.master)
108+
self.make_control_widget(ctrl.hips, mch.wgt_hips)
109+
self.make_control_widget(ctrl.chest, mch.wgt_chest)
110+
111+
def make_master_widget(self, ctrl):
112+
create_cube_widget(
113+
self.obj, ctrl,
114+
radius=0.5,
115+
bone_transform_name=None
116+
)
117+
118+
def make_control_widget(self, ctrl, wgt_mch):
119+
self.get_bone(ctrl).custom_shape_transform = self.get_bone(wgt_mch)
120+
121+
create_circle_widget(
122+
self.obj, ctrl,
123+
radius=1.0,
124+
head_tail=0.75,
125+
with_line=False,
126+
bone_transform_name=wgt_mch
127+
)
128+
129+
####################################################
130+
# MCH bones associated with main controls
131+
132+
@stage_generate_bones
133+
def make_mch_control_bones(self):
134+
orgs = self.bones.org
135+
mch = self.bones.mch
136+
137+
mch.pivot = self.make_mch_pivot_bone(orgs[self.pivot_pos], 'pivot')
138+
mch.wgt_hips = self.make_mch_widget_bone(orgs[0], 'WGT-hips')
139+
mch.wgt_chest = self.make_mch_widget_bone(orgs[-1], 'WGT-chest')
140+
141+
def make_mch_pivot_bone(self, org, name):
142+
name = self.copy_bone(org, make_mechanism_name(name), parent=False)
143+
align_bone_to_axis(self.obj, name, 'y', self.length * 0.6 / 4)
144+
return name
145+
146+
def make_mch_widget_bone(self, org, name):
147+
return self.copy_bone(org, make_mechanism_name(name), parent=False)
148+
149+
@stage_parent_bones
150+
def parent_mch_control_bones(self):
151+
mch = self.bones.mch
152+
self.set_bone_parent(mch.pivot, mch.chain.chest[0])
153+
self.set_bone_parent(mch.wgt_hips, mch.chain.hips[0])
154+
self.set_bone_parent(mch.wgt_chest, mch.chain.chest[-1])
155+
156+
@stage_rig_bones
157+
def rig_mch_control_bones(self):
158+
mch = self.bones.mch
159+
# Is it actually intending to compute average of these, or is this really intentional?
160+
# This effectively adds rotations of the hip and chest controls.
161+
self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', mch.chain.hips[-1], space='LOCAL')
162+
163+
####################################################
164+
# MCH chain for distributing hip & chest transform
165+
166+
@stage_generate_bones
167+
def make_mch_chain(self):
168+
orgs = self.bones.org
169+
self.bones.mch.chain = BoneDict(
170+
hips = map_list(self.make_mch_bone, orgs[0:self.pivot_pos], repeat(True)),
171+
chest = map_list(self.make_mch_bone, orgs[self.pivot_pos:], repeat(False)),
172+
)
173+
174+
def make_mch_bone(self, org, is_hip):
175+
name = self.copy_bone(org, make_mechanism_name(strip_org(org)), parent=False)
176+
align_bone_to_axis(self.obj, name, 'y', self.length / 10, flip=is_hip)
177+
return name
178+
179+
@stage_parent_bones
180+
def parent_mch_chain(self):
181+
master = self.bones.ctrl.master
182+
chain = self.bones.mch.chain
183+
self.parent_bone_chain([master, *reversed(chain.hips)])
184+
self.parent_bone_chain([master, *chain.chest])
185+
186+
@stage_rig_bones
187+
def rig_mch_chain(self):
188+
ctrl = self.bones.ctrl
189+
chain = self.bones.mch.chain
190+
map_apply(self.rig_mch_bone, chain.hips, repeat(ctrl.hips), repeat(len(chain.hips)))
191+
map_apply(self.rig_mch_bone, chain.chest, repeat(ctrl.chest), repeat(len(chain.chest)))
192+
193+
def rig_mch_bone(self, mch, control, count):
194+
self.make_constraint(mch, 'COPY_TRANSFORMS', control, space='LOCAL', influence=1/count)
195+
196+
####################################################
197+
# Tweak bones
198+
199+
@stage_parent_bones
200+
def parent_tweak_chain(self):
201+
mch = self.bones.mch
202+
chain = mch.chain
203+
parents = [chain.hips[0], *chain.hips[0:-1], mch.pivot, *chain.chest[1:], chain.chest[-1]]
204+
map_apply(self.set_bone_parent, self.bones.ctrl.tweak, parents)
205+
206+
@stage_configure_bones
207+
def configure_tweak_chain(self):
208+
super(Rig,self).configure_tweak_chain()
209+
210+
ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak)
211+
212+
####################################################
213+
# Deform bones
214+
215+
@stage_configure_bones
216+
def configure_bbone_chain(self):
217+
self.get_bone(self.bones.deform[0]).bone.bbone_in = 0.0
218+
219+
####################################################
220+
# SETTINGS
221+
222+
@classmethod
223+
def add_parameters(self, params):
224+
""" Add the parameters of this rig type to the
225+
RigifyParameters PropertyGroup
226+
"""
227+
228+
params.pivot_pos = bpy.props.IntProperty(
229+
name='pivot_position',
230+
default=2,
231+
min=0,
232+
description='Position of the torso control and pivot point'
233+
)
234+
235+
# Setting up extra layers for the FK and tweak
236+
ControlLayersOption.TWEAK.add_parameters(params)
237+
238+
@classmethod
239+
def parameters_ui(self, layout, params):
240+
""" Create the ui for the rig parameters."""
241+
242+
r = layout.row()
243+
r.prop(params, "pivot_pos")
244+
245+
ControlLayersOption.TWEAK.parameters_ui(layout, params)
246+
247+
248+
def create_sample(obj):
249+
# generated by rigify.utils.write_metarig
250+
bpy.ops.object.mode_set(mode='EDIT')
251+
arm = obj.data
252+
253+
bones = {}
254+
255+
bone = arm.edit_bones.new('spine')
256+
bone.head[:] = 0.0000, 0.0552, 1.0099
257+
bone.tail[:] = 0.0000, 0.0172, 1.1573
258+
bone.roll = 0.0000
259+
bone.use_connect = False
260+
bones['spine'] = bone.name
261+
262+
bone = arm.edit_bones.new('spine.001')
263+
bone.head[:] = 0.0000, 0.0172, 1.1573
264+
bone.tail[:] = 0.0000, 0.0004, 1.2929
265+
bone.roll = 0.0000
266+
bone.use_connect = True
267+
bone.parent = arm.edit_bones[bones['spine']]
268+
bones['spine.001'] = bone.name
269+
270+
bone = arm.edit_bones.new('spine.002')
271+
bone.head[:] = 0.0000, 0.0004, 1.2929
272+
bone.tail[:] = 0.0000, 0.0059, 1.4657
273+
bone.roll = 0.0000
274+
bone.use_connect = True
275+
bone.parent = arm.edit_bones[bones['spine.001']]
276+
bones['spine.002'] = bone.name
277+
278+
bone = arm.edit_bones.new('spine.003')
279+
bone.head[:] = 0.0000, 0.0059, 1.4657
280+
bone.tail[:] = 0.0000, 0.0114, 1.6582
281+
bone.roll = 0.0000
282+
bone.use_connect = True
283+
bone.parent = arm.edit_bones[bones['spine.002']]
284+
bones['spine.003'] = bone.name
285+
286+
287+
bpy.ops.object.mode_set(mode='OBJECT')
288+
pbone = obj.pose.bones[bones['spine']]
289+
pbone.rigify_type = 'spines.basic_spine'
290+
pbone.lock_location = (False, False, False)
291+
pbone.lock_rotation = (False, False, False)
292+
pbone.lock_rotation_w = False
293+
pbone.lock_scale = (False, False, False)
294+
pbone.rotation_mode = 'QUATERNION'
295+
296+
try:
297+
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]
298+
except AttributeError:
299+
pass
300+
pbone = obj.pose.bones[bones['spine.001']]
301+
pbone.rigify_type = ''
302+
pbone.lock_location = (False, False, False)
303+
pbone.lock_rotation = (False, False, False)
304+
pbone.lock_rotation_w = False
305+
pbone.lock_scale = (False, False, False)
306+
pbone.rotation_mode = 'QUATERNION'
307+
pbone = obj.pose.bones[bones['spine.002']]
308+
pbone.rigify_type = ''
309+
pbone.lock_location = (False, False, False)
310+
pbone.lock_rotation = (False, False, False)
311+
pbone.lock_rotation_w = False
312+
pbone.lock_scale = (False, False, False)
313+
pbone.rotation_mode = 'QUATERNION'
314+
pbone = obj.pose.bones[bones['spine.003']]
315+
pbone.rigify_type = ''
316+
pbone.lock_location = (False, False, False)
317+
pbone.lock_rotation = (False, False, False)
318+
pbone.lock_rotation_w = False
319+
pbone.lock_scale = (False, False, False)
320+
pbone.rotation_mode = 'QUATERNION'
321+
322+
bpy.ops.object.mode_set(mode='EDIT')
323+
for bone in arm.edit_bones:
324+
bone.select = False
325+
bone.select_head = False
326+
bone.select_tail = False
327+
for b in bones:
328+
bone = arm.edit_bones[bones[b]]
329+
bone.select = True
330+
bone.select_head = True
331+
bone.select_tail = True
332+
arm.edit_bones.active = bone

0 commit comments

Comments
 (0)