Skip to content

Python driven Blender

Nicolas Cottaris edited this page Oct 30, 2013 · 66 revisions

This is a guide on procedural scene generation using the Blender-Python API that ships with Blender.

Step 0: Setting up.

To craft a scene using Python scripts, it is imperative to start Blender from within a terminal. This is the only way that you can see error messages generated by Blender's engine and any output generated by your print statements. In Mac OSX, generate a file called startBlender.command and enter the following into it.

#!/bin/sh
/Applications/Blender/blender.app/Contents/MacOS/blender

Next, go to Finder and double click on the startBlender.command icon. Blender should launch together with a Terminal.app.

In Blender, click on the Screen Layout button (button to the right of Help) in the Information viewport, and select the Scripting mode. This will open the following viewports: Text Editor, Python Console and 3D View.

In the Text Editor viewport, click on Text -> Create Text Block (or alternatively, press Control+N). This will create an empty document. Enter the following print command,

print('Hello from the Blender-Python API !');

then hit the Run Script button. You should see Hello from the Blender-Python API ! in the Terminal that launched with Blender. Great!

We will now proceed with the procedural generation of a scene using the Blender-Python API (bpy) module. To ease this process, we have created a Python module (called SceneUtilsV1.py) which contain methods for generating, configuring and positioning various scene components. Using this module the user can experience the power of procedurally-generated scenes without having to first learn the intricacies of the bpy module. More experienced users are welcome to modify and extend the supplied module to fit their needs.

Download the SceneUtilsV1.py module. In the following, it is assumed that the SceneUtilsV1.py module exists at /Users/Shared/PythonDrivenBlender/Toolboxes. It is also assumed that an empty directory named ColladaExports exists at /Users/Shared/PythonDrivenBlender. Let's begin.

Step 1: Importing the necessary modules.

In the Text Editor viewport, create a new Text Block and type the following:

# Import the Blender-Python API module
import bpy
# Import necessary system modules
import sys
import imp
# Import necessary math functions from the math and mathutils modules
from math import pi, floor
from mathutils import Vector

# Set the root, toolboxes, and exports directories
rootDirectory    = '/Users/Shared/PythonDrivenBlender';  
toolboxDirectory = '{}/Toolboxes'.format(rootDirectory);
exportsDirectory = '{}/ColladaExports'.format(rootDirectory);

# Append the toolboxDirectory to the Blender scripts path
sys.path.append(toolboxDirectory);
 
# Import our custom scene toolbox module
import SceneUtilsV1;
imp.reload(SceneUtilsV1);

# Notify user that all is good so far.
print('All ok with the imports')

Hit Text->Save as and save this file as SceneTutorial.py. Then hit the Run Script button. Check the terminal. If all the paths have been set up correctly you should see All ok with the imports. If you see an Error: message, check your paths.

Step 2: Initializing the scene.

In the Text Editor viewport, add the following:

# Generate a dictionary with various scene initialization parameters
params = { 'name'               : 'CheckerShadowScene',  # name of new scene
           'erasePreviousScene' : True,                  # erase existing scene
           'sceneWidthInPixels' : 1024,                  # 1920 pixels along the horizontal-dimension
           'sceneHeightInPixels': 768,                   # 1200 pixels along the vertical-dimension
           'sceneUnitScale'     : 1.0/100.0,             # set unit scale to 1.0 cm
           'sceneGridSpacing'   : 10.0/100.0,            # set the spacing between grid lines to 10 cm
           'sceneGridLinesNum'  : 20,                    # display 20 grid lines
         };
# Instantiate a sceneManager object with the above params
scene = SceneUtilsV1.sceneManager(params);

Save the code. Before re-running the script, note that the 3D view contains Blender's stock scene. Now hit the Run Script button. Note that the 3D viewport is now empty, only displaying a 20x20 spatial grid.

The above code block, instantiates a sceneManager object. The sceneManager class is defined in the SceneUtilsV1 module, and contains several utility methods for managing a Blender scene. This particular initialization, asks to erase all components of the previous scene, sets the size of the rendered image to 1024x768 (this can be changed in RenderToolbox3), specifies a metric coordinate system in which units are 1 cm, and sets a 20x20 display grid with a 10 cm line separation.

Check the terminal. You should see no errors, and a bunch of messages, like so:

Erasing previous scene components
Unlinking object "Camera", from old scene ("Scene")
Unlinking object "Cube", from old scene ("Scene")
Unlinking object "Lamp", from old scene ("Scene")
Clearing all users for mesh "Cube"
Removing mesh "Cube", from old scene ("Scene")
Clearing all users for lamp "Lamp"
Removing lamp "Lamp", from old scene ("Scene")
Clearing all users for camera "Camera"
Removing camera "Camera", from old scene ("Scene")
Clearing all users for material "Material"
Removing material "Material", from old scene ("Scene")
Removing object "Camera", from old scene ("Scene")
Removing object "Cube", from old scene ("Scene")
Removing object "Lamp", from old scene ("Scene")

These are generated by the constructor method of the sceneManager object as it erases the contents of the previous scene.

Step 3: Generating materials for different scene components.

In the Text Editor viewport, add the following:

# Generate the materials
# 1. Generate dictionary with specs for the cylinder material. These can be changed in RenderToolbox3.
params = { 'name'              : 'cylinderMaterial',           # tag with which RenderToolbox3 can access this material
           'diffuse_shader'    : 'LAMBERT',
           'diffuse_intensity' : 0.5,
           'diffuse_color'     : Vector((0.0, 1.0, 0.0)),
           'specular_shader'   : 'WARDISO',
           'specular_intensity': 0.1,
           'specular_color'    : Vector((0.0, 1.0, 0.0)),
           'alpha'             : 1.0
         }; 
cylinderMaterialType = scene.generateMaterialType(params);

# 2. Generate dictionary with specs for the room (walls) material. These can be changed in RenderToolbox3.
params = { 'name'              : 'roomMaterial',              # tag with which RenderToolbox3 can access this material
           'diffuse_shader'    : 'LAMBERT',
           'diffuse_intensity' : 0.5,
           'diffuse_color'     : Vector((0.6, 0.6, 0.6)),
           'specular_shader'   : 'WARDISO',
           'specular_intensity': 0.0,
           'specular_color'    : Vector((1.0, 1.0, 1.0)),
           'alpha'             : 1.0
         }; 
roomMaterialType = scene.generateMaterialType(params);

# 3. Generate dictionary with specs for the cloth material. These can be changed in RenderToolbox3.
params = { 'name'              : 'clothMaterial',            # tag with which RenderToolbox3 can access this material
           'diffuse_shader'    : 'LAMBERT',
           'diffuse_intensity' : 0.5,
           'diffuse_color'     : Vector((0.7, 0.0, 0.1)),
           'specular_shader'   : 'WARDISO',
           'specular_intensity': 0.0,
           'specular_color'    : Vector((0.7, 0.0, 0.1)),
           'alpha'             : 1.0
         }; 
clothMaterialType = scene.generateMaterialType(params);

# 4. Generate dictionary with specs for the dark check material. These can be changed in RenderToolbox3.
params = { 'name'              : 'darkCheckMaterial',        # tag with which RenderToolbox3 can access this material
           'diffuse_shader'    : 'LAMBERT',
           'diffuse_intensity' : 0.1,
           'diffuse_color'     : Vector((0.5, 0.5, 0.5)),
           'specular_shader'   : 'WARDISO',
           'specular_intensity': 0.0,
           'specular_color'    : Vector((0.5, 0.5, 0.5)),
           'alpha'             : 1.0
         }; 
darkCheckMaterialType = scene.generateMaterialType(params);

# 5. Generate dictionary with specs for the light check material. These can be changed in RenderToolbox3.
params = { 'name'              : 'lightCheckMaterial',       # tag with which RenderToolbox3 can access this material
           'diffuse_shader'    : 'LAMBERT',
           'diffuse_intensity' : 0.7,
           'diffuse_color'     : Vector((0.55, 0.55, 0.40)),
           'specular_shader'   : 'WARDISO',
           'specular_intensity': 0.0,
           'specular_color'    : Vector((0.5, 0.5, 0.5)),
           'alpha'             : 1.0
         }; 
lightCheckMaterialType = scene.generateMaterialType(params);

# Generate list of materials
checkBoardMaterialsList = [lightCheckMaterialType, darkCheckMaterialType];

Save the text and hit the Run Script button. The above code block, generates a bunch of different materials for the various scene components. Check the Terminal. There should be no error messages and no other additional messages. Also there should be no changes in the 3D viewport.

Step 4:Adding a lamp.

In the Text Editor viewport, add the following:

# Generate an area lamp model
params = {'name'           : 'areaLampModel',    # tag with which RenderToolbox3 can access this lamp model
          'color'          : Vector((1,1,1)),    # white color
          'fallOffDistance': 120,                # distance at which intensity falls to 0.5 
          'width1'         : 20,                 # width of the area lamp
          'width2'         : 15                  # height of the area lamp
         }
brightLight100 = scene.generateAreaLampType(params);
 
# Position of lamp
leftAreaLampPosition = Vector((
                            -56,      # horizontal position (x-coord)
                             56,      # depth position (y-coord)
                             16       # elevation (z-coord)
                           ));
# Point at which the lamp is directed to
leftAreaLampLooksAt = Vector((
                            -71,          # horizontal position (x-coord)
                             71,          # depth position (y-coord)
                             20           # elevation (z-coord)
                           ));
# Generate dictionary containing the lamp name and model, its location and direction
params = { 'name'     : 'leftAreaLamp',   # tag with which RenderToolbox3 can access this lamp object
           'model'    : brightLight100, 
           'showName' : True, 
           'location' : leftAreaLampPosition, 
           'lookAt'   : leftAreaLampLooksAt
         };
# Add the lamp to the scene
leftAreaLamp = scene.addLampObject(params);

The above code block, generates an area lamp model with a desired size, intensity drop-off, and color, and adds an instance of this lamp to the scene. The lamp is added at a specified location and is directed at a specified direction. Save the text and hit the Run Script button. Note that a lamp has been added to the scene at the desired location.

Step 5:Adding a second lamp.

In the Text Editor viewport, add the following:

# Position of lamp
frontAreaLampPosition = Vector((
                              0,      # horizontal position (x-coord)
                            -50,      # depth position (y-coord)
                             50       # elevation (z-coord)
                             ));
# Point at which the lamp is directed to
frontAreaLampLooksAt = Vector((
                              0,      # horizontal position (x-coord)
                            -90,      # depth position (y-coord)
                             90       # elevation (z-coord)
                             ));
# Generate dictionary containing the lamp name and model, its location and direction
params = { 'name'     : 'frontAreaLamp',      # tag with which RenderToolbox3 can access this lamp object
           'model'    : brightLight100, 
           'showName' : True, 
           'location' : frontAreaLampPosition, 
           'lookAt'   : frontAreaLampLooksAt
          };
# Add the lamp to the scene
frontAreaLamp = scene.addLampObject(params);

Clone this wiki locally