1

The First scripts adds empties to vertices and is quick.

The second script add bones and constraints to the empties,

why on large mesh does the second script bring blender to its knees?

import bpy
import bmesh

context = bpy.context
scene = context.scene
obj = context.edit_object
mesh = obj.data
bm = bmesh.from_edit_mesh(mesh)
empties = []
# selected verts
for v in [v for v in bm.verts if v.select]:
    empty = bpy.data.objects.new("Empty", None)
    empty.parent = obj
    empty.parent_type = 'VERTEX'
    empty.parent_vertices = [v.index] * 3
    scene.objects.link(empty)
    empty.matrix_parent_inverse.identity()
    empties.append((v.index, empty))

THE SECOND SCRIPT

import bpy

context = bpy.context
scene = context.scene

Empty = [o for o in scene.objects if o.type == 'EMPTY']

for l in Empty:
    # add bone
    bpy.ops.object.armature_add(location=l.location)
    bpy.ops.object.posemode_toggle()
    bpy.ops.pose.constraint_add(type='COPY_LOCATION')
    bpy.context.object.pose.bones["Bone"].constraints["Copy Location"].target = l
    bpy.ops.object.posemode_toggle()
con
  • 13
  • 3

1 Answers1

3

There are a few things to improve in your scripts.

for v in [v for v in bm.verts if v.select]:

Here you create a list of selected vertices, and then loop over that list. That list constructed using the list comprehension is unnecessary. Instead just write this:

for v in bm.verts:
    if not v.select:
        continue
    ...

The same goes for creating Empties in your second script. It's not necessary to first create the entire list in memory. Either use a generator expression or just use an if ...: continue construct.


The empties.append((v.index, empty)) adds items to a list that is never used. Better remove it.


In the second script you use operators where you could use regular functions. For example, use bpy.data.armatures.new(...) and link it to a new object, instead of using bpy.ops.object.armature_add(...). Every operator call creates an undo-step in memory, so it's much slower than using direct data access functions.

Here is the second script, which uses a single armature for all the bones, and as much direct data access (properties, function calls) as possible:

import bpy

C = bpy.context
try:
    arm_ob = bpy.data.objects['Armature']
except KeyError:
    arm = bpy.data.armatures.new('Armature')
    arm_ob = bpy.data.objects.new('Armature', arm)
    C.scene.objects.link(arm_ob)
else:
    arm = arm_ob.data

# Set to EDIT mode so we can add bones.
C.scene.objects.active = arm_ob
bpy.ops.object.mode_set(mode='EDIT')

# Add the bones
for l in C.scene.objects:
    if l.type != 'EMPTY':
        continue

    bone = arm.edit_bones.new(l.name)
    bone.head = l.location
    bone.tail = bone.head
    bone.tail.z += 0.5

# Go to POSE mode so we can add constraints
bpy.ops.object.mode_set(mode='POSE')
for bone in arm_ob.pose.bones:
    clc = bone.constraints.new('COPY_LOCATION')
    clc.target = C.scene.objects[bone.name]

The armature for the subdivided Suzanne below took around a second to create, and consists of 2012 bones:

Suzanne

dr. Sybren
  • 7,139
  • 1
  • 23
  • 52
  • 1
    im not creating empties in my second script, im just using there location. I just need a script that gets all empties in a scene location then adds it to a new bone, that bone then copies location the the empty – con Dec 24 '17 at 12:54
  • You are creating unnecessary Python objects and a list to keep them all in memory. I'm referring to Empties, i.e. the name of the list, and not "empties" in general. – dr. Sybren Dec 24 '17 at 12:58
  • I've added my version of the 2nd script, which doesn't use as many operators and creates only a single armature. It's quite fast now. – dr. Sybren Dec 24 '17 at 13:57
  • Don't forget to mark my answer as accepted answer – dr. Sybren Dec 24 '17 at 16:52
  • Shouldn't bpy.data.objects.get('Armature') and an if clause evaluate faster than try except? Nice answer nonetheless! – aliasguru Dec 24 '17 at 16:55
  • It's a style and expectation thing. If the expectation is that the exception won't happen often, the more Pythonic approach is to just do it and handle the exception. If the majority of the time the exception would happen it'll give a better performance to use get(). – dr. Sybren Dec 24 '17 at 17:04
  • However, there will be an issue if bpy.data.objects['Armature'] is not in context.scene.objects. Switching to edit mode on an unlinked object can befuddle the UI. – batFINGER Dec 25 '17 at 06:39
  • Sure, the script assumes a clean file (armature doesn't exist at all) or a properly created and linked armature. Trying to repair a broken situation is beyond the scope of this question, I'd say. You could of course remove the reuse of the existing armature altogether and always create a new one every time the script runs. – dr. Sybren Dec 25 '17 at 08:54
  • The following video is to show what i was trying to achieve https://www.youtube.com/watch?v=AkQj4L-833c&feature=youtu.be – con Jan 24 '18 at 02:24