1
$\begingroup$

I'd like to be able to generate a bunch of primitive objects. All of them will be different sizes and have a material out of a random set.

What I'm having trouble with is the following:

  • Creating 20,000 primitive objects quickly
  • Creating a set of different materials where I can assign the primitive objects to

Here is my code so far:

import bpy, random
from random import randint

#Short hands for common calls
scene = bpy.context.scene
selected = bpy.context.selected_objects
mesh = bpy.ops.mesh
context = bpy.context
object = bpy.ops.object
selected_object = bpy.context.active_object

clouds = 500

def baseObject(x,y,z):
    #Creates base cylinder
    mesh.primitive_cylinder_add(location=(x,y,z))
    return

def clearScene():
    #Clears the current scene
    for selectedObj in bpy.context.scene.objects:
        selectedObj.select= True
        object.delete()
    return

def ObjectCreation(clouds):
    for attempts in range(clouds):
        x = round(random.uniform(-4.0, 4.0), 10)
        y = round(random.uniform(-4.0, 4.0), 10)
        z = round(random.uniform(-4.0, 4.0), 10)
        baseObject(x,y,z)
        context.object.scale[0] = x/10   #X Scale
        context.object.scale[1] = y/10   #Y Scale
        context.object.scale[2] = z/10   #Z Scale

        materialCreation()
    return

def materialCreation():
    #for attempts in range(scale):
    randomRed = random.uniform(0.0, 1.0)    #Used for random color scale, need to adjust later for color mapping
    randomGreen = random.uniform(0.0, 1.0)
    randomBlue = random.uniform(0.0, 1.0)

    material = bpy.data.materials.new("Material")    #Material properties
    material.alpha = round(random.uniform(0.1, 1.0), 10)    #Opacity
    material.use_transparency = True
    context.object.show_transparent = True
    context.object.data.materials.append(material)  #Applies material properties
    context.object.color = (randomRed, randomGreen, randomBlue, 1)  #Changes objects color(RGB Opacity)
    context.object.active_material.use_object_color = True
    return

clearScene()
ObjectCreation(clouds)
$\endgroup$
5
  • $\begingroup$ Adding 20,000 cubes will not be quick. $\endgroup$
    – David
    Commented Jun 15, 2016 at 21:18
  • $\begingroup$ Is there a way to optimize it? $\endgroup$
    – John Pham
    Commented Jun 15, 2016 at 21:30
  • $\begingroup$ Is every single object unique? Or are you going to have a bunch of different cubes, toruses, cones, etc. $\endgroup$ Commented Jun 15, 2016 at 23:33
  • $\begingroup$ For now, it would be a bunch of cylinders with different sizes, positions, and materials. $\endgroup$
    – John Pham
    Commented Jun 16, 2016 at 0:04
  • $\begingroup$ You could always link some of them instead of making separate objects -- probably save some memory. $\endgroup$ Commented Jun 16, 2016 at 0:53

1 Answer 1

5
$\begingroup$

Instead of using the operator to create the mesh each time you can use a copy and link it to the scene. Try and avoid high amounts of operator calls Python performance with Blender operators . They have the tendency to bloat out because of the undo stack, for one thing.

The operator is called once to create the primitive, then a copy is used for each iteration.

Created a list of 10 random materials. Each object mesh will have one randomly selected material from the list.

import bpy, random
from random import randint

context = bpy.context
scene = context.scene

def base_object():
    #Creates base cylinder
    bpy.ops.mesh.primitive_cylinder_add()
    return

def clear_scene(scene):
    #Clears the current scene
    for obj in scene.objects:
        scene.objects.unlink(obj)
        bpy.data.objects.remove(obj)
    return

def object_creation(scene, clouds):
    base_object()
    obj = scene.objects.active
    mesh = obj.data
    nummats = 10
    random_mats = create_material_list(nummats) # create 10 random materials
    for attempts in range(clouds):
        x = round(random.uniform(-4.0, 4.0), 10)
        y = round(random.uniform(-4.0, 4.0), 10)
        z = round(random.uniform(-4.0, 4.0), 10)
        obj.location = (x, y, z)
        obj.scale = 0.1 * obj.location

        red = random.uniform(0.0, 1.0)    #Used for random color scale, need to adjust later for color mapping
        green = random.uniform(0.0, 1.0)
        blue = random.uniform(0.0, 1.0)        
        obj.show_transparent = True
        obj.active_material = random_mats[random.randint(0, nummats-1)] # random pick of random mats

        obj.color = (red, green, blue, 1)  #Changes objects color(RGB Opacity)

        # copy the next one
        obj = obj.copy()
        obj.data = mesh.copy() # a new mesh for each object.
        scene.objects.link(obj) # link it to the scene
    return

def create_material_list(len):
    #for attempts in range(scale):
    mats = []
    for i in range(len):
        material = bpy.data.materials.new("Material")    #Material properties
        material.alpha = round(random.uniform(0.1, 1.0), 10)    #Opacity
        material.use_transparency = True
        material.use_object_color = True
        mats.append(material)

    return mats

clear_scene(scene)
object_creation(scene, 1000)

A similar question answer How can I create many objects quickly?

Might also want to check this out https://blender.stackexchange.com/a/42232/15543

If you stick with CamelCase for class names you will get better formatting.

$\endgroup$
2
  • $\begingroup$ Using your object_creation results in creating 2 objects at a time but it slows down the overall process. I tested with 50 objects with the old function which ran for 6 seconds compared to yours which ran for 2.39 seconds. Your function does have a linear speed compared to mine which drops the longer it runs which might be caused by memory. $\endgroup$
    – John Pham
    Commented Jun 16, 2016 at 23:39
  • $\begingroup$ And for 1000 objects? $\endgroup$
    – batFINGER
    Commented Jun 17, 2016 at 15:58

You must log in to answer this question.

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