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$
5
  • $\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, 2017 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, 2017 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, 2017 at 7:45
  • $\begingroup$ Related $\endgroup$
    – batFINGER
    Dec 24, 2017 at 8:26
  • $\begingroup$ I've added a script to my answer, which uses a single armature. $\endgroup$
    – dr. Sybren
    Dec 24, 2017 at 13:59

1 Answer 1

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

$\endgroup$
9
  • 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, 2017 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, 2017 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, 2017 at 13:57
  • $\begingroup$ Don't forget to mark my answer as accepted answer $\endgroup$
    – dr. Sybren
    Dec 24, 2017 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, 2017 at 16:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .