Difference between revisions of "Scripting Blender"

From PioneerWiki
Jump to: navigation, search
m (Using a loop)
 
(17 intermediate revisions by the same user not shown)
Line 11: Line 11:
 
=== Set up Blender ===
 
=== Set up Blender ===
  
* Switch to 'Scripting' mode at the top, and in the Text pane's file widget hit 'New'. This is where our script will go. Type
+
* You may need to install Python and sort out environment variables first. I'm assuming if Blender works, Python works, but haven't tested on Windows...
 +
* Back in Blender, switch to 'Scripting' screen layout at the top, and in the Text pane's file widget hit 'New'. This is where our script will go. Type
  
 
  import bpy
 
  import bpy
  
as the first line - that's the Blender PYthon module.
+
as the first line - that's the Blender PYthon API, the functions we use to tell it what operations to perform.
  
=== Perform the operations ===
+
=== Perform the operations, like recording a macro ===
  
Now we manually perform the operations we want the script to automate, to get an idea of the underlying python functions the interface uses. The arguments and output will appear in the top console pane. So, when I duplicate the starter box, move it ten units forwards on Y, then shrink it to .9 on X and Z, I get:
+
Now we manually perform the operations we want the script to automate, to get an idea of the underlying Python functions the interface uses. The arguments and output will appear in the top console pane. So, when I duplicate the starter box, move it ten units forwards on Y, then shrink it to .9 on X and Z, I get:
  
 
  bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False),  
 
  bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False),  
  "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0),  
+
  "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST',
"snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
+
"snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
  
 
  bpy.ops.transform.resize(value=(0.9, 1, 1), constraint_axis=(True, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH',  
 
  bpy.ops.transform.resize(value=(0.9, 1, 1), constraint_axis=(True, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH',  
Line 33: Line 34:
 
=== Test ===
 
=== Test ===
  
Copy those three statements from the top pane by right-clicking on each, then hitting ctrl-c. Paste them with ctrl-v just after the 'import bpy' statement. Congratulations, you've just written a Blender Python Script.
+
Copy those three statements from the top pane by right-clicking on each, then hitting ctrl-c. Paste them with ctrl-v just after the 'import bpy' statement. Congratulations, you've just written a Blender Python script :-)
  
Hit 'Run script' below and you'll note that it all works, apart from failing to move the object along Y. By looking at the 0.9 arguments to the resizes, we can work out that the second dimension represents Y (figures... X, Y and Z), so we try putting 10 in as the second argument to the duplicate_move function. Test it again, and it works!
+
Hit 'Run script' below and you'll note that it works, apart from failing to move the object along Y. By looking at the 0.9 arguments to the resizes, we can work out that the second dimension represents Y (figures... X, Y and Z), so we try putting (0,10,0) in as the first part of the second argument of the duplicate_move function. We also reason that the second transform_resize statement is redundant - shrinking with different function calls for each axis might not do any harm, but it's needlessly messy. A little experimentation establishes that as long as we set the constraint_axis arguments to false, we can feed in values for any axis. Test it again, and it works!
  
 
  <nowiki>
 
  <nowiki>
Line 41: Line 42:
  
 
bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 10, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
 
bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 10, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
bpy.ops.transform.resize(value=(0.9, 1, 1), constraint_axis=(True, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
+
bpy.ops.transform.resize(value=(0.9, 1, 0.9), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
bpy.ops.transform.resize(value=(1, 1, 0.9), constraint_axis=(False, False, True), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
 
 
</nowiki>
 
</nowiki>
  
Through a similar process - working out what the interface is doing when you click the buttons, then sticking it together into primitive scripts - you'll soon be Blending much faster.
+
=== Using a loop ===
  
== Tutorials ==
+
If you want to do something a fixed number of times, it's straightforward with a 'For loop'. A little googling on Python For loops should find you some tutorials, but this is the basic setup; be aware that Python is sensitive to whitespace. 'i' is an iterator, and will go through each value in the range 1 - 10. You can use it in expressions, like "i * 15 - 100", to generate other values.
  
 +
<nowiki>
 +
import bpy
 +
 +
for i in range (1, 10):
 +
    bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 10, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
 +
    bpy.ops.transform.resize(value=(0.9, 1, 0.9), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
 +
</nowiki>
 +
 +
== Moving on... ==
 +
 +
Through a similar process - working out what the interface is doing when you click the buttons, then sticking it together into primitive scripts, and a bit of fiddling to get it going - you'll soon be Blending much faster.
 +
 +
== Links and tutorials ==
 +
 +
* http://www.blender.org/documentation/blender_python_api_2_65a_release/ - the 2.65 API manual
 
* http://cgcookie.com/blender/2011/08/26/introduction-to-scripting-with-python-in-blender/ - skip ~27 minutes
 
* http://cgcookie.com/blender/2011/08/26/introduction-to-scripting-with-python-in-blender/ - skip ~27 minutes
 
* http://www.youtube.com/watch?v=1OePNW34-z4
 
* http://www.youtube.com/watch?v=1OePNW34-z4
 
* https://www.youtube.com/watch?v=ALfl4tebiQM&list=PLMYtDzby1wdY3NMM07u9tV0eCJ1HM-llM
 
* https://www.youtube.com/watch?v=ALfl4tebiQM&list=PLMYtDzby1wdY3NMM07u9tV0eCJ1HM-llM
 +
 +
[[Category:Blender]]

Latest revision as of 09:55, 15 March 2013

Why bother?

It's actually really easy, and the kind of geometrically regular 'box modelling' building a spaceship entails is well suited to scripting.

Next time you find yourself repeatedly doing the same sequence of operations to lots of different meshes or objects, consider writing a script. It may take a bit longer the first time, but once you know how you'll be much more efficient.

Walk through

We're going to write a script (more of a macro really) which takes the selected object, duplicates it, moves one duplicate 10 units forward on one axis, then shrinks it 10% on the other two axes.

Set up Blender

  • You may need to install Python and sort out environment variables first. I'm assuming if Blender works, Python works, but haven't tested on Windows...
  • Back in Blender, switch to 'Scripting' screen layout at the top, and in the Text pane's file widget hit 'New'. This is where our script will go. Type
import bpy

as the first line - that's the Blender PYthon API, the functions we use to tell it what operations to perform.

Perform the operations, like recording a macro

Now we manually perform the operations we want the script to automate, to get an idea of the underlying Python functions the interface uses. The arguments and output will appear in the top console pane. So, when I duplicate the starter box, move it ten units forwards on Y, then shrink it to .9 on X and Z, I get:

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False), 
"constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST',
"snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
bpy.ops.transform.resize(value=(0.9, 1, 1), constraint_axis=(True, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', 
proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
bpy.ops.transform.resize(value=(1, 1, 0.9), constraint_axis=(False, False, True), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', 
proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)

Test

Copy those three statements from the top pane by right-clicking on each, then hitting ctrl-c. Paste them with ctrl-v just after the 'import bpy' statement. Congratulations, you've just written a Blender Python script :-)

Hit 'Run script' below and you'll note that it works, apart from failing to move the object along Y. By looking at the 0.9 arguments to the resizes, we can work out that the second dimension represents Y (figures... X, Y and Z), so we try putting (0,10,0) in as the first part of the second argument of the duplicate_move function. We also reason that the second transform_resize statement is redundant - shrinking with different function calls for each axis might not do any harm, but it's needlessly messy. A little experimentation establishes that as long as we set the constraint_axis arguments to false, we can feed in values for any axis. Test it again, and it works!

import bpy

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 10, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
bpy.ops.transform.resize(value=(0.9, 1, 0.9), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)

Using a loop

If you want to do something a fixed number of times, it's straightforward with a 'For loop'. A little googling on Python For loops should find you some tutorials, but this is the basic setup; be aware that Python is sensitive to whitespace. 'i' is an iterator, and will go through each value in the range 1 - 10. You can use it in expressions, like "i * 15 - 100", to generate other values.

import bpy

for i in range (1, 10):
    bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 10, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
    bpy.ops.transform.resize(value=(0.9, 1, 0.9), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)

Moving on...

Through a similar process - working out what the interface is doing when you click the buttons, then sticking it together into primitive scripts, and a bit of fiddling to get it going - you'll soon be Blending much faster.

Links and tutorials