Animating ddg Objects

This guide explains how to animate ddg objects (and their visualizations in Blender) by introducing parameters that can be accessed and changed via Blender’s GUI. Animations can easily be created by adding keyframes to the parameters.

Warning

Modifying objects with Blender properties is still work in progress. If you find unexpected behaviour, try restarting Blender and/or write a mail to our Support.

Adding Parameters with a Callback Function

The most central tool for creating animations is the function add_props_with_callback(). You pass the labels and initial values of the parameters as well as a callback function in the function call. The callback function will be executed each time one of the parameters changes its value. The parameters, called Blender properties, can now be accessed via Blender’s GUI.

Here is a basic example:

from ddg.visualization.blender import props


def print_props(a, b, c, s):
    print(f"{a, b, c, s = }")


props.add_props_with_callback(
    print_props,
    ("a", "b", "c", "s"),  # labels for the properties
    -1.0,  # initial parameters
    1.0,
    0.5,
    -1,
)

Note

The labels “a”, “b”, “c” and “s” of the parameters can be chosen freely. The callback function will be called with the parameters as arguments in given order.

The type of the initial parameters determines the type of the parameters, e.g. “0” will result in int, “0.0” in float.

The example creates the properties a, b, c and s. In Blender’s 3D Viewport you can click on the little arrow to the right side of the coordinate system to open the sidebar. Alternatively you can also press ‘n’. In addition to the Item, Tool and View tabs, you now also find a DDG tab that stores the Blender properties.

../../../_images/animations01.png

If we adjust any of them, the callback function print_props will be executed, and will print the properties values to the console.

../../../_images/animations02.png

Note

The properties need to reside on some object inside of Blender. We store them on the scene, which can be obtained with bpy.context.scene. This is important to know if you want to access these properties via the API (e.g. bpy.context.scene.a) for example when animating them.

Parameter dependent ddg Objects

For more interesting examples involving geometric objects, we can replace print_props by a function that creates Blender objects.

Note

This is the most rudimentary way and does not involve any caching. There are more sophisticated ways of creating callback functions, using a caching mechanism, explained in the next section. This section will help to understand whats going on, especially if you are just starting to work with animations.

The following function creates and visualizes a quadric depending on parameters.

import bpy
import numpy as np

import ddg
from ddg.visualization.blender import props

def create_quadric(a, b, c, s):
    """
    Creates a quadric from the given parameters.
    """
    q = ddg.geometry.quadrics.Quadric(np.diag([a, b, c, s]))
    return q


def visualize_quadric(a, b, c, s):
    """
    Converts a quadric to a Blender object
    and links this to bpy.context.scene (per default).
    Clears all meshes beforehand.
    """
    ddg.visualization.blender.mesh.clear()
    q = create_quadric(a, b, c, s)
    q_bobj = ddg.to_blender_object_helper(q, sampling=[0.1, 50, "c"], name="Quadric")
    return [q_bobj]


We add the visualization function as a callback for the Blender properties a, b, c and s. Changing the parameters then will change the form and the type of the visualized quadric.

props.add_props_with_callback(
    visualize_quadric, ("a", "b", "c", "s"), -1.0, 1.0, 0.5, -1.0
)
../../../_images/animations03.png

Warning

Be aware, that when executing the script, i.e. by pressing ‘Alt + P’, no output is expected. Only when changing a parameter the visualize_quadric function is called and executed.

Warning

If there is no activity, even when changing the parameters, check the console you have started Blender with. Error messages will appear there without being shown in Blender.

Caching Manipulated Objects

There are two utility functions for caching manipulated objects, hide_callback() and overwrite_callback(). For both caching mechanisms we need to adapt the visualization function to return a sequence of unlinked Blender objects. This means that we need to pass the keyword link=False to to_blender_object() for each object.

def create_quadric(a, b, c, s):
    """
    Creates a quadric from the given parameters.
    """
    q = ddg.geometry.quadrics.Quadric(np.diag([a, b, c, s]))
    return q


def visualize_quadric(a, b, c, s):
    """
    Converts a quadric to a Blender object
    and does not link it to any collection.
    No mesh clearing required.
    """
    q = create_quadric(a, b, c, s)
    q_bobj = ddg.to_blender_object_helper(
        q, sampling=[0.1, 50, "c"], name="Quadric", link=False
    )
    return [q_bobj]


The hide_callback() and the overwrite_callback() function expect a name of a collection and a function returning the unlinked Blender objects. Their specific usage is explained below.

Warning

The functions hide_callback() and overwrite_callback() use custom Blender collections to store the objects. It is not compatible with user defined collections. In other words, you can not pass a collection argument to to_blender_object().

Hide Callback

The function hide_callback() creates a Blender collection with passed name. For each distinct set of parameters a child collection is appended (on demand). If a parameter is changed, the previous collection, and therefore its objects, are hidden. Then either a new child collection is created (for a new set of parameters) or a previously created child collection is un-hidden (if the set of parameters has been used before).

callback = props.hide_callback("modified objects", visualize_quadric)
props.add_props_with_callback(
    callback,
    ("a", "b", "c"),
    -1.0,
    1.0,
    0.5,
    s=-1.0,
)
../../../_images/animations_hide.png

Warning

For smaller scripts using the same values of parameters (e.g. animations) hiding and un-hiding is a very fast and cheap option. For large scripts this method results in lots of objects which might be difficult to be handled by Blender. Blender might have difficulties handling many objects when rendering.

Overwrite Callback

If all objects to visualize will have mesh data (i.e. no curves, lights and cameras, no 1D nets for example coming from lines, conics, etc), it is also possible to overwrite the object’s data in-place. This can be done using the overwrite_callback(), which also caches results.

callback = props.overwrite_callback("modified objects", visualize_quadric)
props.add_props_with_callback(
    callback,
    ("a", "b", "c"),
    -1.0,
    1.0,
    0.5,
    s=-1.0,
)
../../../_images/animations_overwrite.png

Clear Callback

There also exists a function clear_callback() that does not cache any data. It can be used in a similar way as the functions above.

Finer Control over Properties

If you want to set the description, minimum or maximum value of a property and more, you’ll have to define the properties yourself:

from functools import partial

props.add_props_with_callback_from_constructors(
    callback,
    {
        "a": partial(bpy.props.FloatProperty, default=-1.0, min=-1.0, max=5.0),
        "b": partial(bpy.props.FloatProperty, default=1.0),
        "c": partial(bpy.props.FloatProperty, default=0.5),
    },
    {"s": partial(bpy.props.IntProperty, default=-1)},
)

Consult the Blender documentation for all the options. The callback code remains the same.

Saving and Reloading .blend Files

Since closing Blender loses all Python state, the callback will no longer exist after saving and reloading a .blend file. To fix this, run the script again. Afterwards the callback will work as expected.

Creating an Animation

Like any Blender properties, DDG properties can be keyframed and interpolated. In particular, the library’s tools for keyframes can be applied, with the slight catch that we store the interactive properties on Blender’s scene, as mentioned above.

To animate the parameter a we already added in the example above, we could do:

from ddg.visualization.blender import animation

SCENE = bpy.context.scene
FPS = 24
animation.set_keyframe(SCENE, 0 * FPS, "a", -1.0)
animation.set_keyframe(SCENE, 3 * FPS, "a", 1.0)

For a more detailed guide on setting and editing keyframes, see Blender Keyframes.

When playing animations multiple times in Blender, the functions hide_callback() or overwrite_callback() are recommended to cache the results. When creating (and rendering) an animation from script, i.e. only playing it once, the functions clear_callback() or overwrite_callback() are recommended.

To find out more about rendering animations check out our Rendering guide.