1
$\begingroup$

Here are our atoms displayed with Ovito and with Matplotlib via Python (click for full size):

some atoms displayed in Ovito some atoms displayed with matplotlib

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:

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:

Blender display

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
$\endgroup$

1 Answer 1

1
$\begingroup$

You almost have it.

import bpy 

# get the object
obj = bpy.context.object
# get the object material slots
slots = obj.material_slots.data


# set the active material slot to the first one (or whichever)
slots.active_material_index = 0

# get your material data-block
mat = bpy.data.materials['MyMaterial']

# set the objects active material to that material data block.
slots.active_material = mat
```
$\endgroup$
5
  • $\begingroup$ I'm trying to incorporate this abstracted solution into my script. I've used the following but so far no change. slots = red_atoms.material_slots.data and slots.active_material_index = 0 and slots.active_material = red_diffuse so while this is potentially helpful to some future readers, it's not yet working for me. $\endgroup$
    – uhoh
    Commented Jul 21, 2022 at 14:25
  • $\begingroup$ Can you share your .blend file? Post it into your original post, and make sure text file with the script is saved in there. $\endgroup$
    – Jakemoyo
    Commented Jul 21, 2022 at 14:37
  • $\begingroup$ .blend file added, Note that the script as written will want to read the text files that I've linked to in pastebin (which Is why I posted as script + links) but I think you can work around that now that the objects are created. $\endgroup$
    – uhoh
    Commented Jul 21, 2022 at 20:23
  • 1
    $\begingroup$ @uhoh The material you see is the material on the atom that particles are stamped out from, so that's the mesh you need to attach the material to: bpy.data.meshes[atom_name[0]].materials.append(red_diffuse), etc. $\endgroup$
    – scurest
    Commented Jul 21, 2022 at 23:43
  • 1
    $\begingroup$ Excellent! bpy.data.meshes['Red_atom'].materials.append(red_diffuse) works. My coworker has addressed the shadows, so I'll remove that from the question and accept this answer. $\endgroup$
    – uhoh
    Commented Jul 22, 2022 at 1:44

You must log in to answer this question.

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