some python examples i wrote for working with data transfer modifier for copying uv's and renaming of bones of an armature. tested in Blender 3.6. there may be bugs so please modify use at your own risk.
#tested in Blender 3.6 import bpy class UVCopier(object): """this class is responsible for copying uvs from one mesh to another. it assumes all meshes have identical topology """ def __init__(self, source=None, destinations=[]): """ @param source (str) data object name for source mesh with uvs @param destination (list of str) data object names for destination meshes with identical topoloyg to source """ assert(source, "requires source mesh") assert(destinations, "requires one or more destination meshes") self._source = source self._destinations = destinations def doIt(self): """ """ print("uv copying") #transfer uv from one mesh to another destinations = self._destinations source = self._source for dest in destinations: self.transferUV(sourceMesh=source, destinationMesh=dest) def transferUV(self, sourceMesh=None, destinationMesh=None): """transfer uv from one mesh to another @param sourceMesh (str) source mesh data object name @param destinationMesh (str) destination mesh data object name """ assert(sourceMesh in bpy.data.objects and destinationMesh in bpy.data.objects, "requires data object names for source and destination meshes") #create the modifier on destination mod = bpy.data.objects[destinationMesh].modifiers.new("uvMod", "DATA_TRANSFER") mod.object = bpy.data.objects[sourceMesh] mod.mix_mode = 'REPLACE' mod.use_loop_data = True mod.data_types_loops = {'UV'} mod.loop_mapping = 'TOPOLOGY' #apply the modifier on destination bpy.data.objects[destinationMesh].select_set(True) bpy.context.view_layer.objects.active = bpy.data.objects[destinationMesh] bpy.ops.object.modifier_apply(modifier="uvMod") """#this was what was done manually in a test scene in Blender. before filling in the class details mod = bpy.data.objects['Cube.002'].modifiers.new("uvMod", "DATA_TRANSFER") mod.object = bpy.data.objects['srcMesh'] mod.mix_mode = 'REPLACE' mod.use_loop_data = True mod.data_types_loops = {'UV'} mod.loop_mapping = 'TOPOLOGY' bpy.data.objects['Cube.002'].select_set(True) bpy.context.view_layer.objects.active = bpy.data.objects['Cube.002'] bpy.ops.object.modifier_apply(modifier="uvMod") """ #import sys #import imp #sys.path.append(r"C:\Users\Nathaniel\Documents\blender_dev\src\snippets") """ import uv_snippets as uv imp.reload(uv) obj = uv.UVCopier(source="srcMesh", destinations=["Cube.001", "Cube.002"]) obj.doIt() """
import string import itertools import bpy import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) #without this info logs wouldnt show in console class BoneNamer(object): """class to handle naming of bones using letters. useful for long bone chains has api for renaming bones by world position or by hierarchy """ def __init__(self, armature='', prefix='part'): """ @param armature (str) name of armature data object name @param prefix (str) prefix to use for bone names """ assert armature, "requires armature" self._armature = armature self._prefix = prefix def nameByHierarchy(self, root, useSingleLetter=True, suffix="L"): """names entire hierarchy starting from root bone (todo: support an endbone so could stop naming after a certain point) @param root (str) name of root bone @param useSingleLetter (bool) whether to use single or multiple letter naming @param suffix (str) suffix to use for name ex: L,R or C """ bones = self.getBoneHierarchy(root) if (len(bones) > 25) and useSingleLetter: logger.warning("cannot rename more than 25 things with single letter naming") return False prefix = self._prefix #name bones using a letter for i in range(0, len(bones)): bone = bones[i] newNameLetter = self._getLetterName(i, useSingleLetter=useSingleLetter) #todo add support for non single letter newName = "{prefix}_{letter}.{suffix}".format(prefix=prefix, letter=newNameLetter, suffix=suffix) #name bone self._nameBone(bone, newName) def nameByWorldPosition(self, bones=[], axis="x", direction="+", useSingleLetter=True, suffix="L"): """ rename bone chain by world axis ex: useful for naming a chain ex: arm_a.L arm_b.L ... @param bones (list of str) bones to rename @param axis (str) world axis to use for position based naming @param direction (str either x,y,z) which axis to use for position based naming @param useSingleLetter (bool) whether to use single letter naming @param suffix (str) which suffix is bone - could be empty string if didnt want a suffix """ assert bones, "requires bones to name" if (len(bones) > 25) and useSingleLetter: logger.warning("cannot rename more than 25 things with single letter naming") return False #populate dict with world position to use posDict = {} for bone in bones: pos = self._getWorldTranslateByAxis(bone, axis) posDict[pos] = bone #in sorted order default start at smallest position - rename bones direction="+" posListSorted = sorted(posDict.items()) #todo allow reverse for direction - prefix = self._prefix for i in range(0, len(posListSorted)): bone = posListSorted[i][1] newNameLetter = self._getLetterName(i, useSingleLetter=useSingleLetter) newName = "{prefix}_{letter}.{suffix}".format(prefix=prefix, letter=newNameLetter, suffix=suffix) #name bone self._nameBone(bone, newName) def _nameBone(self, bone, name): """give provided bone the given name @param bone (str) bone name @param name (str) new name for bone """ armature = self._armature self._enterEditMode() bpy.data.objects[armature].data.edit_bones[bone].name = name return True def getBoneHierarchy(self, root): """get roots bone hierarchy """ rootBone = root armature = self._armature boneChain = [] self._enterEditMode() bpy.ops.armature.select_all(action='DESELECT') bpy.data.objects[armature].data.edit_bones[rootBone].select_head=True bpy.ops.armature.select_linked() boneChain = [bone.name for bone in bpy.data.objects[armature].data.edit_bones if bone.select_head] logger.info(boneChain) return boneChain def _getWorldTranslateByAxis(self, bone, axis): """get world translate of bone by axis - returns a double @param axis (str either x,y or z) for axis to get translation in """ assert bone, "requires a bone provided that exists in armature" result = None armature = self._armature axisIndex = ["x", "y", "z"].index(axis.lower()) self._enterEditMode() result = bpy.data.objects[armature].data.edit_bones[bone].matrix.translation[axisIndex] return result def _enterEditMode(self): armature = self._armature bpy.context.view_layer.objects.active = bpy.data.objects[armature] bpy.ops.object.mode_set(mode='EDIT') def _getLetterName(self, i, useSingleLetter=True): """ @param i (int) index for letter """ alphabets = [let for let in string.ascii_lowercase] #list of a-z if useSingleLetter: return alphabets[i] #use letter triple multipleLetters = [''.join(j) for j in itertools.product(alphabets, repeat=3)] return multipleLetters[i] #import sys #import imp #tested in Blender 3.6.5 #sys.path.append(r"C:\Users\Nathaniel\Documents\blender_dev\src\snippets") """ import bone_snippets as bs #name of python file is bone_snippets.py imp.reload(bs) bln = bs.BoneNamer(armature="Armature", prefix="spine") bln.nameByWorldPosition(bones=["Bone", "Bone.002", "Bone.003", "Bone.004"], axis="z", direction="+", suffix="C") #should get spine_a.L spine_b.L ... #named using hierarchy way bln = bs.BoneNamer(armature="Armature", prefix="spine") bln.nameByHierarchy("Bone", suffix="C") """ #inspired by #https://stackoverflow.com/questions/9001509/how-do-i-sort-a-dictionary-by-key #https://stackoverflow.com/questions/3190122/python-how-to-print-range-a-z #https://stackoverflow.com/questions/7074051/what-is-the-best-way-to-generate-all-possible-three-letter-strings
Happy Sketching!