authors: Silvano Imboden, Paul McManus, Lorenzo Celli
VTKNodes is a Blender Addon providing the possibility to use VTK within Blender
The Visualization Toolkit (VTK https://www.vtk.org/ ) is an open source library for scientific visualization widely
adopted by researcher in fields such as geo-physics, fluid dynamic and biomedicine. VTK is developed
by Kitware, which is also the author of CMake, the build configurator used by Blender.
3DSlicer and Paraview ( https://www.kitware.com/platforms ) are some of the open source applications built on top of VTK.
Some example of Scientific Visualizations done with VTK:
In VTK the process of reading/transform/display data is accomplished by instantiating and connecting
among them a series of reader/filters/actors so it naturally fits the metaphor provided by the Blender Node Editor, but the implementation of a VTKNodes addon for Blender was not feasible until last year, when Kitware
finally provided the VTK bindings for Python3.x.
Purpose:
My personal interest in using VTK from the Blender user interface is aimed at prototyping VTK visualization networks and also to ease teaching and learning VTK. Beside my personal interest , VTK is lacking by design any editing tools, in the past people using VTK were implementing custom editing tools, but recently I saw an increasing number of people preferring to move their data in and out Blender to use the tools that Blender provides. Therefore I hope that VTKNodes may be helpful in creating new visualization-applications entirely within Blender.
Figure 1: An a simple example of a VTKNodes network that loads a CTScan data, extract two different Isosurfaces, and transform them to a Blender mesh.
Project Status at 27 July 2018
VTKNodes is not completed yet, but the work done so far is very promising.
At this moment we provided nodes for 741 classes out of 900.
We plan to release release a first version before the next Blender conference,
History of Development
the rest of this document provides full details on:
TODO:
Contacts:
Silvano Imboden s.imboden@cineca.it
My last goal will be to provide libraries ready to be used for the most common OS, for other platforms I may provide detailed information on how to compile your own version.
These are the steps that worked for me with VTK8 and Blender 2.79 on Ubuntu 16.04 LTS.
In short you have to compile using a Python version that matches the one in Blender, then compile VTK and its python bindings using the same version of Python, lastly you need to configure the environment so that the various libraries can be found from Blender.
find out which python version that blender is using
run blender, create a py-console,
>>> import sys
>>> sys.version
'3.5.3 (default, May 18 2017, 14:40:48) \n[GCC 5.4.1 20161019]'
download and compile python
> wget https://www.python.org/ftp/python/3.5.3/Python-3.5.3.tgz
> tar -xvzf Python-3.5.3.tgz
> cd python-sources
> ./configure --prefix=../install --with-computed-gotos --with-pymalloc [--enable-shared or --enable_framework on osx ]
the last three options should build python matching the way it is built for blender
> make -j 4
> make install
> cd ../install/bin
> python
Python 3.5.3 (default, Nov 3 2017, 17:31:29)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
download and compile vtk using the previous python
prepare the following folders:
xxx/VTK8-py353/src -- explode the vtk source here
xxx/VTK8-py353/build
xxx/VTK8-py353/install
run cmake-gui, and set src and build dir, press generate
set CMAKE_BUILD_TYPE = Release
set INSTALL_PREFIX= xxx/VTK8-py353/install
set module_vtkImagingOpenGL2 = on
set module_vtkPython = on
set module_vtkWrappingPythonCore= on
press generate
set PYTHON/PYTHON_INCLUDE_DIR = your_python_install_dir/include
set PYTHON/PYTHON_LIB_DIR = your_python_install_dir/lib
set VTK/VTK_PYTHON_VERSION = 3
set VTK/VTK_WRAP_PYTHON = on
> cp xxx/PY353/include/python3.5m/patchlevel.h xxx/PY353/install/include/patchlevel.h
press generate
> cd ../build
> make -j 8
> export PYTHONPATH= xxx/PY353/install/lib/python3.5:$PYTHONPATH
> export LD_LIBRARY_PATH= xxx/PY353/install/lib:$LD_LIBRARY_PATH
> make -j 8
> make install
configure the environment
> export PYTHONPATH= xxx/VTK/install/site-packages:$PYTHONPATH
> export LD_LIBRARY_PATH= xxx/VTK/install/lib:$LD_LIBRARY_PATH
test it
Run Blender, open the python console
> import vtk
> dir(vtk)
[ ‘DC’… ….. 'vtkZLibDataCompressor' ]
It is possible to have VTK drawing inside a Blender Viewport, instead or togetherwith Blender.
It is possible to have the VTKCamera synchronized with the Blender Camera.
My purpose for this was mostly to debug VTKNodes but it may be useful to provide some exotic feature of VTK like volume rendering. This feature is provided by a small Addon linked here which adds a VTKPanel to the 3DView with two checkboxes to turn on and off the VTK rendering in exclusive or overlayed mode.
Figure 2: the cone is drawn by VTK, the Cube is drawn by Blender, if you rotate the camera both objects rotate, if you move or resize the view everything works as expected. To the side there is the code that does the work.
This works with VTK OpenGL system 1.0 only ---- on Blender 2.8 we may be able to use a more recent version of OpenGL
Figure 3: some of the vtkData types
Figure 4: some of the most exotic VTK Source nodes
In VTK, apart from when you are using Volume Rendering, all types of data must be transformed to a vtkPolyData before being visualized. Transforming a vtkPolyData without attributes to a Blender Mesh is verysimple.
vtkData Attributes can be mappedto Vertex Data ( see later on )
The opposite conversion ( of static blender mesh ) is very straightforward.
Blender provide a template for custom nodes which is a good starting point, the few other things I needed I learned looking inside the “Animation Nodes”.
The unusual thing for me was that nodes are not object like you expect in Object Oriented Programming,
you can’t store or attach anything inside a Blender Node. The reason for this is that Blender provide you the possibility to define your custom kind of nodes, instantiate and connect them, and have your network saved and later restored from a Blender file, Blender will not being able to serialize your node network if it contains data defined outside the normal Blender data structure. Thus one way to execute a node-network is to read all the nodes just in time, understand what is needed to do and then do it, repeating this each time.
This approach is not the best for the VTKNodes, because we don’t want to recreate the whole VTK pipeline each time, re-executing any Reader and so on. So we need a cache system to store the underlying vtk-objects
once and then reuse them and the data inside them every time the user interacts with the node-network.
I ended-up creating a map local to any VtkNode network, indexed on the vtkNode names and containing its corresponding vtk-object. The map is populated when it is discovered that it is empty ( for example if you reload the addon code, the previous data will be lost, and there are no events that let you know about it )
As shown in figure 1, in a VTKNodes network the termination nodes have an “Update” button.
When you press the update button for the first time, all the incoming nodes are visited, their vtk-object are created and cached. When you press it a second time, all incoming nodes are visited, for each node property a matching method on the vtk-object is called, and for each node connection a correspondingconnection is made among the vtk-objects. In the end all the vtk-object are executed and their output is transformed to a Blender Mesh.
VTK ensures that when the user changes anything in the network, only the affected parts will be re-executed.
VTK provides more then 1000 classes, some of them are internal components and are not meant to be instantiated by the user, some are virtual and are not meant to be instantiated at all.
My first approach was to consider the following categories: Readers, Writers, Sources and Filters.
In the past finding these classes was straightforward, now all of these inherit from vtkAlgorithm, and you have to check at runtime if a class has inputs ( filters, writers, and other sinks ) or if it has outputs ( filter, readers,… )
Now I realized that I’ll need more classes, which are not strictly vtk-pipeline-elements but are used as pipeline parameters. Examples are ColorMaps, Integrators, ImplicitFunctions and so on. I haven't implemented support for these yet.
VTK is huge, therefore manually coding each node is not an option. Code for each node will be generated by some python script.
So I created a “fat” VTKNode-base-class and stored there most of the code needed : creating the object, synchronizing the vtk-object internal status with the corresponding Node properties, synchronizing vtk-object connections with node connections, executing the network.
Having a “fat” base class, simplify the coding needed in derived classes, which in the end only need to declare their properties ( name, type and default value of each property ).[a] the next figure shows the generated source code for the the vtkConeSource node class.
The next figure shows how the code is organized.
At the bottom there is the mentioned NodeBaseClass, the Yellow layer represent the generated code for the specific classes organized in modules by categories ( gen_sources, … ). These modules are imported by a second layer of modules ( sources, … ) that provide the possibility of overriding/customizing any of the generated classes if/when this is required. Finally, all the previous modules are imported by the Addon Main File which performs the nodes registrations in Blender.
The code generation exploits the strict method naming convention followed by VTK and the Python introspection features: A first python script enumerates the vtk classes and tries to instantiate them in turn, virtual classes not meant to be instantiated will raise an exception, classes inheriting vtkAlgorthm will be classified as sources, reader, writer, …. the other classes are ignored. For the interesting classes we now enumerate the methods and we try to match methods in pair as follows
If I find a method named SetXXX, I search for another method named GetXXX, if I find it I look at the method documentation which reports the type of the arguments, if the arguments match
( i.e. I found void SetXXX( int ) and int GetXXX() ) then we know that this object has a property named ‘XXX’
with type int and default value = GetXXX().
For int properties meant to be a boolean VTK provide also the method void SetXXXOn(), void SetXXXOff()
For int properties meant to be a combobox VTK also provides the methods
void SetXXXToYYY(), void SetXXXtoZZZ(), void SetXXXToWWW()
I am also using some heuristics to hide methods not meant to be exposed to node users, like Incr/DecrRefCount.
All the information is stored in a sqlite database that helps me to count and track properties analysis failures. Many of the failures are false alarms ( like finding a methods SetX( int, int ) when you also have SetX( int[2]) and int[2] GetX() ) other are genuine failures. I identified 10 types of weird signatures, and for the moment I can address most of them by manually override the generated code.
A second python scripts reads the information from the database and creates the specific node source code using a Jinja2 template system.
When VTKNodes processes a node network, the setter of each property will be called on each vtk-object.
Some methods are intended to be used in mutual exclusion, for example you can provide the vtkConeSource
with radius and height, or with radius and angle, but if you set all the three what will happen depends on the order of the calls. In these case it is better to forget one of the three properties, but which ?
The current code provide the user the possibility to show and hide any properties of each node type. These settings are stored Blender-wide so they will be preserved among different project or different Blender files.
This feature and the following were implemented by Lorenzo Celli.
for each vtkNode it is possibile to read the documentation of the corresponding vtk-class
VTKNodes provides features to inspect the status of each node, and of each node output.
Colors are important in Scientific Visualization.
Any numeric attribute of a VTK Data can be mapped to colors.
With the limitation of exporting a single user-chosen numeric attribute, the vtk_to_blender_node now stores the mesh attributes as vertex data, and it is possible possible to create a ColorTable node to control how values are mapped to colors.
an example showing attributes created by an elevation filter on a sphere
example of colored streamtubes ( the kitchenflow vtk-example recreated in blender )
Examples can obviously be shipped as blender files, but we were looking for something lighter so that we can distribute it within the addon, so Lorenzo provided an example machine: a network can be stored as a json file inside the addon folder, examples can be loaded from a menu in the user interface, examples can be organized in sections. Now we just need to make the examples :-)