1 #====================== BEGIN GPL LICENSE BLOCK ======================
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.
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.
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.
17 #======================= END GPL LICENSE BLOCK ========================
24 from mathutils
import Matrix
26 from .errors
import MetarigError
27 from .collections
import ensure_widget_collection
29 WGT_PREFIX
= "WGT-" # Prefix for widget objects
31 #=============================================
33 #=============================================
36 def obj_to_bone(obj
, rig
, bone_name
, bone_transform_name
=None):
37 """ Places an object at the location/rotation/scale of the given bone.
39 if bpy
.context
.mode
== 'EDIT_ARMATURE':
40 raise MetarigError("obj_to_bone(): does not work while in edit mode")
42 bone
= rig
.pose
.bones
[bone_name
]
43 scale
= bone
.custom_shape_scale
45 if bone
.use_custom_shape_bone_size
:
48 if bone_transform_name
is not None:
49 bone
= rig
.pose
.bones
[bone_transform_name
]
50 elif bone
.custom_shape_transform
:
51 bone
= bone
.custom_shape_transform
53 obj
.rotation_mode
= 'XYZ'
54 obj
.matrix_basis
= rig
.matrix_world
@ bone
.bone
.matrix_local
@ Matrix
.Scale(scale
, 4)
57 def create_widget(rig
, bone_name
, bone_transform_name
=None):
58 """ Creates an empty widget object for a bone, and returns the object.
60 obj_name
= WGT_PREFIX
+ rig
.name
+ '_' + bone_name
61 scene
= bpy
.context
.scene
62 collection
= ensure_widget_collection(bpy
.context
)
64 # Check if it already exists in the scene
65 if obj_name
in scene
.objects
:
66 # Move object to bone position, in case it changed
67 obj
= scene
.objects
[obj_name
]
68 obj_to_bone(obj
, rig
, bone_name
, bone_transform_name
)
72 # Delete object if it exists in blend data but not scene data.
73 # This is necessary so we can then create the object without
75 if obj_name
in bpy
.data
.objects
:
76 bpy
.data
.objects
[obj_name
].user_clear()
77 bpy
.data
.objects
.remove(bpy
.data
.objects
[obj_name
])
80 mesh
= bpy
.data
.meshes
.new(obj_name
)
81 obj
= bpy
.data
.objects
.new(obj_name
, mesh
)
82 collection
.objects
.link(obj
)
84 # Move object to bone position and set layers
85 obj_to_bone(obj
, rig
, bone_name
, bone_transform_name
)
86 wgts_group_name
= 'WGTS_' + rig
.name
87 if wgts_group_name
in bpy
.data
.objects
.keys():
88 obj
.parent
= bpy
.data
.objects
[wgts_group_name
]
93 def create_circle_polygon(number_verts
, axis
, radius
=1.0, head_tail
=0.0):
94 """ Creates a basic circle around of an axis selected.
95 number_verts: number of vertices of the polygon
96 axis: axis normal to the circle
97 radius: the radius of the circle
98 head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
102 angle
= 2 * math
.pi
/ number_verts
105 assert(axis
in 'XYZ')
107 while i
< (number_verts
):
108 a
= math
.cos(i
* angle
)
109 b
= math
.sin(i
* angle
)
112 verts
.append((head_tail
, a
* radius
, b
* radius
))
114 verts
.append((a
* radius
, head_tail
, b
* radius
))
116 verts
.append((a
* radius
, b
* radius
, head_tail
))
118 if i
< (number_verts
- 1):
119 edges
.append((i
, i
+ 1))
123 edges
.append((0, number_verts
- 1))
128 def adjust_widget_axis(obj
, axis
='y', offset
=0.0):
137 trans_matrix
= Matrix
.Translation((0.0, offset
, 0.0))
138 rot_matrix
= Matrix
.Diagonal((1.0, s
, 1.0, 1.0))
141 rot_matrix
= Matrix
.Rotation(-s
*math
.pi
/2, 4, 'Z')
142 trans_matrix
= Matrix
.Translation((offset
, 0.0, 0.0))
145 rot_matrix
= Matrix
.Rotation(s
*math
.pi
/2, 4, 'X')
146 trans_matrix
= Matrix
.Translation((0.0, 0.0, offset
))
148 matrix
= trans_matrix
@ rot_matrix
150 for vert
in mesh
.vertices
:
151 vert
.co
= matrix
@ vert
.co
154 def adjust_widget_transform_mesh(obj
, matrix
, local
=None):
155 """Adjust the generated widget by applying a correction matrix to the mesh.
156 If local is false, the matrix is in world space.
157 If local is True, it's in the local space of the widget.
158 If local is a bone, it's in the local space of the bone.
161 if local
is not True:
163 assert isinstance(local
, bpy
.types
.PoseBone
)
164 bonemat
= local
.id_data
.matrix_world
@ local
.bone
.matrix_local
165 matrix
= bonemat
@ matrix
@ bonemat
.inverted()
167 obmat
= obj
.matrix_basis
168 matrix
= obmat
.inverted() @ matrix
@ obmat
170 obj
.data
.transform(matrix
)
173 def write_widget(obj
):
174 """ Write a mesh object as a python script for widget use.
177 script
+= "def create_thing_widget(rig, bone_name, size=1.0, bone_transform_name=None):\n"
178 script
+= " obj = create_widget(rig, bone_name, bone_transform_name)\n"
179 script
+= " if obj is not None:\n"
182 script
+= " verts = ["
183 for i
, v
in enumerate(obj
.data
.vertices
):
184 script
+= "({:g}*size, {:g}*size, {:g}*size),".format(v
.co
[0], v
.co
[1], v
.co
[2])
185 script
+= "\n " if i
% 2 == 1 else " "
189 script
+= " edges = ["
190 for i
, e
in enumerate(obj
.data
.edges
):
191 script
+= "(" + str(e
.vertices
[0]) + ", " + str(e
.vertices
[1]) + "),"
192 script
+= "\n " if i
% 10 == 9 else " "
196 script
+= " faces = ["
197 for i
, f
in enumerate(obj
.data
.polygons
):
200 script
+= str(v
) + ", "
202 script
+= "\n " if i
% 10 == 9 else " "
206 script
+= "\n mesh = obj.data\n"
207 script
+= " mesh.from_pydata(verts, edges, faces)\n"
208 script
+= " mesh.update()\n"
209 script
+= " return obj\n"
211 script
+= " return None\n"