Skip to content

Commit cc90d51

Browse files
committed
feat: experimental bendy_face
utils: strip_def
1 parent 772337b commit cc90d51

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

rigs/experimental/bendy_face.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import bpy, re
22
from mathutils import Vector
33
from ...utils import copy_bone, flip_bone, put_bone
4-
from ...utils import org, strip_org, make_deformer_name, connected_children_names, make_mechanism_name
4+
from ...utils import org, strip_org, strip_def, make_deformer_name, connected_children_names, make_mechanism_name
55
from ...utils import create_circle_widget, create_sphere_widget, create_widget, create_cube_widget
66
from ...utils import MetarigError
77
from rna_prop_ui import rna_idprop_ui_prop_get
@@ -13,6 +13,8 @@ class Rig:
1313
CTRL_SCALE = 0.1
1414
MCH_SCALE = 0.3
1515

16+
POSITION_RELATIVE_ERROR = 1e-3 # error below which two positions are considered equal (relative to bone len)
17+
1618
def __init__(self, obj, bone_name, params):
1719
self.obj = obj
1820
self.bones = dict()
@@ -130,6 +132,54 @@ def aggregate_ctrls(self):
130132

131133
return True
132134

135+
def make_constraints(self):
136+
"""
137+
Make constraints for each bone subgroup
138+
:return:
139+
"""
140+
141+
bpy.ops.object.mode_set(mode='OBJECT')
142+
pose_bones = self.obj.pose.bones
143+
144+
### Constrain DEF-bones ###
145+
for subchain in self.bones['def']:
146+
for name in self.bones['def'][subchain]:
147+
owner_pb = pose_bones[name]
148+
149+
subtarget = make_mechanism_name(strip_def(name))
150+
const = owner_pb.constraints.new('COPY_LOCATION')
151+
const.target = self.obj
152+
const.subtarget = subtarget
153+
154+
tail_subtargets = self.get_ctrls_by_position(owner_pb.bone.tail, subchain=subchain)
155+
156+
if tail_subtargets:
157+
const = owner_pb.constraints.new('DAMPED_TRACK')
158+
const.target = self.obj
159+
const.subtarget = tail_subtargets[0]
160+
161+
const = owner_pb.constraints.new('STRETCH_TO')
162+
const.target = self.obj
163+
const.subtarget = tail_subtargets[0]
164+
165+
def parent_bones(self):
166+
"""
167+
Specify bone parenting
168+
:return:
169+
"""
170+
171+
bpy.ops.object.mode_set(mode='EDIT')
172+
edit_bones = self.obj.data.edit_bones
173+
174+
### PARENT MCH-bones ###
175+
for subchain in self.bones['mch']:
176+
for name in self.bones['mch'][subchain]:
177+
mch_bone = edit_bones[name]
178+
parents = self.get_ctrls_by_position(mch_bone.head, subchain=subchain)
179+
180+
if parents:
181+
mch_bone.parent = edit_bones[parents[0]]
182+
133183
def get_aggregate_name(self, aggregate):
134184

135185
total = '.'.join(aggregate)
@@ -182,10 +232,48 @@ def get_start_bones(self):
182232

183233
return start_bones
184234

235+
def get_ctrls_by_position(self, position, subchain='', relative_error=0):
236+
"""
237+
Returns the controls closest to position in given relative_error range and subchain
238+
checking subchain first and then aggregates
239+
:param subchain:
240+
:type subchain: str
241+
:param position:
242+
:type subchain: Vector
243+
:return:
244+
:rtype: list(str)
245+
"""
246+
247+
bpy.ops.object.mode_set(mode='EDIT')
248+
edit_bones = self.obj.data.edit_bones
249+
250+
bones_in_range = []
251+
252+
if subchain:
253+
keys = [subchain]
254+
if 'aggregate' in self.bones['ctrl']:
255+
keys.append('aggregate')
256+
else:
257+
keys = self.bones['ctrl'].keys()
258+
259+
if not relative_error:
260+
relative_error = self.POSITION_RELATIVE_ERROR
261+
262+
for chain in keys:
263+
for name in self.bones['ctrl'][chain]:
264+
error = edit_bones[name].length * relative_error
265+
if (edit_bones[name].head - position).magnitude <= error:
266+
bones_in_range.append(name)
267+
268+
return bones_in_range
269+
185270
def generate(self):
186271

187272
self.create_mch()
188273
self.create_def()
189274
self.create_controls()
190275

276+
self.make_constraints()
277+
self.parent_bones()
278+
191279
return [""]

utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ def strip_mch(name):
125125
else:
126126
return name
127127

128+
def strip_def(name):
129+
""" Returns the name with DEF_PREFIX stripped from it.
130+
"""
131+
if name.startswith(DEF_PREFIX):
132+
return name[len(DEF_PREFIX):]
133+
else:
134+
return name
135+
128136
def org(name):
129137
""" Prepends the ORG_PREFIX to a name if it doesn't already have
130138
it, and returns it.

0 commit comments

Comments
 (0)