Clarisse 5.0 SP8 SDK  5.0.5.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
The simplified Python API

Table of Contents

The simplified Python API is a set of Python functions defined in a file called clarisse_helper.py. These functions simplify rendering and saving images, project item management, selection manipulation etc... You can find this file in the python folder in the directory containing all Clarisse binaries.

These functions are implemented in Python directly using Clarisse C++ Python wrapped API. We highly recommend you to have a look at the file as reference if you wish to learn more about Clarisse C++ API. You can find the documentation of all the methods and classes it defines in the namespace section of the SDK documentation: clarisse_helper.

Note
The first time a python script is executed, clarisse_helper.py is automatically imported in the root of the ix namespace. Please, also note that this file can be modified if you wish to extend it with your own helpers. However, make sure to only extend the file without modifying existing methods: this would ultimately break existing built-in scripts that rely on it.

Working with the application selection

Clarisse selection is a set containing both contexts (OfContext) and objects (OfObject). The selection is nested in the application and in order to access it using the C++ API wrapped in Python you need to call:

1 ix.application.get_selection()

This method returns an AppSelection class which handles selection in Clarisse. This class is very extensive and can look too complicated to many script writers. This is why we've included a simplified one in clarisse_helper.py. To access the global application selection we recommend to use this special Python class called ix.ApplicationSelection. Let's see, how cumbersome the C++ one is versus the Python one.

For example, if you wish to access the first item of the selection using the C++ API, you would need to write:

1 ix.application.get_selection().get_item("global", 0)

Now using the one provided in clarisse_helper.py, you would need to write:

1 ix.selection[0]

In the very same way, to get the number of items in the selection, you would have to write:

1 ix.application.get_selection().get_count("global")

Instead of:

1 ix.selection.get_count()

Managing object attributes

Using the C++ API can be a little cumbersome to manage object attributes. Attribute are identified by a string you need to pass to the method OfObject::get_attribute. This method then returns a OfAttr on which you can get or set the value according to the attribute OfAttr::Type type. For example:

1 obj.get_attribute('unseen_by_camera').set_bool(False)
2 obj.get_attribute('translate').set_vec3d(ix.api.GMathVec3d(0.0, 0.0, 0.0))
3 obj.get_attribute('filename').set_string('/home/user/foo/data/tree.obj')
4 obj2.get_attribute('filename').set_string(obj.get_attribute('filename').get_string())

Instead, the simple API provides some shortcuts to the object attribute values:

1 obj.attrs.unseen_by_camera = False
2 obj.attrs.translate[0] = 0.0
3 obj.attrs.translate[1] = 0.0
4 obj.attrs.translate[2] = 0.0
5 obj.attrs.filename = '/home/user/foo/data/tree.obj'
6 obj2.attrs.filename = obj.attrs.filename

You can also directly access to the OfAttr by using:

1 obj.attrs.filename.attr

Study of ix.render_image implementation

Let's have a look at the function ix.render_image defined in clarisse_helper.py. This function renders the specified image and returns the rendered image. Please note that, if the image was already rendered and nothing relevant has changed in it, the function will still return resulting image without re-rendering it.

1 def render_image(image):
2  cimg = image
3  if isinstance(image, str): cimg = ix.get_item(image)
4  if not isinstance(cimg.get_module(), ix.api.ModuleImage):
5  raise RuntimeError('the specified item isn\'t an Image')
6 
7  if cimg.get_module().is_image_dirty(ix.api.ModuleImageQuality.QUALITY_FULL):
8  cimg.get_module().compute_image(ix.api.ModuleImageQuality.QUALITY_FULL, api.ModuleImageQuality.QUALITY_FULL)
9 
10  while cimg.get_module().is_image_dirty(ix.api.ModuleImageQuality.QUALITY_FULL):
11  ix.application.check_for_events()
12 
13  return cimg.get_module().get_image(ix.api.ModuleImageQuality.QUALITY_FULL)

As Python is not a static typed language we first need to check what's the type of image input argument the function just received.

1 def render_image(image):
2  cimg = image
3  if isinstance(image, str): cimg = get_item(image)
4  if not isinstance(cimg.get_module(), api.ModuleImage):
5  raise RuntimeError('the specified item isn\'t an Image')

If the input argument is a string we then assume it describes a path to an actual image of the project, for example, project://scene/image.

In that case, we call the get_item function, defined again in clarisse_helper.py to find the corresponding project item according to the input string. If the input argument is not a string, we simply assume it is an OfObject. Now, we need to check if the item is an actual Image by checking the type of the item module against ModuleImage type.

If we pass successfully these checks, we can be sure the input item is indeed what we expect. We will now check if the input image is already rendered.

1 if cimg.get_module().is_image_dirty(api.ModuleImageQuality.QUALITY_FULL):
2  cimg.get_module().compute_image(api.ModuleImageQuality.QUALITY_FULL, api.ModuleImageQuality.QUALITY_FULL)

To do so, we use ModuleImage::is_image_dirty. This method returns if the image needs to be evaluated for a specific quality. This is why we pass ix.api.ModuleImageQuality.QUALITY_FULL to tell we are interested by the highest image quality. The highest image quality is the actual image at full resolution and number of samples as defined by the user. If the image is indeed "dirty", we then need to evaluate it.

1 cimg.get_module().compute_image(api.ModuleImageQuality.QUALITY_FULL, api.ModuleImageQuality.QUALITY_FULL)

To evaluate an image we have to call ModuleImage::compute_image and specify the lowest quality and the highest one we're interested in. Here, both lowest and highest qualities are set to maximum QUALITY_FULL. This tells the evaluation to bypass the progressive refinement to directly render the highest image quality.

Another very important thing is that ModuleImage::compute_image runs asynchronously. In other words, this means that this method is not blocking and performed in the background. We are then obliged to wait for the image to be rendered before we can continue the script execution.

1 while cimg.get_module().is_image_dirty(api.ModuleImageQuality.QUALITY_FULL):
2  application.check_for_events()

Then, we have to continuously check if the image is dirty while asking the application to continue to read incoming events by calling AppBase::check_for_events. Clarisse keeps on processing the flow of events and the script is blocked until the image rendering is completed. This call is extremely important as, this way, the application isn't blocked while the script is waiting the render to end.

1 return cimg.get_module().get_image(api.ModuleImageQuality.QUALITY_FULL)

Finally, we just have to return the computed image. Please note something very important: calling ModuleImage::get_image isn't asynchronous: the method is blocking. In other words, ModuleImage::get_image should never be directly called from a script or from the main application loop: it may freeze or hang the application.