Here are our atoms displayed with Ovito and with Matplotlib via Python (click for full size):
We can read the text files and build the atom arrays, but our attempt to create diffuse red and blue materials and add them to the atoms has failed. While the atoms are solid and cast shadows on each other, they don't seem to have taken their assigned colors.
Question: How can this script be adjusted so that we can see and render the atoms in color? For now, we only need to see it rendered in a window to demonstrate that Blender will do what we need.
The script reads the positions from these two files:
- red.txt https://pastebin.com/t0vu7dVQ
- blue.txt https://pastebin.com/K1L2LaLS
The attempt to add a diffuse material is copied (approximately) from https://vividfax.github.io/2021/01/14/blender-materials.html
What we have so far:
Blend:
Script:
import numpy as np
import bpy
import bmesh
#remove beginning object
for obj in bpy.data.collections:
bpy.ops.object.delete(use_global=False)
#remove data block
for block in bpy.data.meshes:
if block.users == 0:
bpy.data.meshes.remove(block)
for block in bpy.data.materials:
if block.users == 0:
bpy.data.materials.remove(block)
for block in bpy.data.textures:
if block.users == 0:
bpy.data.textures.remove(block)
for block in bpy.data.images:
if block.users == 0:
bpy.data.images.remove(block)
for block in bpy.data.particles:
if block.users == 0:
bpy.data.particles.remove(block)
for block in bpy.data.objects:
if block.users == 0:
bpy.data.objects.remove(block)
with open('red.txt', 'r') as infile: # https://pastebin.com/t0vu7dVQ
lines = infile.readlines()
red_array = np.array([[float(val) for val in line.split()] for line in lines])
with open('blue.txt', 'r') as infile: # https://pastebin.com/K1L2LaLS
lines = infile.readlines()
blue_array = np.array([[float(val) for val in line.split()] for line in lines])
print('red_array read with shape: ', red_array.shape)
print('blue_array read with shape: ', blue_array.shape)
collection_name = ['Red', 'Blue']
mesh_name = []
object_name = []
atom_name = []
particlesettings = []
for i in collection_name:
mesh_name.append(str(i)+'_mesh')
object_name.append(str(i)+'_object')
atom_name.append(str(i)+'_atom')
particlesettings.append(str(i)+'_particlesetting')
def make_lattice(array, n, d):
# make mesh
vertices = array
edges = []
faces = []
new_mesh = bpy.data.meshes.new(mesh_name[n])
new_mesh.from_pydata(vertices, edges, faces)
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(object_name[n], new_mesh)
# make collection
new_collection = bpy.data.collections.new(collection_name[n])
bpy.context.scene.collection.children.link(new_collection)
# add object to scene collection
new_collection.objects.link(new_object)
# Create an empty mesh and the object.
mesh = bpy.data.meshes.new(atom_name[n])
basic_sphere = bpy.data.objects.new(atom_name[n], mesh)
# Add the object into the scene.
bpy.context.collection.objects.link(basic_sphere)
# Select the newly created object
bpy.context.view_layer.objects.active = basic_sphere
basic_sphere.select_set(True)
# Construct the bmesh sphere and assign it to the blender mesh.
bm = bmesh.new()
bmesh.ops.create_uvsphere(bm, u_segments = 32, v_segments = 16, diameter = d)
bm.to_mesh(mesh)
bm.free()
bpy.ops.object.modifier_add(type='SUBSURF')
bpy.ops.object.shade_smooth()
#setting particle on frame
bpy.context.view_layer.objects.active = bpy.data.objects[object_name[n]]
bpy.ops.object.particle_system_add()
#bpy.context.object.particle_systems["ParticleSettings"].name = particlesettings[n]
bpy.data.particles["ParticleSettings"].name = particlesettings[n]
bpy.data.particles[particlesettings[n]].type = 'HAIR'
bpy.data.particles[particlesettings[n]].particle_size = 0.25
bpy.data.particles[particlesettings[n]].count = array.shape[0]
bpy.data.particles[particlesettings[n]].emit_from = 'VERT'
bpy.data.particles[particlesettings[n]].use_emit_random = False
bpy.data.particles[particlesettings[n]].render_type = 'OBJECT'
bpy.data.particles[particlesettings[n]].instance_object = bpy.data.objects[atom_name[n]]
#hide original atom
bpy.data.objects[atom_name[n]].select_set(True)
for o in bpy.context.selected_objects: o.hide_set(True)
#hide the render
bpy.data.objects[atom_name[n]].hide_render = True
return bpy.context.object
red_atoms = make_lattice(red_array, 0, 0.5)
blue_atoms = make_lattice(blue_array, 1, 1.0)
def make_mat(matid, type, rgb):
# from https://vividfax.github.io/2021/01/14/blender-materials.html
mat = bpy.data.materials.get(matid)
if mat is None:
mat = bpy.data.materials.new(name=matid)
mat.use_nodes = True
if mat.node_tree:
mat.node_tree.links.clear()
mat.node_tree.nodes.clear()
nodes = mat.node_tree.nodes
links = mat.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
r, g, b = rgb
if type == "diffuse":
shader = nodes.new(type='ShaderNodeBsdfDiffuse')
nodes["Diffuse BSDF"].inputs[0].default_value = (r, g, b, 1)
elif type == "emission":
shader = nodes.new(type='ShaderNodeEmission')
nodes["Emission"].inputs[0].default_value = (r, g, b, 1)
nodes["Emission"].inputs[1].default_value = 1
elif type == "glossy":
shader = nodes.new(type='ShaderNodeBsdfGlossy')
nodes["Glossy BSDF"].inputs[0].default_value = (r, g, b, 1)
nodes["Glossy BSDF"].inputs[1].default_value = 0
else:
print('uhoh! type not supported')
links.new(shader.outputs[0], output.inputs[0])
return mat
if True:
red_diffuse = make_mat(matid='red_diffuse', type='diffuse', rgb=(1, 0, 0))
blue_diffuse = make_mat(matid='blue_diffuse', type='diffuse', rgb=(0, 0, 1))
red_atoms.data.materials.append(red_diffuse)
blue_atoms.data.materials.append(blue_diffuse)
red_atoms.active_material = red_diffuse
blue_atoms.active_material = blue_diffuse
bpy.ops.object.light_add(type='SUN', align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
bpy.context.object.data.energy = 10