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

|improve this answer|||||
$\endgroup$
  • $\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 Jun 16 '16 at 23:39
  • $\begingroup$ And for 1000 objects? $\endgroup$ – batFINGER Jun 17 '16 at 15:58

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.