How to optimize my script to import a lot of sphere

(phm) #1

I need to import a lot (more than 100) of spheres or cylinders in Blender. I have written a little function to do that. However, my machine (Bi-Xeon core duo, 4Go mem) become completely busy when the number of sphere increase.
It’s probably due to the fact that I create all the objects and after join them to a new.
I don’t know how I can avoid this.
Here the script of the function, if somebody could take a look.


def draw_spheres(line) :
global ob, me, Z_scale, point_size
ob_list = []
scn = Scene.GetCurrent()
ln = line.split()
nb_sphere = int(ln[3])
for i in range(0, nb_sphere) : # read points coordinates
line = ImodFile.readline()
ln = line.split()
no = Vector(float(ln[0]), float(ln[1]), float(ln[2])*Z_scale)
me_new = Mesh.Primitives.Icosphere(2,point_size)
ob_new = scn.objects.new(me_new,‘sphere’)
for v in me_new.verts : # translate the sphere
v.co += no
me_new.update()
ob_new.link(me_new)
ob_list.append(ob_new)
ob.join(ob_list)
for i in ob_list : scn.objects.unlink(i)

0 Likes

(damir) #2

First of all from that code I see almost nothing. It’s not formated as it should be, and I can only assume what actually does.
I see that you call mesh.update() and if that’s in the main loop, that most certainly slows things up.
But here is a fast test how would I write something similar. It doesn’t load anything but it creates 500 Icospheres, merges them in to one object and for that, on my machine with Pentum 4 HT 3.0 GHz, 512 MB of RAM, it needs 0.65 seconds…


import Blender, time
from Blender import *
from Blender.Mathutils import *


def draw_spheres():
    
    point_size = 1
        
    all_verts = [] #place to store all verts
    all_faces = [] #place to store all faces
    
    sph_me = None
    
    for i in range(500):
        
        n = Vector(i,i,i)
        
        sverts = [] # place to store verts from single sphere
        
        sph_me = Mesh.Primitives.Icosphere(2, point_size)
        
        for vert in sph_me.verts:
            sverts.append(vert.co+n)
        
        all_verts.extend(sverts) # add stored and shifted verts to all verts
        
        fOffset = len(all_verts)-len(sverts)
        
        for fac in sph_me.faces:
            face = []
            for v in fac:
                face.append(v.index+fOffset)
            all_faces.append(face)
    
    new_me = Mesh.New("IcoSpheres")
    
    if all_verts:
        new_me.verts.extend(all_verts)
    else:
        print "Error!"
        return
    
    if all_faces:
        new_me.faces.extend(all_faces)
    
    new_ob = Object.New("Mesh","MultiSpheres")
    
    new_ob.link(new_me)
    
    scn = Scene.GetCurrent()
    
    scn.link(new_ob)
    
    Window.RedrawAll()


def do_test():    
    t1 = time.clock()
    draw_spheres()
    print "Icospheres created in: ", str(time.clock()-t1)

do_test()

0 Likes

(phm) #3

damir:
Sorry for the bad formatting of the code, it was a copy/paste and I didn’t pay attention to the indentation.
Thank you for your suggestions they work very nice.

0 Likes

(IanC) #4

You are moving every single vert, why not move the object instead?

0 Likes

(dudecon) #5

Well, first off the object would have to exist first. This means appending and creating a mesh for every sphere, which takes a very long time. Objects have much more data than just a list of points, so creating an object takes a while, and moving points is cheap.

0 Likes

(IanC) #6

However, python is not great for large loops. Cycling through large lists is most definitely not cheap. The creation of the objects is done in C, which is very good for large lists and transforms.

I’ll do a speed test and report back.

0 Likes

(IanC) #7

Ok, creating, linking, joining and unlinking objects takes 0.5 seconds whereas the above code moving verts takes 1.7.

I’m sure there is going to be a better way to share data than that though, I’m needlessly re-creating objects and copying mesh data.

Despite this it scales much better with larger meshes, moving meshes vert by vert takes 27 seconds for 50 level 3 icospheres, which for 500 would be 270, assuming good scaling. Doing it the object way takes 1.6s for 500. Also, blender actually crashes/stalls when trying to redraw the first version, but not the object one.

The lesson here is that python has slow lists. Powerful, but slow, compared to C.

0 Likes

(dudecon) #8

IanC:
Can I see the code you used to do the object link-move-join-unlink? I’m doing something very similar with my Auto Masonry script, and I’d like to optimize it as much as possible. Thanks!

0 Likes

(IanC) #9

I’ll dig it out, hopefully I saved it. I must warn you though, it is horrible…

Edit- I can’t find it, sorry. I’ll try and knock up an example later. Basically you want to create new objects, then use firstobject.join([list of other objects]). All objects must be part of the scene, hence the linking, then unlinking/removing (to clear away the clutter).

Be careful though, the way the data is copied or linked is important, because you can end up editing all objects, and with a lot more than you expected (if b and c are copies of a, and you join b to a, c will look like the new a)

0 Likes