only apply modifiers if the mesh has modifiers
[blender-addons.git] / space_view3d_copy_attributes.py
blob49bc1b2206a5536030c59c7e2c37b3327e04c879
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 #####
19 # <pep8 compliant>
21 bl_info = {
22 "name": "Copy Attributes Menu",
23 "author": "Bassam Kurdali, Fabian Fricke, Adam Wiseman",
24 "version": (0, 4, 7),
25 "blender": (2, 63, 0),
26 "location": "View3D > Ctrl-C",
27 "description": "Copy Attributes Menu from Blender 2.4",
28 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
29 "Scripts/3D_interaction/Copy_Attributes_Menu",
30 "tracker_url": "https://projects.blender.org/tracker/index.php?"
31 "func=detail&aid=22588",
32 "category": "3D View"}
34 import bpy
35 from mathutils import Matrix
38 def build_exec(loopfunc, func):
39 """Generator function that returns exec functions for operators """
41 def exec_func(self, context):
42 loopfunc(self, context, func)
43 return {'FINISHED'}
44 return exec_func
47 def build_invoke(loopfunc, func):
48 """Generator function that returns invoke functions for operators"""
50 def invoke_func(self, context, event):
51 loopfunc(self, context, func)
52 return {'FINISHED'}
53 return invoke_func
56 def build_op(idname, label, description, fpoll, fexec, finvoke):
57 """Generator function that returns the basic operator"""
59 class myopic(bpy.types.Operator):
60 bl_idname = idname
61 bl_label = label
62 bl_description = description
63 execute = fexec
64 poll = fpoll
65 invoke = finvoke
66 return myopic
69 def genops(copylist, oplist, prefix, poll_func, loopfunc):
70 """Generate ops from the copy list and its associated functions """
71 for op in copylist:
72 exec_func = build_exec(loopfunc, op[3])
73 invoke_func = build_invoke(loopfunc, op[3])
74 opclass = build_op(prefix + op[0], "Copy " + op[1], op[2],
75 poll_func, exec_func, invoke_func)
76 oplist.append(opclass)
79 def generic_copy(source, target, string=""):
80 """ copy attributes from source to target that have string in them """
81 for attr in dir(source):
82 if attr.find(string) > -1:
83 try:
84 setattr(target, attr, getattr(source, attr))
85 except:
86 pass
87 return
90 def getmat(bone, active, context, ignoreparent):
91 """Helper function for visual transform copy,
92 gets the active transform in bone space
93 """
94 obj_act = context.active_object
95 data_bone = obj_act.data.bones[bone.name]
96 #all matrices are in armature space unless commented otherwise
97 otherloc = active.matrix # final 4x4 mat of target, location.
98 bonemat_local = data_bone.matrix_local.copy() # self rest matrix
99 if data_bone.parent:
100 parentposemat = obj_act.pose.bones[data_bone.parent.name].matrix.copy()
101 parentbonemat = data_bone.parent.matrix_local.copy()
102 else:
103 parentposemat = parentbonemat = Matrix()
104 if parentbonemat == parentposemat or ignoreparent:
105 newmat = bonemat_local.inverted() * otherloc
106 else:
107 bonemat = parentbonemat.inverted() * bonemat_local
109 newmat = bonemat.inverted() * parentposemat.inverted() * otherloc
110 return newmat
113 def rotcopy(item, mat):
114 """copy rotation to item from matrix mat depending on item.rotation_mode"""
115 if item.rotation_mode == 'QUATERNION':
116 item.rotation_quaternion = mat.to_3x3().to_quaternion()
117 elif item.rotation_mode == 'AXIS_ANGLE':
118 quat = mat.to_3x3().to_quaternion()
119 item.rotation_axis_angle = quat.axis[:] + (quat.angle, )
120 else:
121 item.rotation_euler = mat.to_3x3().to_euler(item.rotation_mode)
124 def pLoopExec(self, context, funk):
125 """Loop over selected bones and execute funk on them"""
126 active = context.active_pose_bone
127 selected = context.selected_pose_bones
128 selected.remove(active)
129 for bone in selected:
130 funk(bone, active, context)
132 #The following functions are used o copy attributes frome active to bone
135 def pLocLocExec(bone, active, context):
136 bone.location = active.location
139 def pLocRotExec(bone, active, context):
140 rotcopy(bone, active.matrix_basis.to_3x3())
143 def pLocScaExec(bone, active, context):
144 bone.scale = active.scale
147 def pVisLocExec(bone, active, context):
148 bone.location = getmat(bone, active, context, False).to_translation()
151 def pVisRotExec(bone, active, context):
152 rotcopy(bone, getmat(bone, active,
153 context, not context.active_object.data.bones[bone.name].use_inherit_rotation))
156 def pVisScaExec(bone, active, context):
157 bone.scale = getmat(bone, active, context,
158 not context.active_object.data.bones[bone.name].use_inherit_scale)\
159 .to_scale()
162 def pDrwExec(bone, active, context):
163 bone.custom_shape = active.custom_shape
166 def pLokExec(bone, active, context):
167 for index, state in enumerate(active.lock_location):
168 bone.lock_location[index] = state
169 for index, state in enumerate(active.lock_rotation):
170 bone.lock_rotation[index] = state
171 bone.lock_rotations_4d = active.lock_rotations_4d
172 bone.lock_rotation_w = active.lock_rotation_w
173 for index, state in enumerate(active.lock_scale):
174 bone.lock_scale[index] = state
177 def pConExec(bone, active, context):
178 for old_constraint in active.constraints.values():
179 new_constraint = bone.constraints.new(old_constraint.type)
180 generic_copy(old_constraint, new_constraint)
183 def pIKsExec(bone, active, context):
184 generic_copy(active, bone, "ik_")
186 pose_copies = (('pose_loc_loc', "Local Location",
187 "Copy Location from Active to Selected", pLocLocExec),
188 ('pose_loc_rot', "Local Rotation",
189 "Copy Rotation from Active to Selected", pLocRotExec),
190 ('pose_loc_sca', "Local Scale",
191 "Copy Scale from Active to Selected", pLocScaExec),
192 ('pose_vis_loc', "Visual Location",
193 "Copy Location from Active to Selected", pVisLocExec),
194 ('pose_vis_rot', "Visual Rotation",
195 "Copy Rotation from Active to Selected", pVisRotExec),
196 ('pose_vis_sca', "Visual Scale",
197 "Copy Scale from Active to Selected", pVisScaExec),
198 ('pose_drw', "Bone Shape",
199 "Copy Bone Shape from Active to Selected", pDrwExec),
200 ('pose_lok', "Protected Transform",
201 "Copy Protected Tranforms from Active to Selected", pLokExec),
202 ('pose_con', "Bone Constraints",
203 "Copy Object Constraints from Active to Selected", pConExec),
204 ('pose_iks', "IK Limits",
205 "Copy IK Limits from Active to Selected", pIKsExec))
208 @classmethod
209 def pose_poll_func(cls, context):
210 return(context.mode == 'POSE')
213 def pose_invoke_func(self, context, event):
214 wm = context.window_manager
215 wm.invoke_props_dialog(self)
216 return {'RUNNING_MODAL'}
219 class CopySelectedPoseConstraints(bpy.types.Operator):
220 """Copy Chosen constraints from active to selected"""
221 bl_idname = "pose.copy_selected_constraints"
222 bl_label = "Copy Selected Constraints"
223 selection = bpy.props.BoolVectorProperty(size=32)
225 poll = pose_poll_func
226 invoke = pose_invoke_func
228 def draw(self, context):
229 layout = self.layout
230 for idx, const in enumerate(context.active_pose_bone.constraints):
231 layout.prop(self, "selection", index=idx, text=const.name,
232 toggle=True)
234 def execute(self, context):
235 active = context.active_pose_bone
236 selected = context.selected_pose_bones[:]
237 selected.remove(active)
238 for bone in selected:
239 for index, flag in enumerate(self.selection):
240 if flag:
241 old_constraint = active.constraints[index]
242 new_constraint = bone.constraints.new(\
243 active.constraints[index].type)
244 generic_copy(old_constraint, new_constraint)
245 return {'FINISHED'}
247 pose_ops = [] # list of pose mode copy operators
249 genops(pose_copies, pose_ops, "pose.copy_", pose_poll_func, pLoopExec)
252 class VIEW3D_MT_posecopypopup(bpy.types.Menu):
253 bl_label = "Copy Attributes"
255 def draw(self, context):
256 layout = self.layout
257 layout.operator_context = 'INVOKE_REGION_WIN'
258 for op in pose_copies:
259 layout.operator("pose.copy_" + op[0])
260 layout.operator("pose.copy_selected_constraints")
261 layout.operator("pose.copy", text="copy pose")
264 def obLoopExec(self, context, funk):
265 """Loop over selected objects and execute funk on them"""
266 active = context.active_object
267 selected = context.selected_objects[:]
268 selected.remove(active)
269 for obj in selected:
270 msg = funk(obj, active, context)
271 if msg:
272 self.report({msg[0]}, msg[1])
275 def world_to_basis(active, ob, context):
276 """put world coords of active as basis coords of ob"""
277 local = ob.parent.matrix_world.inverted() * active.matrix_world
278 P = ob.matrix_basis * ob.matrix_local.inverted()
279 mat = P * local
280 return(mat)
282 #The following functions are used o copy attributes from
283 #active to selected object
286 def obLoc(ob, active, context):
287 ob.location = active.location
290 def obRot(ob, active, context):
291 rotcopy(ob, active.matrix_local.to_3x3())
294 def obSca(ob, active, context):
295 ob.scale = active.scale
298 def obVisLoc(ob, active, context):
299 if ob.parent:
300 mat = world_to_basis(active, ob, context)
301 ob.location = mat.to_translation()
302 else:
303 ob.location = active.matrix_world.to_translation()
306 def obVisRot(ob, active, context):
307 if ob.parent:
308 mat = world_to_basis(active, ob, context)
309 rotcopy(ob, mat.to_3x3())
310 else:
311 rotcopy(ob, active.matrix_world.to_3x3())
314 def obVisSca(ob, active, context):
315 if ob.parent:
316 mat = world_to_basis(active, ob, context)
317 ob.scale = mat.to_scale()
318 else:
319 ob.scale = active.matrix_world.to_scale()
322 def obDrw(ob, active, context):
323 ob.draw_type = active.draw_type
324 ob.show_axis = active.show_axis
325 ob.show_bounds = active.show_bounds
326 ob.draw_bounds_type = active.draw_bounds_type
327 ob.show_name = active.show_name
328 ob.show_texture_space = active.show_texture_space
329 ob.show_transparent = active.show_transparent
330 ob.show_wire = active.show_wire
331 ob.show_x_ray = active.show_x_ray
332 ob.empty_draw_type = active.empty_draw_type
333 ob.empty_draw_size = active.empty_draw_size
336 def obOfs(ob, active, context):
337 ob.time_offset = active.time_offset
338 return('INFO', "time offset copied")
341 def obDup(ob, active, context):
342 generic_copy(active, ob, "dupli")
343 return('INFO', "duplication method copied")
346 def obCol(ob, active, context):
347 ob.color = active.color
350 def obMas(ob, active, context):
351 ob.game.mass = active.game.mass
352 return('INFO', "mass copied")
355 def obLok(ob, active, context):
356 for index, state in enumerate(active.lock_location):
357 ob.lock_location[index] = state
358 for index, state in enumerate(active.lock_rotation):
359 ob.lock_rotation[index] = state
360 ob.lock_rotations_4d = active.lock_rotations_4d
361 ob.lock_rotation_w = active.lock_rotation_w
362 for index, state in enumerate(active.lock_scale):
363 ob.lock_scale[index] = state
364 return('INFO', "transform locks copied")
367 def obCon(ob, active, context):
368 #for consistency with 2.49, delete old constraints first
369 for removeconst in ob.constraints:
370 ob.constraints.remove(removeconst)
371 for old_constraint in active.constraints.values():
372 new_constraint = ob.constraints.new(old_constraint.type)
373 generic_copy(old_constraint, new_constraint)
374 return('INFO', "constraints copied")
377 def obTex(ob, active, context):
378 if 'texspace_location' in dir(ob.data) and 'texspace_location' in dir(
379 active.data):
380 ob.data.texspace_location[:] = active.data.texspace_location[:]
381 if 'texspace_size' in dir(ob.data) and 'texspace_size' in dir(active.data):
382 ob.data.texspace_size[:] = active.data.texspace_size[:]
383 return('INFO', "texture space copied")
386 def obIdx(ob, active, context):
387 ob.pass_index = active.pass_index
388 return('INFO', "pass index copied")
391 def obMod(ob, active, context):
392 for modifier in ob.modifiers:
393 #remove existing before adding new:
394 ob.modifiers.remove(modifier)
395 for old_modifier in active.modifiers.values():
396 new_modifier = ob.modifiers.new(name=old_modifier.name,
397 type=old_modifier.type)
398 generic_copy(old_modifier, new_modifier)
399 return('INFO', "modifiers copied")
402 def obGrp(ob, active, context):
403 for grp in bpy.data.groups:
404 if active.name in grp.objects and ob.name not in grp.objects:
405 grp.objects.link(ob)
406 return('INFO', "groups copied")
409 def obWei(ob, active, context):
410 me_source = active.data
411 me_target = ob.data
412 # sanity check: do source and target have the same amount of verts?
413 if len(me_source.vertices) != len(me_target.vertices):
414 return('ERROR', "objects have different vertex counts, doing nothing")
415 vgroups_IndexName = {}
416 for i in range(0, len(active.vertex_groups)):
417 groups = active.vertex_groups[i]
418 vgroups_IndexName[groups.index] = groups.name
419 data = {} # vert_indices, [(vgroup_index, weights)]
420 for v in me_source.vertices:
421 vg = v.groups
422 vi = v.index
423 if len(vg) > 0:
424 vgroup_collect = []
425 for i in range(0, len(vg)):
426 vgroup_collect.append((vg[i].group, vg[i].weight))
427 data[vi] = vgroup_collect
428 # write data to target
429 if ob != active:
430 # add missing vertex groups
431 for vgroup_name in vgroups_IndexName.values():
432 #check if group already exists...
433 already_present = 0
434 for i in range(0, len(ob.vertex_groups)):
435 if ob.vertex_groups[i].name == vgroup_name:
436 already_present = 1
437 # ... if not, then add
438 if already_present == 0:
439 ob.vertex_groups.new(name=vgroup_name)
440 # write weights
441 for v in me_target.vertices:
442 for vi_source, vgroupIndex_weight in data.items():
443 if v.index == vi_source:
445 for i in range(0, len(vgroupIndex_weight)):
446 groupName = vgroups_IndexName[vgroupIndex_weight[i][0]]
447 groups = ob.vertex_groups
448 for vgs in range(0, len(groups)):
449 if groups[vgs].name == groupName:
450 groups[vgs].add((v.index,),
451 vgroupIndex_weight[i][1], "REPLACE")
452 return('INFO', "weights copied")
454 object_copies = (
455 #('obj_loc', "Location",
456 #"Copy Location from Active to Selected", obLoc),
457 #('obj_rot', "Rotation",
458 #"Copy Rotation from Active to Selected", obRot),
459 #('obj_sca', "Scale",
460 #"Copy Scale from Active to Selected", obSca),
461 ('obj_vis_loc', "Location",
462 "Copy Location from Active to Selected", obVisLoc),
463 ('obj_vis_rot', "Rotation",
464 "Copy Rotation from Active to Selected", obVisRot),
465 ('obj_vis_sca', "Scale",
466 "Copy Scale from Active to Selected", obVisSca),
467 ('obj_drw', "Draw Options",
468 "Copy Draw Options from Active to Selected", obDrw),
469 ('obj_ofs', "Time Offset",
470 "Copy Time Offset from Active to Selected", obOfs),
471 ('obj_dup', "Dupli",
472 "Copy Dupli from Active to Selected", obDup),
473 ('obj_col', "Object Color",
474 "Copy Object Color from Active to Selected", obCol),
475 ('obj_mas', "Mass",
476 "Copy Mass from Active to Selected", obMas),
477 #('obj_dmp', "Damping",
478 #"Copy Damping from Active to Selected"),
479 #('obj_all', "All Physical Attributes",
480 #"Copy Physical Atributes from Active to Selected"),
481 #('obj_prp', "Properties",
482 #"Copy Properties from Active to Selected"),
483 #('obj_log', "Logic Bricks",
484 #"Copy Logic Bricks from Active to Selected"),
485 ('obj_lok', "Protected Transform",
486 "Copy Protected Tranforms from Active to Selected", obLok),
487 ('obj_con', "Object Constraints",
488 "Copy Object Constraints from Active to Selected", obCon),
489 #('obj_nla', "NLA Strips",
490 #"Copy NLA Strips from Active to Selected"),
491 #('obj_tex', "Texture Space",
492 #"Copy Texture Space from Active to Selected", obTex),
493 #('obj_sub', "Subsurf Settings",
494 #"Copy Subsurf Setings from Active to Selected"),
495 #('obj_smo', "AutoSmooth",
496 #"Copy AutoSmooth from Active to Selected"),
497 ('obj_idx', "Pass Index",
498 "Copy Pass Index from Active to Selected", obIdx),
499 ('obj_mod', "Modifiers",
500 "Copy Modifiers from Active to Selected", obMod),
501 ('obj_wei', "Vertex Weights",
502 "Copy vertex weights based on indices", obWei),
503 ('obj_grp', "Group Links",
504 "Copy selected into active object's groups", obGrp))
507 @classmethod
508 def object_poll_func(cls, context):
509 return(len(context.selected_objects) > 1)
512 def object_invoke_func(self, context, event):
513 wm = context.window_manager
514 wm.invoke_props_dialog(self)
515 return {'RUNNING_MODAL'}
518 class CopySelectedObjectConstraints(bpy.types.Operator):
519 """Copy Chosen constraints from active to selected"""
520 bl_idname = "object.copy_selected_constraints"
521 bl_label = "Copy Selected Constraints"
522 selection = bpy.props.BoolVectorProperty(size=32)
524 poll = object_poll_func
526 invoke = object_invoke_func
528 def draw(self, context):
529 layout = self.layout
530 for idx, const in enumerate(context.active_object.constraints):
531 layout.prop(self, "selection", index=idx, text=const.name,
532 toggle=True)
534 def execute(self, context):
535 active = context.active_object
536 selected = context.selected_objects[:]
537 selected.remove(active)
538 for obj in selected:
539 for index, flag in enumerate(self.selection):
540 if flag:
541 old_constraint = active.constraints[index]
542 new_constraint = obj.constraints.new(\
543 active.constraints[index].type)
544 generic_copy(old_constraint, new_constraint)
545 return{'FINISHED'}
548 class CopySelectedObjectModifiers(bpy.types.Operator):
549 """Copy Chosen modifiers from active to selected"""
550 bl_idname = "object.copy_selected_modifiers"
551 bl_label = "Copy Selected Modifiers"
552 selection = bpy.props.BoolVectorProperty(size=32)
554 poll = object_poll_func
556 invoke = object_invoke_func
558 def draw(self, context):
559 layout = self.layout
560 for idx, const in enumerate(context.active_object.modifiers):
561 layout.prop(self, 'selection', index=idx, text=const.name,
562 toggle=True)
564 def execute(self, context):
565 active = context.active_object
566 selected = context.selected_objects[:]
567 selected.remove(active)
568 for obj in selected:
569 for index, flag in enumerate(self.selection):
570 if flag:
571 old_modifier = active.modifiers[index]
572 new_modifier = obj.modifiers.new(\
573 type=active.modifiers[index].type,
574 name=active.modifiers[index].name)
575 generic_copy(old_modifier, new_modifier)
576 return{'FINISHED'}
578 object_ops = []
579 genops(object_copies, object_ops, "object.copy_", object_poll_func, obLoopExec)
582 class VIEW3D_MT_copypopup(bpy.types.Menu):
583 bl_label = "Copy Attributes"
585 def draw(self, context):
586 layout = self.layout
587 layout.operator_context = 'INVOKE_REGION_WIN'
588 for op in object_copies:
589 layout.operator("object.copy_" + op[0])
590 layout.operator("object.copy_selected_constraints")
591 layout.operator("object.copy_selected_modifiers")
593 #Begin Mesh copy settings:
596 class MESH_MT_CopyFaceSettings(bpy.types.Menu):
597 bl_label = "Copy Face Settings"
599 @classmethod
600 def poll(cls, context):
601 return context.mode == 'EDIT_MESH'
603 def draw(self, context):
604 mesh = context.object.data
605 uv = len(mesh.uv_textures) > 1
606 vc = len(mesh.vertex_colors) > 1
607 layout = self.layout
609 op = layout.operator(MESH_OT_CopyFaceSettings.bl_idname,
610 text="Copy Material")
611 op['layer'] = ''
612 op['mode'] = 'MAT'
613 if mesh.uv_textures.active:
614 op = layout.operator(MESH_OT_CopyFaceSettings.bl_idname,
615 text="Copy Image")
616 op['layer'] = ''
617 op['mode'] = 'IMAGE'
618 op = layout.operator(MESH_OT_CopyFaceSettings.bl_idname,
619 text="Copy UV Coords")
620 op['layer'] = ''
621 op['mode'] = 'UV'
622 if mesh.vertex_colors.active:
623 op = layout.operator(MESH_OT_CopyFaceSettings.bl_idname,
624 text="Copy Vertex Colors")
625 op['layer'] = ''
626 op['mode'] = 'VCOL'
627 if uv or vc:
628 layout.separator()
629 if uv:
630 layout.menu("MESH_MT_CopyImagesFromLayer")
631 layout.menu("MESH_MT_CopyUVCoordsFromLayer")
632 if vc:
633 layout.menu("MESH_MT_CopyVertexColorsFromLayer")
636 def _buildmenu(self, mesh, mode):
637 layout = self.layout
638 if mode == 'VCOL':
639 layers = mesh.vertex_colors
640 else:
641 layers = mesh.uv_textures
642 for layer in layers:
643 if not layer.active:
644 op = layout.operator(MESH_OT_CopyFaceSettings.bl_idname,
645 text=layer.name)
646 op['layer'] = layer.name
647 op['mode'] = mode
650 @classmethod
651 def _poll_layer_uvs(cls, context):
652 return context.mode == "EDIT_MESH" and len(
653 context.object.data.uv_layers) > 1
656 @classmethod
657 def _poll_layer_vcols(cls, context):
658 return context.mode == "EDIT_MESH" and len(
659 context.object.data.vertex_colors) > 1
662 def _build_draw(mode):
663 return (lambda self, context: _buildmenu(self, context.object.data, mode))
665 _layer_menu_data = (("UV Coords", _build_draw("UV"), _poll_layer_uvs),
666 ("Images", _build_draw("IMAGE"), _poll_layer_uvs),
667 ("Vertex Colors", _build_draw("VCOL"), _poll_layer_vcols))
668 _layer_menus = []
669 for name, draw_func, poll_func in _layer_menu_data:
670 classname = "MESH_MT_Copy" + "".join(name.split()) + "FromLayer"
671 menuclass = type(classname, (bpy.types.Menu,),
672 dict(bl_label="Copy " + name + " from layer",
673 bl_idname=classname,
674 draw=draw_func,
675 poll=poll_func))
676 _layer_menus.append(menuclass)
679 class MESH_OT_CopyFaceSettings(bpy.types.Operator):
680 """Copy settings from active face to all selected faces"""
681 bl_idname = 'mesh.copy_face_settings'
682 bl_label = "Copy Face Settings"
683 bl_options = {'REGISTER', 'UNDO'}
685 mode = bpy.props.StringProperty(name="mode")
686 layer = bpy.props.StringProperty(name="layer")
688 @classmethod
689 def poll(cls, context):
690 return context.mode == 'EDIT_MESH'
692 def execute(self, context):
693 mode = getattr(self, 'mode', '')
694 if not mode in {'MAT', 'VCOL', 'IMAGE', 'UV'}:
695 self.report({'ERROR'}, "No mode specified or invalid mode.")
696 return self._end(context, {'CANCELLED'})
697 layername = getattr(self, 'layer', '')
698 mesh = context.object.data
700 # Switching out of edit mode updates the selected state of faces and
701 # makes the data from the uv texture and vertex color layers available.
702 bpy.ops.object.editmode_toggle()
704 polys = mesh.polygons
705 if mode == 'MAT':
706 to_data = from_data = polys
707 else:
708 if mode == 'VCOL':
709 layers = mesh.vertex_colors
710 act_layer = mesh.vertex_colors.active
711 elif mode == 'IMAGE':
712 layers = mesh.uv_textures
713 act_layer = mesh.uv_textures.active
714 elif mode == 'UV':
715 layers = mesh.uv_layers
716 act_layer = mesh.uv_layers.active
717 if not layers or (layername and not layername in layers):
718 self.report({'ERROR'}, "Invalid UV or color layer.")
719 return self._end(context, {'CANCELLED'})
720 from_data = layers[layername or act_layer.name].data
721 to_data = act_layer.data
722 from_index = polys.active
724 for f in polys:
725 if f.select:
726 if to_data != from_data:
727 # Copying from another layer.
728 # from_face is to_face's counterpart from other layer.
729 from_index = f.index
730 elif f.index == from_index:
731 # Otherwise skip copying a face to itself.
732 continue
733 if mode == 'MAT':
734 f.material_index = polys[from_index].material_index
735 continue
736 elif mode == 'IMAGE':
737 to_data[f.index].image = from_data[from_index].image
738 continue
739 if len(f.loop_indices) != len(polys[from_index].loop_indices):
740 self.report({'WARNING'}, "Different number of vertices.")
741 for i in range(len(f.loop_indices)):
742 to_vertex = f.loop_indices[i]
743 from_vertex = polys[from_index].loop_indices[i]
744 if mode == 'VCOL':
745 to_data[to_vertex].color = from_data[from_vertex].color
746 elif mode == 'UV':
747 to_data[to_vertex].uv = from_data[from_vertex].uv
749 return self._end(context, {'FINISHED'})
751 def _end(self, context, retval):
752 if context.mode != 'EDIT_MESH':
753 # Clean up by returning to edit mode like it was before.
754 bpy.ops.object.editmode_toggle()
755 return(retval)
758 def register():
759 bpy.utils.register_module(__name__)
761 ''' mostly to get the keymap working '''
762 kc = bpy.context.window_manager.keyconfigs.addon
763 if kc:
764 km = kc.keymaps.new(name="Object Mode")
765 kmi = km.keymap_items.new('wm.call_menu', 'C', 'PRESS', ctrl=True)
766 kmi.properties.name = 'VIEW3D_MT_copypopup'
768 km = kc.keymaps.new(name="Pose")
769 kmi = km.keymap_items.get("pose.copy")
770 if kmi is not None:
771 kmi.idname = 'wm.call_menu'
772 else:
773 kmi = km.keymap_items.new('wm.call_menu', 'C', 'PRESS', ctrl=True)
774 kmi.properties.name = 'VIEW3D_MT_posecopypopup'
775 for menu in _layer_menus:
776 bpy.utils.register_class(menu)
778 km = kc.keymaps.new(name="Mesh")
779 kmi = km.keymap_items.new('wm.call_menu', 'C', 'PRESS')
780 kmi.ctrl = True
781 kmi.properties.name = 'MESH_MT_CopyFaceSettings'
784 def unregister():
785 bpy.utils.unregister_module(__name__)
787 ''' mostly to remove the keymap '''
788 kc = bpy.context.window_manager.keyconfigs.addon
789 if kc:
790 kms = kc.keymaps['Pose']
791 for item in kms.keymap_items:
792 if item.name == 'Call Menu' and item.idname == 'wm.call_menu' and \
793 item.properties.name == 'VIEW3D_MT_posecopypopup':
794 item.idname = 'pose.copy'
795 break
796 for menu in _layer_menus:
797 bpy.utils.unregister_class(menu)
798 km = kc.keymaps['Mesh']
799 for kmi in km.keymap_items:
800 if kmi.idname == 'wm.call_menu':
801 if kmi.properties.name == 'MESH_MT_CopyFaceSettings':
802 km.keymap_items.remove(kmi)
804 km = kc.keymaps['Object Mode']
805 for kmi in km.keymap_items:
806 if kmi.idname == 'wm.call_menu':
807 if kmi.properties.name == 'VIEW3D_MT_copypopup':
808 km.keymap_items.remove(kmi)
810 if __name__ == "__main__":
811 register()