Skip to content

Tutorial 4 Scripting

Louis Gagnon edited this page Feb 28, 2020 · 6 revisions

Scripting Blendyn

This is not intended to be a fully-fledged tutorial, but rather a very brief showcase of what can be accomplished by writing simple Python scripts that rely on the Blender and Blendyn APIs to automate repeated operations when importing MBDyn simulation results.

A lot of the times, especially when developing models, one finds themself doing the same stuff over and over re-importing the model every time something is changed in the MBDyn input file. I find myself over and over again starting from scratch, doing a Standard Import, selecting the nodes, scaling them, then selecting some elements, scaling them, etc...

So I decided to write a very simple script that helps me setup my model the way I want it to. Here it is:

import bpy
from mathutils import *
from math import *

# Import model
bpy.ops.blendyn.standard_import()

# Scale different elements
sN = 0.1
sB = 0.5
sSH = 0.3
# nodes
for node in bpy.context.scene.mbdyn.nodes:
    bpy.data.objects[node.blender_object].scale = Vector((sN, sN, sN))
# elements
for elem in bpy.context.scene.mbdyn.elems:
    if elem.type in ['body','clamp']:
        bpy.data.objects[elem.blender_object].scale = Vector((sB, sB, sB))
    elif elem.type == 'spherical_hinge':
        bpy.data.objects[elem.blender_object].scale = Vector((sSH, sSH, sSH))

# Assign section to beams
airfoil = 'NACA 23012 12%'
path = '/home/me/work/mbdyn/blender/models/naca_sections.blend/Object/'
bpy.ops.wm.link(directory = path, filename = airfoil)
naca23012 = bpy.data.objects[airfoil]
for elem in bpy.context.scene.mbdyn.elems:
    if elem.type == 'beam3':
        bpy.data.objects[elem.blender_object].data.bevel_object = naca23012

# Apply new dimensions to all objects
bpy.ops.object.select_all(action = 'SELECT')
bpy.ops.object.transform_apply(location = False, rotation = False, scale = True)
bpy.ops.object.select_all(action = 'DESELECT')

# Collapse the scene outline
bpy.ops.blendyn.outline_collapse()

I won't go through it here in detail, but I hope you can find it useful!

Hints and things to keep in mind

  1. the script applies to the Blender 2.8x version of the code, contained as of the time of writing (January 2020) in the branch develop. If you have an older version, please update Blendyn before trying to execute the script;
  2. Blender will print the error messages of Python scripts only in the system console, so it is a good idea to start Blender from there each time you are writing a new script;
  3. bpy.ops.blendyn. is the root of the Blendyn operators: as always, you can obtain a list of all the operators inside the Blendyn root typing it in the Blender Python console and then using AUTOCOMPLETE;
  4. the containers bpy.context.scene.mbdyn.nodes and bpy.context.scene.mbdyn.elems hold the information about, you guessed it, MBDyn nodes and elements, obtained parsing the .log file of the simulation output.

Another approach to scripting Blendyn is to save your Python file in the addons directory of your Blender installation. This allows you to quickly call the script from the Blender console without having to copy and paste your commands.

Let's say that you always want to load the same results file, standard import it, and animate it with a specific frequency. Then you could write a Python module file and save it in ~/.config/blender/2.81/scripts/addons/mbd.py

import bpy
from mathutils import *
from math import *

def new():
	bpy.ops.wm.open_mainfile(filepath="/your/blendyn/default/file.blend")


def imp():
	mbs=bpy.context.scene.mbdyn
	mbs.file_path='/home/your/path/to/results/'
	mbs.file_basename='resultsFileNameRoot'
	mbs.use_netcdf=1
	bpy.ops.blendyn.read_mbdyn_log_file()
	bpy.ops.blendyn.standard_import()
	mbs.load_frequency=1
	bpy.ops.blendyn.set_motion_paths()

You can then import it and run its function from Blender's Python console as such:

import mbd
mbd.imp()

Note, however, that the mbd.new() call or creating a new file will reset the imported modules and thus the "mbd" module must be newly imported for each new file.