1
$\begingroup$

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()
$\endgroup$
  • $\begingroup$ The second script uses four operators within the loop. Each operator handles scene updating, and some in addition the undo stack. Try to at least work around armature_add and constraint_add $\endgroup$ – aliasguru Dec 24 '17 at 7:12
  • 1
    $\begingroup$ Also, do you really need an extra armature for each vertex? Could it be one armature with a lot of bones instead? $\endgroup$ – aliasguru Dec 24 '17 at 7:14
  • $\begingroup$ I know nothing of scripting i just joined these together. So it would be great to use one armature! $\endgroup$ – con Dec 24 '17 at 7:45
  • $\begingroup$ Related $\endgroup$ – batFINGER Dec 24 '17 at 8:26
  • $\begingroup$ I've added a script to my answer, which uses a single armature. $\endgroup$ – dr. Sybren Dec 24 '17 at 13:59
3
$\begingroup$

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

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ 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 $\endgroup$ – con Dec 24 '17 at 12:54
  • $\begingroup$ 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. $\endgroup$ – dr. Sybren Dec 24 '17 at 12:58
  • $\begingroup$ 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. $\endgroup$ – dr. Sybren Dec 24 '17 at 13:57
  • $\begingroup$ Don't forget to mark my answer as accepted answer $\endgroup$ – dr. Sybren Dec 24 '17 at 16:52
  • $\begingroup$ Shouldn't bpy.data.objects.get('Armature') and an if clause evaluate faster than try except? Nice answer nonetheless! $\endgroup$ – aliasguru Dec 24 '17 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.