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 #####
22 "name": "Copy Attributes Menu",
23 "author": "Bassam Kurdali, Fabian Fricke, Adam Wiseman",
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 "category": "3D View",
34 from mathutils
import Matrix
37 def build_exec(loopfunc
, func
):
38 """Generator function that returns exec functions for operators """
40 def exec_func(self
, context
):
41 loopfunc(self
, context
, func
)
46 def build_invoke(loopfunc
, func
):
47 """Generator function that returns invoke functions for operators"""
49 def invoke_func(self
, context
, event
):
50 loopfunc(self
, context
, func
)
55 def build_op(idname
, label
, description
, fpoll
, fexec
, finvoke
):
56 """Generator function that returns the basic operator"""
58 class myopic(bpy
.types
.Operator
):
61 bl_description
= description
68 def genops(copylist
, oplist
, prefix
, poll_func
, loopfunc
):
69 """Generate ops from the copy list and its associated functions """
71 exec_func
= build_exec(loopfunc
, op
[3])
72 invoke_func
= build_invoke(loopfunc
, op
[3])
73 opclass
= build_op(prefix
+ op
[0], "Copy " + op
[1], op
[2],
74 poll_func
, exec_func
, invoke_func
)
75 oplist
.append(opclass
)
78 def generic_copy(source
, target
, string
=""):
79 """ copy attributes from source to target that have string in them """
80 for attr
in dir(source
):
81 if attr
.find(string
) > -1:
83 setattr(target
, attr
, getattr(source
, attr
))
89 def getmat(bone
, active
, context
, ignoreparent
):
90 """Helper function for visual transform copy,
91 gets the active transform in bone space
93 obj_act
= context
.active_object
94 data_bone
= obj_act
.data
.bones
[bone
.name
]
95 #all matrices are in armature space unless commented otherwise
96 otherloc
= active
.matrix
# final 4x4 mat of target, location.
97 bonemat_local
= data_bone
.matrix_local
.copy() # self rest matrix
99 parentposemat
= obj_act
.pose
.bones
[data_bone
.parent
.name
].matrix
.copy()
100 parentbonemat
= data_bone
.parent
.matrix_local
.copy()
102 parentposemat
= parentbonemat
= Matrix()
103 if parentbonemat
== parentposemat
or ignoreparent
:
104 newmat
= bonemat_local
.inverted() * otherloc
106 bonemat
= parentbonemat
.inverted() * bonemat_local
108 newmat
= bonemat
.inverted() * parentposemat
.inverted() * otherloc
112 def rotcopy(item
, mat
):
113 """copy rotation to item from matrix mat depending on item.rotation_mode"""
114 if item
.rotation_mode
== 'QUATERNION':
115 item
.rotation_quaternion
= mat
.to_3x3().to_quaternion()
116 elif item
.rotation_mode
== 'AXIS_ANGLE':
117 rot
= mat
.to_3x3().to_quaternion().to_axis_angle() # returns (Vector((x, y, z)), w)
118 axis_angle
= rot
[1], rot
[0][0], rot
[0][1], rot
[0][2] # convert to w, x, y, z
119 item
.rotation_axis_angle
= axis_angle
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
)\
162 def pDrwExec(bone
, active
, context
):
163 bone
.custom_shape
= active
.custom_shape
164 bone
.bone
.show_wire
= active
.bone
.show_wire
167 def pLokExec(bone
, active
, context
):
168 for index
, state
in enumerate(active
.lock_location
):
169 bone
.lock_location
[index
] = state
170 for index
, state
in enumerate(active
.lock_rotation
):
171 bone
.lock_rotation
[index
] = state
172 bone
.lock_rotations_4d
= active
.lock_rotations_4d
173 bone
.lock_rotation_w
= active
.lock_rotation_w
174 for index
, state
in enumerate(active
.lock_scale
):
175 bone
.lock_scale
[index
] = state
178 def pConExec(bone
, active
, context
):
179 for old_constraint
in active
.constraints
.values():
180 new_constraint
= bone
.constraints
.new(old_constraint
.type)
181 generic_copy(old_constraint
, new_constraint
)
184 def pIKsExec(bone
, active
, context
):
185 generic_copy(active
, bone
, "ik_")
188 def pBBonesExec(bone
, active
, context
):
189 object = active
.id_data
191 object.data
.bones
[active
.name
],
192 object.data
.bones
[bone
.name
],
195 pose_copies
= (('pose_loc_loc', "Local Location",
196 "Copy Location from Active to Selected", pLocLocExec
),
197 ('pose_loc_rot', "Local Rotation",
198 "Copy Rotation from Active to Selected", pLocRotExec
),
199 ('pose_loc_sca', "Local Scale",
200 "Copy Scale from Active to Selected", pLocScaExec
),
201 ('pose_vis_loc', "Visual Location",
202 "Copy Location from Active to Selected", pVisLocExec
),
203 ('pose_vis_rot', "Visual Rotation",
204 "Copy Rotation from Active to Selected", pVisRotExec
),
205 ('pose_vis_sca', "Visual Scale",
206 "Copy Scale from Active to Selected", pVisScaExec
),
207 ('pose_drw', "Bone Shape",
208 "Copy Bone Shape from Active to Selected", pDrwExec
),
209 ('pose_lok', "Protected Transform",
210 "Copy Protected Tranforms from Active to Selected", pLokExec
),
211 ('pose_con', "Bone Constraints",
212 "Copy Object Constraints from Active to Selected", pConExec
),
213 ('pose_iks', "IK Limits",
214 "Copy IK Limits from Active to Selected", pIKsExec
),
215 ('bbone_settings', "BBone Settings",
216 "Copy BBone Settings from Active to Selected", pBBonesExec
),)
220 def pose_poll_func(cls
, context
):
221 return(context
.mode
== 'POSE')
224 def pose_invoke_func(self
, context
, event
):
225 wm
= context
.window_manager
226 wm
.invoke_props_dialog(self
)
227 return {'RUNNING_MODAL'}
230 class CopySelectedPoseConstraints(bpy
.types
.Operator
):
231 """Copy Chosen constraints from active to selected"""
232 bl_idname
= "pose.copy_selected_constraints"
233 bl_label
= "Copy Selected Constraints"
234 selection
= bpy
.props
.BoolVectorProperty(size
=32, options
={'SKIP_SAVE'})
236 poll
= pose_poll_func
237 invoke
= pose_invoke_func
239 def draw(self
, context
):
241 for idx
, const
in enumerate(context
.active_pose_bone
.constraints
):
242 layout
.prop(self
, "selection", index
=idx
, text
=const
.name
,
245 def execute(self
, context
):
246 active
= context
.active_pose_bone
247 selected
= context
.selected_pose_bones
[:]
248 selected
.remove(active
)
249 for bone
in selected
:
250 for index
, flag
in enumerate(self
.selection
):
252 old_constraint
= active
.constraints
[index
]
253 new_constraint
= bone
.constraints
.new(\
254 active
.constraints
[index
].type)
255 generic_copy(old_constraint
, new_constraint
)
258 pose_ops
= [] # list of pose mode copy operators
260 genops(pose_copies
, pose_ops
, "pose.copy_", pose_poll_func
, pLoopExec
)
263 class VIEW3D_MT_posecopypopup(bpy
.types
.Menu
):
264 bl_label
= "Copy Attributes"
266 def draw(self
, context
):
268 layout
.operator_context
= 'INVOKE_REGION_WIN'
269 layout
.operator("view3d.copybuffer", icon
="COPY_ID")
270 for op
in pose_copies
:
271 layout
.operator("pose.copy_" + op
[0])
272 layout
.operator("pose.copy_selected_constraints")
273 layout
.operator("pose.copy", text
="copy pose")
276 def obLoopExec(self
, context
, funk
):
277 """Loop over selected objects and execute funk on them"""
278 active
= context
.active_object
279 selected
= context
.selected_objects
[:]
280 selected
.remove(active
)
282 msg
= funk(obj
, active
, context
)
284 self
.report({msg
[0]}, msg
[1])
287 def world_to_basis(active
, ob
, context
):
288 """put world coords of active as basis coords of ob"""
289 local
= ob
.parent
.matrix_world
.inverted() * active
.matrix_world
290 P
= ob
.matrix_basis
* ob
.matrix_local
.inverted()
294 #The following functions are used o copy attributes from
295 #active to selected object
298 def obLoc(ob
, active
, context
):
299 ob
.location
= active
.location
302 def obRot(ob
, active
, context
):
303 rotcopy(ob
, active
.matrix_local
.to_3x3())
306 def obSca(ob
, active
, context
):
307 ob
.scale
= active
.scale
310 def obVisLoc(ob
, active
, context
):
312 mat
= world_to_basis(active
, ob
, context
)
313 ob
.location
= mat
.to_translation()
315 ob
.location
= active
.matrix_world
.to_translation()
318 def obVisRot(ob
, active
, context
):
320 mat
= world_to_basis(active
, ob
, context
)
321 rotcopy(ob
, mat
.to_3x3())
323 rotcopy(ob
, active
.matrix_world
.to_3x3())
326 def obVisSca(ob
, active
, context
):
328 mat
= world_to_basis(active
, ob
, context
)
329 ob
.scale
= mat
.to_scale()
331 ob
.scale
= active
.matrix_world
.to_scale()
334 def obDrw(ob
, active
, context
):
335 ob
.draw_type
= active
.draw_type
336 ob
.show_axis
= active
.show_axis
337 ob
.show_bounds
= active
.show_bounds
338 ob
.draw_bounds_type
= active
.draw_bounds_type
339 ob
.show_name
= active
.show_name
340 ob
.show_texture_space
= active
.show_texture_space
341 ob
.show_transparent
= active
.show_transparent
342 ob
.show_wire
= active
.show_wire
343 ob
.show_x_ray
= active
.show_x_ray
344 ob
.empty_draw_type
= active
.empty_draw_type
345 ob
.empty_draw_size
= active
.empty_draw_size
348 def obOfs(ob
, active
, context
):
349 ob
.time_offset
= active
.time_offset
350 return('INFO', "time offset copied")
353 def obDup(ob
, active
, context
):
354 generic_copy(active
, ob
, "dupli")
355 return('INFO', "duplication method copied")
358 def obCol(ob
, active
, context
):
359 ob
.color
= active
.color
362 def obMas(ob
, active
, context
):
363 ob
.game
.mass
= active
.game
.mass
364 return('INFO', "mass copied")
367 def obLok(ob
, active
, context
):
368 for index
, state
in enumerate(active
.lock_location
):
369 ob
.lock_location
[index
] = state
370 for index
, state
in enumerate(active
.lock_rotation
):
371 ob
.lock_rotation
[index
] = state
372 ob
.lock_rotations_4d
= active
.lock_rotations_4d
373 ob
.lock_rotation_w
= active
.lock_rotation_w
374 for index
, state
in enumerate(active
.lock_scale
):
375 ob
.lock_scale
[index
] = state
376 return('INFO', "transform locks copied")
379 def obCon(ob
, active
, context
):
380 #for consistency with 2.49, delete old constraints first
381 for removeconst
in ob
.constraints
:
382 ob
.constraints
.remove(removeconst
)
383 for old_constraint
in active
.constraints
.values():
384 new_constraint
= ob
.constraints
.new(old_constraint
.type)
385 generic_copy(old_constraint
, new_constraint
)
386 return('INFO', "constraints copied")
389 def obTex(ob
, active
, context
):
390 if 'texspace_location' in dir(ob
.data
) and 'texspace_location' in dir(
392 ob
.data
.texspace_location
[:] = active
.data
.texspace_location
[:]
393 if 'texspace_size' in dir(ob
.data
) and 'texspace_size' in dir(active
.data
):
394 ob
.data
.texspace_size
[:] = active
.data
.texspace_size
[:]
395 return('INFO', "texture space copied")
398 def obIdx(ob
, active
, context
):
399 ob
.pass_index
= active
.pass_index
400 return('INFO', "pass index copied")
403 def obMod(ob
, active
, context
):
404 for modifier
in ob
.modifiers
:
405 #remove existing before adding new:
406 ob
.modifiers
.remove(modifier
)
407 for old_modifier
in active
.modifiers
.values():
408 new_modifier
= ob
.modifiers
.new(name
=old_modifier
.name
,
409 type=old_modifier
.type)
410 generic_copy(old_modifier
, new_modifier
)
411 return('INFO', "modifiers copied")
414 def obGrp(ob
, active
, context
):
415 for grp
in bpy
.data
.groups
:
416 if active
.name
in grp
.objects
and ob
.name
not in grp
.objects
:
418 return('INFO', "groups copied")
421 def obWei(ob
, active
, context
):
422 me_source
= active
.data
424 # sanity check: do source and target have the same amount of verts?
425 if len(me_source
.vertices
) != len(me_target
.vertices
):
426 return('ERROR', "objects have different vertex counts, doing nothing")
427 vgroups_IndexName
= {}
428 for i
in range(0, len(active
.vertex_groups
)):
429 groups
= active
.vertex_groups
[i
]
430 vgroups_IndexName
[groups
.index
] = groups
.name
431 data
= {} # vert_indices, [(vgroup_index, weights)]
432 for v
in me_source
.vertices
:
437 for i
in range(0, len(vg
)):
438 vgroup_collect
.append((vg
[i
].group
, vg
[i
].weight
))
439 data
[vi
] = vgroup_collect
440 # write data to target
442 # add missing vertex groups
443 for vgroup_name
in vgroups_IndexName
.values():
444 #check if group already exists...
446 for i
in range(0, len(ob
.vertex_groups
)):
447 if ob
.vertex_groups
[i
].name
== vgroup_name
:
449 # ... if not, then add
450 if already_present
== 0:
451 ob
.vertex_groups
.new(name
=vgroup_name
)
453 for v
in me_target
.vertices
:
454 for vi_source
, vgroupIndex_weight
in data
.items():
455 if v
.index
== vi_source
:
457 for i
in range(0, len(vgroupIndex_weight
)):
458 groupName
= vgroups_IndexName
[vgroupIndex_weight
[i
][0]]
459 groups
= ob
.vertex_groups
460 for vgs
in range(0, len(groups
)):
461 if groups
[vgs
].name
== groupName
:
462 groups
[vgs
].add((v
.index
,),
463 vgroupIndex_weight
[i
][1], "REPLACE")
464 return('INFO', "weights copied")
467 #('obj_loc', "Location",
468 #"Copy Location from Active to Selected", obLoc),
469 #('obj_rot', "Rotation",
470 #"Copy Rotation from Active to Selected", obRot),
471 #('obj_sca', "Scale",
472 #"Copy Scale from Active to Selected", obSca),
473 ('obj_vis_loc', "Location",
474 "Copy Location from Active to Selected", obVisLoc
),
475 ('obj_vis_rot', "Rotation",
476 "Copy Rotation from Active to Selected", obVisRot
),
477 ('obj_vis_sca', "Scale",
478 "Copy Scale from Active to Selected", obVisSca
),
479 ('obj_drw', "Draw Options",
480 "Copy Draw Options from Active to Selected", obDrw
),
481 ('obj_ofs', "Time Offset",
482 "Copy Time Offset from Active to Selected", obOfs
),
484 "Copy Dupli from Active to Selected", obDup
),
485 ('obj_col', "Object Color",
486 "Copy Object Color from Active to Selected", obCol
),
488 "Copy Mass from Active to Selected", obMas
),
489 #('obj_dmp', "Damping",
490 #"Copy Damping from Active to Selected"),
491 #('obj_all', "All Physical Attributes",
492 #"Copy Physical Attributes from Active to Selected"),
493 #('obj_prp', "Properties",
494 #"Copy Properties from Active to Selected"),
495 #('obj_log', "Logic Bricks",
496 #"Copy Logic Bricks from Active to Selected"),
497 ('obj_lok', "Protected Transform",
498 "Copy Protected Tranforms from Active to Selected", obLok
),
499 ('obj_con', "Object Constraints",
500 "Copy Object Constraints from Active to Selected", obCon
),
501 #('obj_nla', "NLA Strips",
502 #"Copy NLA Strips from Active to Selected"),
503 #('obj_tex', "Texture Space",
504 #"Copy Texture Space from Active to Selected", obTex),
505 #('obj_sub', "Subsurf Settings",
506 #"Copy Subsurf Setings from Active to Selected"),
507 #('obj_smo', "AutoSmooth",
508 #"Copy AutoSmooth from Active to Selected"),
509 ('obj_idx', "Pass Index",
510 "Copy Pass Index from Active to Selected", obIdx
),
511 ('obj_mod', "Modifiers",
512 "Copy Modifiers from Active to Selected", obMod
),
513 ('obj_wei', "Vertex Weights",
514 "Copy vertex weights based on indices", obWei
),
515 ('obj_grp', "Group Links",
516 "Copy selected into active object's groups", obGrp
))
520 def object_poll_func(cls
, context
):
521 return(len(context
.selected_objects
) > 1)
524 def object_invoke_func(self
, context
, event
):
525 wm
= context
.window_manager
526 wm
.invoke_props_dialog(self
)
527 return {'RUNNING_MODAL'}
530 class CopySelectedObjectConstraints(bpy
.types
.Operator
):
531 """Copy Chosen constraints from active to selected"""
532 bl_idname
= "object.copy_selected_constraints"
533 bl_label
= "Copy Selected Constraints"
534 selection
= bpy
.props
.BoolVectorProperty(size
=32, options
={'SKIP_SAVE'})
536 poll
= object_poll_func
538 invoke
= object_invoke_func
540 def draw(self
, context
):
542 for idx
, const
in enumerate(context
.active_object
.constraints
):
543 layout
.prop(self
, "selection", index
=idx
, text
=const
.name
,
546 def execute(self
, context
):
547 active
= context
.active_object
548 selected
= context
.selected_objects
[:]
549 selected
.remove(active
)
551 for index
, flag
in enumerate(self
.selection
):
553 old_constraint
= active
.constraints
[index
]
554 new_constraint
= obj
.constraints
.new(\
555 active
.constraints
[index
].type)
556 generic_copy(old_constraint
, new_constraint
)
560 class CopySelectedObjectModifiers(bpy
.types
.Operator
):
561 """Copy Chosen modifiers from active to selected"""
562 bl_idname
= "object.copy_selected_modifiers"
563 bl_label
= "Copy Selected Modifiers"
564 selection
= bpy
.props
.BoolVectorProperty(size
=32, options
={'SKIP_SAVE'})
566 poll
= object_poll_func
568 invoke
= object_invoke_func
570 def draw(self
, context
):
572 for idx
, const
in enumerate(context
.active_object
.modifiers
):
573 layout
.prop(self
, 'selection', index
=idx
, text
=const
.name
,
576 def execute(self
, context
):
577 active
= context
.active_object
578 selected
= context
.selected_objects
[:]
579 selected
.remove(active
)
581 for index
, flag
in enumerate(self
.selection
):
583 old_modifier
= active
.modifiers
[index
]
584 new_modifier
= obj
.modifiers
.new(\
585 type=active
.modifiers
[index
].type,
586 name
=active
.modifiers
[index
].name
)
587 generic_copy(old_modifier
, new_modifier
)
591 genops(object_copies
, object_ops
, "object.copy_", object_poll_func
, obLoopExec
)
594 class VIEW3D_MT_copypopup(bpy
.types
.Menu
):
595 bl_label
= "Copy Attributes"
597 def draw(self
, context
):
599 layout
.operator_context
= 'INVOKE_REGION_WIN'
600 layout
.operator("view3d.copybuffer", icon
="COPY_ID")
601 for op
in object_copies
:
602 layout
.operator("object.copy_" + op
[0])
603 layout
.operator("object.copy_selected_constraints")
604 layout
.operator("object.copy_selected_modifiers")
606 #Begin Mesh copy settings:
609 class MESH_MT_CopyFaceSettings(bpy
.types
.Menu
):
610 bl_label
= "Copy Face Settings"
613 def poll(cls
, context
):
614 return context
.mode
== 'EDIT_MESH'
616 def draw(self
, context
):
617 mesh
= context
.object.data
618 uv
= len(mesh
.uv_textures
) > 1
619 vc
= len(mesh
.vertex_colors
) > 1
621 layout
.operator("view3d.copybuffer", icon
="COPY_ID")
622 layout
.operator("view3d.pastebuffer", icon
="COPY_ID")
623 op
= layout
.operator(MESH_OT_CopyFaceSettings
.bl_idname
,
624 text
="Copy Material")
627 if mesh
.uv_textures
.active
:
628 op
= layout
.operator(MESH_OT_CopyFaceSettings
.bl_idname
,
632 op
= layout
.operator(MESH_OT_CopyFaceSettings
.bl_idname
,
633 text
="Copy UV Coords")
636 if mesh
.vertex_colors
.active
:
637 op
= layout
.operator(MESH_OT_CopyFaceSettings
.bl_idname
,
638 text
="Copy Vertex Colors")
644 layout
.menu("MESH_MT_CopyImagesFromLayer")
645 layout
.menu("MESH_MT_CopyUVCoordsFromLayer")
647 layout
.menu("MESH_MT_CopyVertexColorsFromLayer")
650 def _buildmenu(self
, mesh
, mode
):
653 layers
= mesh
.vertex_colors
655 layers
= mesh
.uv_textures
658 op
= layout
.operator(MESH_OT_CopyFaceSettings
.bl_idname
,
660 op
['layer'] = layer
.name
665 def _poll_layer_uvs(cls
, context
):
666 return context
.mode
== "EDIT_MESH" and len(
667 context
.object.data
.uv_layers
) > 1
671 def _poll_layer_vcols(cls
, context
):
672 return context
.mode
== "EDIT_MESH" and len(
673 context
.object.data
.vertex_colors
) > 1
676 def _build_draw(mode
):
677 return (lambda self
, context
: _buildmenu(self
, context
.object.data
, mode
))
679 _layer_menu_data
= (("UV Coords", _build_draw("UV"), _poll_layer_uvs
),
680 ("Images", _build_draw("IMAGE"), _poll_layer_uvs
),
681 ("Vertex Colors", _build_draw("VCOL"), _poll_layer_vcols
))
683 for name
, draw_func
, poll_func
in _layer_menu_data
:
684 classname
= "MESH_MT_Copy" + "".join(name
.split()) + "FromLayer"
685 menuclass
= type(classname
, (bpy
.types
.Menu
,),
686 dict(bl_label
="Copy " + name
+ " from layer",
690 _layer_menus
.append(menuclass
)
693 class MESH_OT_CopyFaceSettings(bpy
.types
.Operator
):
694 """Copy settings from active face to all selected faces"""
695 bl_idname
= 'mesh.copy_face_settings'
696 bl_label
= "Copy Face Settings"
697 bl_options
= {'REGISTER', 'UNDO'}
699 mode
= bpy
.props
.StringProperty(name
="mode")
700 layer
= bpy
.props
.StringProperty(name
="layer")
703 def poll(cls
, context
):
704 return context
.mode
== 'EDIT_MESH'
706 def execute(self
, context
):
707 mode
= getattr(self
, 'mode', '')
708 if not mode
in {'MAT', 'VCOL', 'IMAGE', 'UV'}:
709 self
.report({'ERROR'}, "No mode specified or invalid mode.")
710 return self
._end
(context
, {'CANCELLED'})
711 layername
= getattr(self
, 'layer', '')
712 mesh
= context
.object.data
714 # Switching out of edit mode updates the selected state of faces and
715 # makes the data from the uv texture and vertex color layers available.
716 bpy
.ops
.object.editmode_toggle()
718 polys
= mesh
.polygons
720 to_data
= from_data
= polys
723 layers
= mesh
.vertex_colors
724 act_layer
= mesh
.vertex_colors
.active
725 elif mode
== 'IMAGE':
726 layers
= mesh
.uv_textures
727 act_layer
= mesh
.uv_textures
.active
729 layers
= mesh
.uv_layers
730 act_layer
= mesh
.uv_layers
.active
731 if not layers
or (layername
and not layername
in layers
):
732 self
.report({'ERROR'}, "Invalid UV or color layer.")
733 return self
._end
(context
, {'CANCELLED'})
734 from_data
= layers
[layername
or act_layer
.name
].data
735 to_data
= act_layer
.data
736 from_index
= polys
.active
740 if to_data
!= from_data
:
741 # Copying from another layer.
742 # from_face is to_face's counterpart from other layer.
744 elif f
.index
== from_index
:
745 # Otherwise skip copying a face to itself.
748 f
.material_index
= polys
[from_index
].material_index
750 elif mode
== 'IMAGE':
751 to_data
[f
.index
].image
= from_data
[from_index
].image
753 if len(f
.loop_indices
) != len(polys
[from_index
].loop_indices
):
754 self
.report({'WARNING'}, "Different number of vertices.")
755 for i
in range(len(f
.loop_indices
)):
756 to_vertex
= f
.loop_indices
[i
]
757 from_vertex
= polys
[from_index
].loop_indices
[i
]
759 to_data
[to_vertex
].color
= from_data
[from_vertex
].color
761 to_data
[to_vertex
].uv
= from_data
[from_vertex
].uv
763 return self
._end
(context
, {'FINISHED'})
765 def _end(self
, context
, retval
):
766 if context
.mode
!= 'EDIT_MESH':
767 # Clean up by returning to edit mode like it was before.
768 bpy
.ops
.object.editmode_toggle()
773 bpy
.utils
.register_module(__name__
)
775 ''' mostly to get the keymap working '''
776 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
778 km
= kc
.keymaps
.new(name
="Object Mode")
779 kmi
= km
.keymap_items
.new('wm.call_menu', 'C', 'PRESS', ctrl
=True)
780 kmi
.properties
.name
= 'VIEW3D_MT_copypopup'
782 km
= kc
.keymaps
.new(name
="Pose")
783 kmi
= km
.keymap_items
.get("pose.copy")
785 kmi
.idname
= 'wm.call_menu'
787 kmi
= km
.keymap_items
.new('wm.call_menu', 'C', 'PRESS', ctrl
=True)
788 kmi
.properties
.name
= 'VIEW3D_MT_posecopypopup'
789 for menu
in _layer_menus
:
790 bpy
.utils
.register_class(menu
)
792 km
= kc
.keymaps
.new(name
="Mesh")
793 kmi
= km
.keymap_items
.new('wm.call_menu', 'C', 'PRESS')
795 kmi
.properties
.name
= 'MESH_MT_CopyFaceSettings'
799 bpy
.utils
.unregister_module(__name__
)
801 ''' mostly to remove the keymap '''
802 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
804 kms
= kc
.keymaps
['Pose']
805 for item
in kms
.keymap_items
:
806 if item
.name
== 'Call Menu' and item
.idname
== 'wm.call_menu' and \
807 item
.properties
.name
== 'VIEW3D_MT_posecopypopup':
808 item
.idname
= 'pose.copy'
810 for menu
in _layer_menus
:
811 bpy
.utils
.unregister_class(menu
)
812 km
= kc
.keymaps
['Mesh']
813 for kmi
in km
.keymap_items
:
814 if kmi
.idname
== 'wm.call_menu':
815 if kmi
.properties
.name
== 'MESH_MT_CopyFaceSettings':
816 km
.keymap_items
.remove(kmi
)
818 km
= kc
.keymaps
['Object Mode']
819 for kmi
in km
.keymap_items
:
820 if kmi
.idname
== 'wm.call_menu':
821 if kmi
.properties
.name
== 'VIEW3D_MT_copypopup':
822 km
.keymap_items
.remove(kmi
)
824 if __name__
== "__main__":