Introduction
The Omegalib Houdini Engine module may be used to import Houdini digital assets, hosted by the Houdini Engine, into an Omegalib visualisation. Often, these digital assets expose parameters which can be modified in order to manipulate properties of the underlying asset geometry while a visualisation is running.
One of the examples included in the Omegalib tutorial series explains how to use the provided daHandles library to implement on-screen controls. The daHandles module can also be combined with daHEngine and used to attach on-screen controls to exposed parameters on a Houdini digital asset.
In this tutorial, we will demonstrate how to attach an on-screen control provided by daHandles to an exposed Houdini digital asset parameter. The control will allow us to directly manipulate the value of the underlying parameter, while the Houdini Engine will evaluate and apply any necessary updates to the associated asset. The daHEngine module will allow us to view the results of these interactive updates within Omegalib.
Example
In this example, we use a handle to adjust the length of a 1D axis. The axis is implemented as a Houdini digital asset which provides a single parameter len that specifies the length of the axis. Take a look at the video shown below, to see the example in action:
As in our earlier example, the handle which may be used to adjust the length parameter is hidden by default. Use the mouse cursor to hover over the axis geometry and click to display the handle. Click on the handle and drag the mouse from side to side in order to increase or decrease the length of the axis. To hide the handle once you have finished interacting with the axis, click on the background of the scene.
Let’s now take a look at the Omegalib script which was used to implement this functionality. You can find a copy of this script (parameter.py) in the examples directory on the DAVM under /local/examples/handles:
import os
from cyclops import *
from omegaToolkit import *
from daInput import *
from daHEngine import *
from daHandles import *
if __name__ == '__main__':
"""
This example demonstrates how to set up a scene containing houdini engine
objects with parameters which may be manipulated using on-screen handles.
It shows how to use various features which are provided by the daHandles and
daHEngine omegalib modules, including:
- How to use the Houdini Engine to load an OTL into an omegalib scene
- How to use the Houdini Engine to access parameters exported by an OTL
- How to attach controls to the parameters and manipulate their values
"""
path = os.path.dirname(__file__)
if not path:
path = os.getcwd()
resources = os.path.join(path, 'resources')
ui_context = UiContext()
getDefaultCamera().setControllerEnabled(False)
houdini = HoudiniEngine.createAndInitialize()
houdini.setLoggingEnabled(False)
houdini.loadAssetLibraryFromFile(os.path.join(resources, 'otl', 'axisA1.otl'))
houdini.instantiateAsset('Object/axisA1')
houdini.instantiateGeometry('axisA11')
parameters = HoudiniParameter.load_parameters(houdini, 'Object/axisA1')
geometry_builder = SphereControlGeometryBuilder()
control_builder = HoudiniParameterControlBuilder()
control_builder.set_geometry_builder(geometry_builder)
control_builder.set_control_parameter(parameters['len'])
control_builder.set_control_rate_limiter(RateLimiter(10))
slider_builder = HoudiniParameterSliderControlGroupBuilder()
slider_builder.set_ui_context(ui_context)
slider_builder.set_control_builder(control_builder)
slider_builder.set_axis(Axis.X_AXIS)
axis = ControllableSceneNode('axis', StaticObject.create('axisA11'))
axis.add_control(slider_builder.set_parent(axis).build())
axis.node.setPosition(-1, 2, -10)
axis.node.rotate(Vector3(0, 1, 0), math.radians(-45), Space.Local)
light = Light.create()
light.setPosition(0, 4, 0)
manager = SelectionManager(ui_context)
manager.add(axis)
def on_event():
manager.on_event()
setEventFunction(on_event)
You may have noticed that this script exhibits some similarities to the earlier Omegalib on-screen handles example. Once again, let’s break this script down and look at each part in turn.
As before, we declare a UiContext
instance to manage the default mouse pointer and we disable the navigation controls on the default camera, as this example also requires a fixed camera:
ui_context = UiContext()
getDefaultCamera().setControllerEnabled(False)
Next, we create and initialize a connection to the Houdini Engine (make sure an instance of the Houdini Engine HARS service is up and running, as described in the getting started guide). Once this is done, we then use the Houdini Engine to load the digital asset containing the 1D axis we wish to manipulate and instantiate an instance within the Omegalib scene:
houdini = HoudiniEngine.createAndInitialize()
houdini.setLoggingEnabled(False)
houdini.loadAssetLibraryFromFile(os.path.join(resources, 'otl', 'axisA1.otl'))
houdini.instantiateAsset('Object/axisA1')
houdini.instantiateGeometry('axisA11')
It is then possible to load any parameters exposed by the asset:
parameters = HoudiniParameter.load_parameters(houdini, 'Object/axisA1')
Now that we have access to the asset parameter(s), we need to set up the control which we will use to connect to the parameter and manipulate it. In this example, we make use of a sphere as the visual representation of our control within the scene. To create this geometry, we use the built-in SphereControlGeometryBuilder
. This builder provides access to a number of different parameters which may be used to customise the appearance of the sphere, however in this case we just accept all of the defaults:
geometry_builder = SphereControlGeometryBuilder()
The geometry builder is now passed into a HoudiniParameterControlBuilder
, which creates our control and connects it to the specified parameter. We provide this builder with access to the Houdini parameter we wish to attach to (len) and an update rate limiter. The rate limiter is important in order to ensure that the performance of the visualisation remains interactive. When the mouse cursor moves, Omegalib generates an update event for each tiny increment in the mouse pointer’s motion. If we were to evaluate every single one of these tiny updates and apply them to the connected parameter, the performance of our visualisation would suffer as the Houdini Engine is constantly instructed to update and rebuild the affected asset. Instead, we restrict the rate of these updates to every 10th event emitted by Omegalib during the mouse pointer’s motion. This ensures that the performance of the visualisation is maintained at an acceptable level:
control_builder = HoudiniParameterControlBuilder()
control_builder.set_geometry_builder(geometry_builder)
control_builder.set_control_parameter(parameters['len'])
control_builder.set_control_rate_limiter(RateLimiter(10))
We only require a single individual control to interact with the axis length parameter, so in this example we will make use of a “slider” control group which manages a single control and allows us to increase or decrease its value by sliding the mouse from side to side:
slider_builder = HoudiniParameterSliderControlGroupBuilder()
slider_builder.set_ui_context(ui_context)
slider_builder.set_control_builder(control_builder)
slider_builder.set_axis(Axis.X_AXIS)
Now that the control builders have been defined, we are able to start making use of them. In this example, we don’t need to define the geometry that we wish to interact with, as this has been provided for us by the Houdini Engine. We do need to attach this geometry to a ControllableSceneNode
however, and provide this node with access to our control group. The daHEngine plugin registered the geometry of the axis asset with Omegalib when we called the instantiateGeometry
method earlier in our script. This allows us to reference the axis geometry by name and pass it into the scene node as shown below:
axis = ControllableSceneNode('axis', StaticObject.create('axisA11'))
axis.add_control(single_builder.set_parent(axis).build())
axis.node.setPosition(-1, 2, -10)
axis.node.rotate(Vector3(0, 1, 0), math.radians(-45), Space.Local)
As usual, we add a light to illuminate the scene, allowing us to see the objects we have just defined (along with their controls, when selected):
light = Light.create()
light.setPosition(0, 4, 0)
And lastly, Once again, the final step is to define a SelectionManager
. The manager keeps track of all of the controllable objects in the scene, the cursors which have been defined, and which object (if any) has been selected by each of the cursors. We add the controllable scene node to the manager so it is aware that it exists, and then register the manager’s on_event
method as a callback so that Omegalib will invoke it in response to any input events generated by attached user input devices:
manager = SelectionManager(ui_context)
manager.add(axis)
def on_event():
manager.on_event()
setEventFunction(on_event)
Where to Next?
If you have a copy of the DAVM installed, you can try the example out for yourself by running the following commands in a terminal window (note: this example relies on Houdini, so make sure you’ve worked through our getting started guide to set up Houdini and provide the DAVM with access to it first):
$ cd /local/examples/handles
$ orun parameter.py
Some possible future enhancements you may like to try to implement include:
- Creating a Houdini digital asset of your own, and using daHandles to connect to and manipulate its parameter(s)
- Developing a daHandle control which allows you to choose and apply a selection from a multi-choice parameter to a Houdini asset
- Writing a framework (perhaps as an extension to daHandles or as a module of your own), which is able to interrogate a Houdini asset and automatically generate and attach suitable handles based on the type(s) of the parameter(s) exposed