I am trying to script a simple exporter that would help me get material data to a webGL engine from Blender 2.8. I am able to get default values of roughness, metallic and transparency. For now this is how it looks:
I am can’t find a way to check if the Principled BSDF has textures attached controlling this values and save their paths. How can I extend this script to get the texture information?
Hi @const, thank you for your answer! Unfortunatelly it doesn’t work on my side. Gives me an error:
KeyError : 'bpy_prop_collection[key]: key “Material” not found. Not sure how to interpret it.
Anyway I got some ideas from your code and tried to incorporate it into my script but with no success. Here is what I tried:
import bpy
materials_list = []
for mat in bpy.data.materials:
mat_dict = {}
prinBSDF = mat.node_tree.nodes["Principled BSDF"]
mat_dict["name"] = mat.name
if prinBSDF.inputs["Roughness"].type == 'TEX_IMAGE':
mat_dict["roughness"] = prinBSDF.inputs["Roughness"].image.filepath
else:
mat_dict["roughness"] = prinBSDF.inputs["Roughness"].default_value
materials_list.append(mat_dict)
print(materials_list)
but I get an error "AttributeError: ‘NodeSocketFloatFactor’ object has no attribute ‘image’.
Do you have any idea what is missing here? Or am I completely wrong about it?
Hi @const, thank you very much for your time! It all seems very logical for me but unfortunately doesn’t work. It still collects only default values and ignores the bitmaps. I checked a simplified version of the script to ignore default value and force writing textures and I get an error "AttributeError: ‘NodeSocketFloatFactor’ object has no attribute ‘image’ ". Do you have any idea how to move forward with it?
This time I tried to get access to the data types more properly. It would be fun though if Python allowed direct access, less code to type.
import bpy
def get_materials():
# a list of dictionaries
matlist = []
# iterating all of the materials
for mat in bpy.data.materials:
# get the bsdf node
bsdf = None
if mat.node_tree != None: # should have node tree
for n in mat.node_tree.nodes.keys(): # searching all nodes
node = mat.node_tree.nodes[n]
if node.type == 'BSDF_PRINCIPLED': # node type should be bdsf
bsdf = node # setting the node
break # exit node tree
# bsdf not found, skipping
if bsdf == None:
continue
# dictionary
d = {} # create a dictionary
d["name"] = mat.name # set material name
# get roughness socket
roughness = bsdf.inputs.get("Roughness")
# ensure input node is an image
if len(roughness.links) == 1 and roughness.links[0].from_node.type == 'TEX_IMAGE':
d["roughness"] = roughness.links[0].from_node.image.filepath
else:
d["roughness"] = roughness.default_value
# list
matlist.append(d) # appending to list
# return
return matlist
m = get_materials()
print(m)
I tried also some tests in various cases. With an image and then without one. You should see the same results.
I faced another challenge trying to collect information about lights in the scene. I can easily get all properties of the lights but can’t find a way to get location and rotation of all instances of the lights. How should I approach this?
For now this is how it looks like:
import bpy
import math
def get_lights():
lights_list = []
for light in bpy.data.lights:
light_dict = {}
light_dict['type'] = light.type
light_dict['name'] = light.name
if light.type == 'SPOT':
light_dict['angle'] = int(light.spot_size*180/math.pi)
if light.type == 'SPOT' or light.type == 'POINT':
light_dict['size'] = light.shadow_soft_size
light_dict['strength'] = light.energy
light_color = []
light_color.append(round(light.color.r, 2))
light_color.append(round(light.color.g, 2))
light_color.append(round(light.color.b, 2))
light_dict['color'] = light_color
lights_list.append(light_dict)
return lights_list
l = get_lights()
print(l)