How Blender Scripting Saves Hours: Automating 3D Asset Exports and Thumbnails
- KJWalker
- Sep 25, 2024
- 4 min read
Blender scripting can seem intimidating at first, but once you start exploring it, the possibilities for saving time and improving results are endless. Recently, I’ve been diving into scripting while working on a 3D UI project for Decentraland’s In World Builder, and I’d like to share how it made my workflow smoother and more efficient. If you’re looking to automate tedious tasks and spend more time on the creative side of things, this one’s for you.

If you're completely new to Blender or Scripting, take a look at this post first:

The Situation: For this particular project, I was tasked with creating letters, numbers, and symbols for the In World Builder in Decentraland. Each character had different fonts and colorways, which resulted in a massive set of over 1600 files. As you can imagine, manually exporting each asset would’ve been a time-consuming nightmare.
Enter Blender Scripting: I knew there had to be a better way to handle the exporting process. So, I wrote a Blender script to export each asset individually, using the object’s name to keep things organized. On top of that, the script automatically added custom colliders for each asset to ensure they worked smoothly in Decentraland.
Once the exporting was done, I realized I needed thumbnails for each asset too. Instead of manually rendering 1600 images (no, thank you!), I wrote another script. This one created a thumbnail render for each individual asset and exported it as a PNG file with the same name.

Not only did these scripts save me an incredible amount of time, but they also ensured consistency across all the assets.

Learning as You Go: The best part about Blender scripting? You don’t have to be an expert to make it work for you. I’m far from a scripting guru, but by experimenting and problem-solving, I was able to drastically improve my workflow. The key is staying curious and being willing to learn—scripting is just another tool in your creative toolbox.
Ready to Try? If you’re interested in trying these scripts yourself, I’ve included them below. Feel free to tweak, adjust, and experiment! And if you have any questions, drop a comment or reach out—I’m always happy to chat about ways to work smarter in Blender.
Let’s keep learning together. What tasks have you automated in Blender, or what processes do you think could benefit from scripting? Share your ideas, and let’s see where this methodology can take us next.
Scripts:
Script 1: Automating asset export with custom colliders.
# Exports each selected object into its own file with an additional collider cubeimport bpyimport os# Export to blend file locationbasedir = os.path.dirname(bpy.data.filepath)if not basedir: raise Exception("Blend file is not saved")# Define your custom set name here if needed# set_name = "YourSetName"view_layer = bpy.context.view_layerobj_active = view_layer.objects.activeselection = bpy.context.selected_objectsbpy.ops.object.select_all(action='DESELECT')for obj in selection: # Select the object obj.select_set(True) # Set the active object for some exporters view_layer.objects.active = obj # Get the object name and file path for export object_name = bpy.path.clean_name(obj.name) # If you want to add a set name prefix, uncomment the next line: # full_name = f"{set_name}_{object_name}" fn = os.path.join(basedir, object_name) # Create a simple cube for the collider bpy.ops.mesh.primitive_cube_add(size=0.25) collider = bpy.context.object collider.name = object_name + "_collider" collider.display_type = 'WIRE' # Position the collider at the object's origin collider.location = (obj.location.x, obj.location.y, obj.location.z + 0.15) # Parent the collider to the object collider.parent = obj # Select both the object and the collider for export collider.select_set(True) obj.select_set(True) # Export to GLB try: bpy.ops.export_scene.gltf(filepath=fn + ".glb", use_selection=True) except AttributeError: print("Error: glTF 2.0 Export Add-on is not enabled.") break # Deselect the objects collider.select_set(False) obj.select_set(False) # Delete the collider after export bpy.data.objects.remove(collider) print("Written:", fn + ".glb")# Restore the previous active objectview_layer.objects.active = obj_active# Reselect the original selectionfor obj in selection: obj.select_set(True)Script 2: Thumbnail rendering for each asset in PNG format.
import bpyimport os# Define the resolution and file format for the thumbnailsthumbnail_size = 512output_format = 'PNG' # You can adjust this if needed# Export to blend file locationbasedir = os.path.dirname(bpy.data.filepath)if not basedir: raise Exception("Blend file is not saved")# Set render settings for thumbnailsbpy.context.scene.render.image_settings.file_format = output_formatbpy.context.scene.render.image_settings.color_mode = 'RGBA' # RGBA for transparencybpy.context.scene.render.resolution_x = thumbnail_sizebpy.context.scene.render.resolution_y = thumbnail_sizebpy.context.scene.render.film_transparent = True # Make background transparentbpy.context.scene.render.resolution_percentage = 100# Add a sun lamp to the scenedef add_sunlight(): bpy.ops.object.light_add(type='SUN', align='WORLD', location=(10, -10, 10)) sun = bpy.context.object sun.data.energy = 5 # Adjust light intensity if needed sun.rotation_euler = (0.7854, 0, 0.7854) # Rotate sun for better illumination return sun# Set up the camera closer to the objectdef setup_camera(): bpy.ops.object.camera_add(location=(0, -0.36, 0.195)) # Closer camera camera = bpy.context.object camera.rotation_euler = (1.3, 0, 0) # Adjust camera angle bpy.context.scene.camera = camera return camera# Hide all objects except the current onedef hide_other_objects(exclude_object): for obj in bpy.context.scene.objects: if obj != exclude_object: obj.hide_render = True# Restore visibility for all objectsdef show_all_objects(): for obj in bpy.context.scene.objects: obj.hide_render = False# Get the view layer and selected objectsview_layer = bpy.context.view_layerselection = bpy.context.selected_objectsfor obj in selection: # Deselect all and select the current object bpy.ops.object.select_all(action='DESELECT') obj.select_set(True) view_layer.objects.active = obj # Hide all other objects from render hide_other_objects(obj) # Set up camera to frame the object camera = setup_camera() # Align the camera to fit the object (tightly frame the asset) bpy.ops.view3d.camera_to_view_selected() # Add sunlight for better illumination sun = add_sunlight() # Set the filename to the object name object_name = bpy.path.clean_name(obj.name) output_path = os.path.join(basedir, object_name + ".png") # Render the thumbnail and save the image bpy.context.scene.render.filepath = output_path bpy.ops.render.render(write_still=True) # Clean up by removing the camera and sun after render bpy.data.objects.remove(camera) bpy.data.objects.remove(sun) # Show all objects again show_all_objects() print("Thumbnail created:", output_path)# Reselect the original selectionfor obj in selection: obj.select_set(True)



Comments