Changeset View
Changeset View
Standalone View
Standalone View
rigify/utils/widgets.py
- This file was added.
| #====================== 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 | |||||
| import math | |||||
| from .errors import MetarigError | |||||
| WGT_PREFIX = "WGT-" # Prefix for widget objects | |||||
| #============================================= | |||||
| # Widget creation | |||||
| #============================================= | |||||
| def obj_to_bone(obj, rig, bone_name): | |||||
| """ Places an object at the location/rotation/scale of the given bone. | |||||
| """ | |||||
| if bpy.context.mode == 'EDIT_ARMATURE': | |||||
| raise MetarigError("obj_to_bone(): does not work while in edit mode") | |||||
| bone = rig.data.bones[bone_name] | |||||
| mat = rig.matrix_world @ bone.matrix_local | |||||
| obj.location = mat.to_translation() | |||||
| obj.rotation_mode = 'XYZ' | |||||
| obj.rotation_euler = mat.to_euler() | |||||
| scl = mat.to_scale() | |||||
| scl_avg = (scl[0] + scl[1] + scl[2]) / 3 | |||||
| obj.scale = (bone.length * scl_avg), (bone.length * scl_avg), (bone.length * scl_avg) | |||||
| def create_widget(rig, bone_name, bone_transform_name=None): | |||||
| """ Creates an empty widget object for a bone, and returns the object. | |||||
| """ | |||||
| if bone_transform_name is None: | |||||
| bone_transform_name = bone_name | |||||
| obj_name = WGT_PREFIX + rig.name + '_' + bone_name | |||||
| scene = bpy.context.scene | |||||
| collection = bpy.context.collection | |||||
| # Check if it already exists in the scene | |||||
| if obj_name in scene.objects: | |||||
| # Move object to bone position, in case it changed | |||||
| obj = scene.objects[obj_name] | |||||
| obj_to_bone(obj, rig, bone_transform_name) | |||||
| return None | |||||
| else: | |||||
| # Delete object if it exists in blend data but not scene data. | |||||
| # This is necessary so we can then create the object without | |||||
| # name conflicts. | |||||
| if obj_name in bpy.data.objects: | |||||
| bpy.data.objects[obj_name].user_clear() | |||||
| bpy.data.objects.remove(bpy.data.objects[obj_name]) | |||||
| # Create mesh object | |||||
| mesh = bpy.data.meshes.new(obj_name) | |||||
| obj = bpy.data.objects.new(obj_name, mesh) | |||||
| collection.objects.link(obj) | |||||
| # Move object to bone position and set layers | |||||
| obj_to_bone(obj, rig, bone_transform_name) | |||||
| wgts_group_name = 'WGTS_' + rig.name | |||||
| if wgts_group_name in bpy.data.objects.keys(): | |||||
| obj.parent = bpy.data.objects[wgts_group_name] | |||||
| return obj | |||||
| def create_circle_polygon(number_verts, axis, radius=1.0, head_tail=0.0): | |||||
| """ Creates a basic circle around of an axis selected. | |||||
| number_verts: number of vertices of the poligon | |||||
| axis: axis normal to the circle | |||||
| radius: the radius of the circle | |||||
| head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail) | |||||
| """ | |||||
| verts = [] | |||||
| edges = [] | |||||
| angle = 2 * math.pi / number_verts | |||||
| i = 0 | |||||
| assert(axis in 'XYZ') | |||||
| while i < (number_verts): | |||||
| a = math.cos(i * angle) | |||||
| b = math.sin(i * angle) | |||||
| if axis == 'X': | |||||
| verts.append((head_tail, a * radius, b * radius)) | |||||
| elif axis == 'Y': | |||||
| verts.append((a * radius, head_tail, b * radius)) | |||||
| elif axis == 'Z': | |||||
| verts.append((a * radius, b * radius, head_tail)) | |||||
| if i < (number_verts - 1): | |||||
| edges.append((i , i + 1)) | |||||
| i += 1 | |||||
| edges.append((0, number_verts - 1)) | |||||
| return verts, edges | |||||
| def write_widget(obj): | |||||
| """ Write a mesh object as a python script for widget use. | |||||
| """ | |||||
| script = "" | |||||
| script += "def create_thing_widget(rig, bone_name, size=1.0, bone_transform_name=None):\n" | |||||
| script += " obj = create_widget(rig, bone_name, bone_transform_name)\n" | |||||
| script += " if obj != None:\n" | |||||
| # Vertices | |||||
| if len(obj.data.vertices) > 0: | |||||
| script += " verts = [" | |||||
| for v in obj.data.vertices: | |||||
| script += "(" + str(v.co[0]) + "*size, " + str(v.co[1]) + "*size, " + str(v.co[2]) + "*size), " | |||||
| script += "]\n" | |||||
| # Edges | |||||
| if len(obj.data.edges) > 0: | |||||
| script += " edges = [" | |||||
| for e in obj.data.edges: | |||||
| script += "(" + str(e.vertices[0]) + ", " + str(e.vertices[1]) + "), " | |||||
| script += "]\n" | |||||
| # Faces | |||||
| if len(obj.data.polygons) > 0: | |||||
| script += " faces = [" | |||||
| for f in obj.data.polygons: | |||||
| script += "(" | |||||
| for v in f.vertices: | |||||
| script += str(v) + ", " | |||||
| script += "), " | |||||
| script += "]\n" | |||||
| # Build mesh | |||||
| script += "\n mesh = obj.data\n" | |||||
| script += " mesh.from_pydata(verts, edges, faces)\n" | |||||
| script += " mesh.update()\n" | |||||
| script += " mesh.update()\n" | |||||
| script += " return obj\n" | |||||
| script += " else:\n" | |||||
| script += " return None\n" | |||||
| return script | |||||