import bpy
import os
import sys
Argument parsing assumes this file was called from terminal in this format:
./blender --background --python ~/Code/blender_code.py -- arg1 arg2 arg3
"--" is Blender specific for passing in arguments (i.e. arg1, arg2, etc.) to Blender python scripts
https://blender.stackexchange.com/questions/6817/how-to-pass-command-line-arguments-to-a-blender-python-script
args = sys.argv[sys.argv.index("--") + 1:]
print("ARGS:",args)
INPUT_PATH = args[0]
OUTPUT_PATH = args[1]
RESOLUTION = int(args[2])
print('Input Path:', INPUT_PATH)
print('Output Path:', OUTPUT_PATH)
print('Resolution:', RESOLUTION)
print()
input_path = INPUT_PATH
export_filepath = OUTPUT_PATH
delete the default cube at blender startup
objs = bpy.data.objects
if 'Cube' in objs:
objs.remove(objs["Cube"])
Load obj
imported_object = bpy.ops.import_scene.obj(filepath=input_path)
obj = bpy.context.selected_objects[0]
print('Imported name: ', obj.name)
objs[obj.name].select_set(True) # obj should already be selected, but just in case
sets our mesh to be active
bpy.context.view_layer.objects.active = objs[obj.name]
bpy.ops.object.mode_set(mode='EDIT') # Go to Edit mode
new_uv_map_name = 'UVMap_2'
obj.data.uv_layers.new(name=new_uv_map_name) # create and name new UV Map
Set new UV map to active
bpy.data.meshes[obj.name].uv_layers[new_uv_map_name].active = True
UV Unwrap the mesh textures
bpy.ops.uv.smart_project(angle_limit=1.15192, island_margin=0.001)
Working in shader view now...
obj = bpy.context.active_object
You can choose your texture size (This will be the baked image)
image_name = obj.name + '_BakedTexture'
img = bpy.data.images.new(image_name, RESOLUTION, RESOLUTION)
texture_node_name = 'Bake_node'
old_uv_map_node_name = 'UV1_node'
new_uv_map_node_name = 'UV2_node'
print("Creating nodes for each material...")
for mat in obj.data.materials:
node_tree = mat.node_tree
nodes = node_tree.nodes
orig_img_tex_node = nodes['Image Texture']
# Creates uv map node in shader view and sets its uv map to current uv map
uv_map_node = nodes.new('ShaderNodeUVMap')
uv_map_node.name = old_uv_map_node_name
uv_map_node.uv_map = obj.data.uv_layers[0].name
node_tree.links.new(uv_map_node.outputs['UV'], orig_img_tex_node.inputs['Vector'])
# Create image texture node in shader view and sets its image
texture_node = nodes.new('ShaderNodeTexImage') # Creates node in shader view
texture_node.name = texture_node_name
texture_node.select = True
nodes.active = texture_node
texture_node.image = img #Assign the image to the node
# Creates another uv map node in shader view and sets its uv map to new uv map
new_uv_map_node = nodes.new('ShaderNodeUVMap')
new_uv_map_node.name = new_uv_map_node_name
new_uv_map_node.uv_map = obj.data.uv_layers[new_uv_map_name].name
node_tree.links.new(new_uv_map_node.outputs['UV'], texture_node.inputs['Vector'])
# Make texture_node the only one selected. Make it active, as required for baking
for n in nodes:
n.select = False
nodes[texture_node.name].select = True
nodes.active = nodes[texture_node.name]
Set render engine to cycles and set settings
This changes things in the Render Properties tab on the right-hand-side (Camera icon)
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 10
bpy.context.scene.cycles.preview_samples = 10 # Takes a few seconds to update GUI
bpy.context.scene.cycles.bake_type = 'DIFFUSE'
bpy.context.scene.render.bake.use_pass_direct = False
bpy.context.scene.render.bake.use_pass_indirect = False
print("Baking...")
bpy.ops.object.bake(type='DIFFUSE', save_mode='EXTERNAL', filepath='/home/nitz/Downloads/8_8_22')
This tells Blender what textures belong to the model during export
In shader view, for each material, link new texture image node to BDSF node
And delete the original image texture and old uv map nodes
for mat in obj.data.materials:
node_tree = mat.node_tree
nodes = node_tree.nodes
texture_node = nodes[texture_node_name]
old_texture_node = nodes['Image Texture']
old_uv_map_node = nodes[old_uv_map_node_name]
BSDF_node = nodes['Principled BSDF']
# Link new texture node to model
node_tree.links.new(texture_node.outputs['Color'], BSDF_node.inputs['Base Color'])
# Delete old texture and uv map nodes
nodes.remove(old_texture_node)
nodes.remove(old_uv_map_node)
Remove old UV Map layer, which essentially makes the new UV Map used during export
uv_textures = obj.data.uv_layers
uv_textures.remove(uv_textures['UVMap'])
output_dir = os.path.dirname(export_filepath)
print("OUTPUT PATH:", output_dir)
if not os.path.exists(output_dir):
os.mkdir(output_dir)
Textures will be saved to path where blender file is saved to.
The saving of the blend file has no other purpose. File is deleted.
blend_file_name = 'temp.blend'
blend_file_path = os.path.join(output_dir, blend_file_name)
bpy.ops.wm.save_as_mainfile(filepath=blend_file_path)
Packing and unpacking is used for exported files to include textures
bpy.data.images[image_name].pack()
bpy.ops.file.unpack_all(method='USE_ORIGINAL') # USE_ORIGINAL saves textures in curr dir
bpy.ops.export_scene.obj(filepath=export_filepath, path_mode='RELATIVE')
os.remove(blend_file_path) # remove temp blend file
print('Exported to:', export_filepath)
print('Finished! \nBlender quitting...')