1
1
import bpy , re
2
2
from mathutils import Vector
3
3
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
5
5
from ...utils import create_circle_widget , create_sphere_widget , create_widget , create_cube_widget
6
6
from ...utils import MetarigError
7
7
from rna_prop_ui import rna_idprop_ui_prop_get
@@ -13,6 +13,8 @@ class Rig:
13
13
CTRL_SCALE = 0.1
14
14
MCH_SCALE = 0.3
15
15
16
+ POSITION_RELATIVE_ERROR = 1e-3 # error below which two positions are considered equal (relative to bone len)
17
+
16
18
def __init__ (self , obj , bone_name , params ):
17
19
self .obj = obj
18
20
self .bones = dict ()
@@ -130,6 +132,54 @@ def aggregate_ctrls(self):
130
132
131
133
return True
132
134
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
+
133
183
def get_aggregate_name (self , aggregate ):
134
184
135
185
total = '.' .join (aggregate )
@@ -182,10 +232,48 @@ def get_start_bones(self):
182
232
183
233
return start_bones
184
234
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
+
185
270
def generate (self ):
186
271
187
272
self .create_mch ()
188
273
self .create_def ()
189
274
self .create_controls ()
190
275
276
+ self .make_constraints ()
277
+ self .parent_bones ()
278
+
191
279
return ["" ]
0 commit comments