Please note, that I read about an issue with calling bpy.ops.* because they may update the whole scene after they apply - and that update takes long time…especially when there are 10.000 spheres to update.
This is why there is only one single bpy.ops. call in my script.
#!/usr/bin/env python
import bpy
from math import sin,cos,sqrt
import json
import os, time
PATH = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(PATH, "atoms.json")) as in_file:
atom_data = json.load(in_file)
###############################################################
def draw_substrate():
scale = 1 #scales atomic radius by scale - does not change lattice constant
a = 0.255 #lattice constant in nm
a1 = a * 3.20432 # ####3,240692139
#correct spacings for correct nearest neighbour distance to be a
# This is for hexagonal lattice (111) for fcc lattice constant
dx=a1*cos(30)*1.0115836 # *1.0115836 to get nearest neighbour to 0.255
dy=a1*sin(30)/sqrt(3)* 0.947583 # ------------ " --------------
d= (a1/sqrt(3)) * scale / 7 # original layer spacing
#Iteration index, width of substrate drawn
n = 120
layers = 3 ### number of layers to draw ... >8gb for 3rd layer with n=50...
smooth = True
join = True
link_to_scene = True
shapes = []
verbose = True
verbose2 = False
# Add atom primitive
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.mesh.primitive_uv_sphere_add()
sphere = bpy.context.object
sphere.dimensions = [atom_data["Cu"]["radius"]* scale] * 3
key = "Cu1"
bpy.data.materials.new(name=key)
bpy.data.materials[key].diffuse_color = (1, 0.638152, 0.252242) #light brown
bpy.data.materials[key].diffuse_intensity = 0.7
bpy.data.materials[key].specular_intensity = 0.2
key = "Cu2"
bpy.data.materials.new(name=key)
bpy.data.materials[key].diffuse_color = (0.281, 0.183, 0.076) #darker brown
bpy.data.materials[key].diffuse_intensity = 0.5
bpy.data.materials[key].specular_intensity = 0
bpy.data.materials[key].use_shadeless = True
key = "Cu3"
bpy.data.materials.new(name=key)
bpy.data.materials[key].diffuse_color = (0.073, 0.049, 0.022) #dark brown
bpy.data.materials[key].diffuse_intensity = 0.2
bpy.data.materials[key].specular_intensity = 0
bpy.data.materials[key].use_shadeless = True
###############################################################################################
print("Drawing layer 1:")
layer_start_time = time.time()
for i in range(n):
if verbose: print("row: ", i, " of ", n)
row_start_time = time.time()
for j in range(n):
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx,j*dy,0)
atom_sphere.active_material = bpy.data.materials["Cu1"]
shapes.append(atom_sphere)
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx+dx,j*dy+dy/2,0)
atom_sphere.active_material = bpy.data.materials["Cu1"]
bpy.context.scene.objects.link(atom_sphere)
shapes.append(atom_sphere)
if verbose2: print("Atom: ",j)
row_run_time = round((time.time() - row_start_time),2)
if verbose: print("Time for row: ", i, " in layer I is ", row_run_time)
layer_runtime = round((time.time() - layer_start_time),2)
print("--- Runtime: ", layer_runtime, " seconds --- for first layer")
if layers > 1: # build second layer
print("Drawing layer 2:")
layer_start_time = time.time()
for i in range(n):
if verbose: print("row: ", i, " of ", n)
row_start_time = time.time()
for j in range(n):
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx+dx,j*dy+dy/6,-d)
atom_sphere.active_material = bpy.data.materials["Cu2"]
shapes.append(atom_sphere)
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx+dx+dx,j*dy+dy/2+dy/6,-d)
atom_sphere.active_material = bpy.data.materials["Cu2"]
shapes.append(atom_sphere)
if verbose2: print("Atom: ",j)
row_run_time = round((time.time() - row_start_time),2)
if verbose: print("Time for row: ", i, " in layer II is ", row_run_time)
layer_runtime = round((time.time() - layer_start_time),2)
print("--- Runtime: ", layer_runtime, " seconds --- for second layer")
if layers > 2: # build third layer
print("Drawing layer 2:")
layer_start_time = time.time()
for i in range(n):
if verbose: print("row: ", i, " of ", n)
row_start_time = time.time()
for j in range(n):
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx+dx+dx,j*dy+2*dy/6,-2*d)
atom_sphere.active_material = bpy.data.materials["Cu3"]
shapes.append(atom_sphere)
atom_sphere = sphere.copy()
atom_sphere.data = sphere.data.copy()
atom_sphere.location = (2*i*dx+dx+dx+dx,j*dy+dy/2+2*dy/6,-2*d)
atom_sphere.active_material = bpy.data.materials["Cu3"]
shapes.append(atom_sphere)
if verbose2: print("Atom: ",j)
if verbose:
row_run_time = round((time.time() - row_start_time),2)
print("Time for row: ", i, " in layer II is ", row_run_time)
layer_runtime = round((time.time() - layer_start_time),2)
print("--- Runtime: ", layer_runtime, " seconds --- for third layer")
if layers > 3: print("Choose 1-3 layers or modify code!")
# Smooth and join molecule shapes
if smooth:
print("smoothing...")
start_time = time.time()
for shape in shapes:
shape.select = True
bpy.context.scene.objects.active = shapes[0]
bpy.ops.object.shade_smooth()
bpy.ops.object.select_all(action='DESELECT')
runtime = round((time.time() - start_time),2)
print("--- Runtime: ", runtime, " seconds --- for smoothing")
if link_to_scene:
print("linking all shapes to scene")
start_time = time.time()
for shape in shapes:
bpy.context.scene.objects.link(shape)
bpy.ops.object.select_all(action='DESELECT')
runtime = round((time.time() - start_time),2)
print("--- Runtime: ", runtime, " seconds --- for linking to scene")
if join:
print("joining all shapes into one object")
start_time = time.time()
for shape in shapes:
shape.select = True
bpy.context.scene.objects.active = shapes[0]
bpy.ops.object.join()
bpy.ops.object.select_all(action='DESELECT')
runtime = round((time.time() - start_time),2)
print("--- Runtime: ", runtime, " seconds --- for joing into single object")
###############################################################
def add_light(tx, ty, tz, style):
scene = bpy.context.scene
lamp_data = bpy.data.lamps.new(name="New Lamp", type=style)
lamp_object = bpy.data.objects.new(name="New Lamp", object_data=lamp_data)
scene.objects.link(lamp_object)
lamp_object.location = (tx, ty, tz)
lamp_object.select = True
scene.objects.active = lamp_object
###############################################################
def clear_scene():
# If the starting cube is there, remove it
if "Cube" in bpy.data.objects.keys():
bpy.data.objects.get("Cube").select = True
if "Lamp" in bpy.data.objects.keys():
bpy.data.objects.get("Lamp").select = True
if "Camera" in bpy.data.objects.keys():
bpy.data.objects.get("Lamp").select = True
bpy.ops.object.delete()
###############################################################
def add_camera(tx,ty,tz,rx,ry,rz,label):
import bpy
fov = 25.0
pi = 3.14159265
scene = bpy.context.scene
camera_data = bpy.data.cameras.new(name=label)
camera_object = bpy.data.objects.new(name=label, object_data=camera_data)
scene.objects.link(camera_object)
camera_object.location = (tx, ty, tz)
camera_object.select = True
scene.objects.active = camera_object
###############################################################
# Runs the method
if __name__ == "__main__":
clear_scene()
print('draw substrate . . .')
draw_substrate()
print('add light 1')
add_light(2,-3,10,"HEMI")
print('add light 2')
add_light(0,0,12,"POINT")
print('add light 3')
add_light(0,-10,13,"POINT")
print('add light 4')
add_light(10,0,12,"POINT")
print('add light 5')
add_light(10,-10,12,"POINT")
print('add camera 1')
add_camera(3,-4,20,0,0,0,"cam1")
print('add camera 2')
add_camera(2,5,5,-90,180,0,"cam2")
print('update scene')
bpy.context.scene.update()