top of page

How Blender Scripting Saves Hours: Automating 3D Asset Exports and Thumbnails

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.



A grid view of multiple file icons displayed on a computer screen, each representing different 3D assets labeled with letters, numbers, and symbols in various styles and colors.
Here’s what Blender scripting helped us batch export— no manual exporting required 🎉


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


 

Screenshot showing a Blender interface with a custom Python script on the right-hand side and a 3D model on the left. The script exports all 3D assets with colliders for use in Decentraland. Blue highlighted text in the script emphasizes the lines related to collider export. Labels in the image point to 'All the assets' on the left and 'Script to export all assets with colliders' on the right.
Batch export individual assets with colliders using this Blender script.

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.



Blender interface with a script rendering thumbnails for individual assets.
Automating thumbnail creation for each asset with Blender scripting.


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



A screen filled with many files, each representing letters and numbers in different colors and styles, neatly organized in rows.
Batch exporting and automatic thumbnail creation in action! Much quicker than doing it manually.
 

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 cube
import bpy
import os
# Export to blend file location
basedir = 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_layer
obj_active = view_layer.objects.active
selection = bpy.context.selected_objects
bpy.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 object
view_layer.objects.active = obj_active
# Reselect the original selection
for obj in selection:
    obj.select_set(True)


  • Script 2: Thumbnail rendering for each asset in PNG format.




import bpy
import os
# Define the resolution and file format for the thumbnails
thumbnail_size = 512
output_format = 'PNG'  # You can adjust this if needed
# Export to blend file location
basedir = os.path.dirname(bpy.data.filepath)
if not basedir:
    raise Exception("Blend file is not saved")
# Set render settings for thumbnails
bpy.context.scene.render.image_settings.file_format = output_format
bpy.context.scene.render.image_settings.color_mode = 'RGBA'  # RGBA for transparency
bpy.context.scene.render.resolution_x = thumbnail_size
bpy.context.scene.render.resolution_y = thumbnail_size
bpy.context.scene.render.film_transparent = True  # Make background transparent
bpy.context.scene.render.resolution_percentage = 100
# Add a sun lamp to the scene
def 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 object
def 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 one
def hide_other_objects(exclude_object):
    for obj in bpy.context.scene.objects:
        if obj != exclude_object:
            obj.hide_render = True
# Restore visibility for all objects
def show_all_objects():
    for obj in bpy.context.scene.objects:
        obj.hide_render = False
# Get the view layer and selected objects
view_layer = bpy.context.view_layer
selection = bpy.context.selected_objects
for 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 selection
for obj in selection:
    obj.select_set(True)

תגובות


bottom of page