Wiki

Clone wiki

arnold_shaders / assExport_source_code

Source Code Documentation

The plugin is called assExport because it's main purpose it to extend Maya to be able to export a scene description for Arnold's .ass (ASS - Arnold Scene Source) file format.

Directory Structure

The source for the plug-in is found in arnold_shaders/maya/src and has a couple of sub-directories:

> cd $GIT/arnold_shaders/maya/src/
> ls -1 -F
Makefile
assExport.cpp
assExport.hpp
commands/
helpers/
nodes/
shapes/
translator/

Entry Point

The main entry point is the file assExport.cpp where the plug-in gets initialized and un-initialized during loading and un-loading of the plug-in. It needs to register a couple of things to Maya: assExport_init.png

A Simple Ass File

Let's look at a simple .ass file exported from another application (in this case from within Blender). The original scene consisted of:

  1. camera
  2. point light
  3. sphere (with a single material)

A screenshot of a part of the outliner is visible in the image below on the lower right part:

simple_ass_file.png

Beside the four entries we would expect, there are four additional entries being found on the Arnold side. One of it is a default material which we will export from within Maya as well. The strategy is to define such a default shader (or material) and fall back to use it, whenever the exporter runs into a problem, while exporting some geometry (which needs a shader). This way we will always end up with a valid .ass file and a warning can be printed to the Maya user about the problem the exporter was facing when it tried to export a part of the scene.

AiSetAppString

One information we would like to keep in the .ass file is the host application (and probably additional settings) where the file was exported from. Therefore we will use a function from the Arnold API before we write the .ass file. That will result in a comment line starting with ### host app:

AiSetAppString.png

The instance of the class AssTranslator is created immediately after the plug-in was loaded, but the function writer(...) will be invoked either using the GUI (File->Export All...) or from within Maya's Script Editor using MEL:

file -force -options "..." -type "ASSexport" -pr -ea "/path/simple.ass";

Supported Shapes

If you ask Arnold about the built-in shapes you will get the following list:

> kick -nodes n | grep shape                           
 box                              shape
 cone                             shape
 curves                           shape
 cylinder                         shape
 disk                             shape
 ginstance                        shape
 implicit                         shape
 nurbs                            shape
 plane                            shape
 points                           shape
 polymesh                         shape
 procedural                       shape
 sphere                           shape

Most of them are supported by the assExport plug-in. Currently only the ginstance, implicit, and procedural don't have an equivalent Maya node. Subdivision surfaces are part of the polymesh using related parameters. Here is a scene file (.ass) exported by the assExport plug-in and being renderer via Arnold's standalone kick command:

arnold_shapes.png

Particles are rendered by using the points shape.

Export an .ASS File

As mentioned before the main purpose of the plug-in is to export an .ass file (e.g. to render a frame of a sequence on a render farm). From Maya's Script Editor you can accomplish this with the following MEL command:

file -force -options "..." -type "ASSexport" -pr -ea "/path/simple.ass";

How does this work on the C++ side? First you have to register the new command and then shovel data from the Maya side to the Arnold side (which handles the writing to an .ass file once it has all the info). We have seen already how the class AssTranslator gets registered and how we end up in the exportAll(...) member function (see above). See the MEL command reference for the file command and flags for exportAll (-ea) and exportSelected (-es).

assExport_ASSexport_command.png

Render an Exported .ASS File

For convenience you can start a sub-process to render an exported .ass file via Arnold's standalone kick program. Maya isn't blocked in any way and can be used immediately again, once the sub-process got spawned. From within Maya's Script Editor you call a MEL command providing the camera resolution, a gamma value, the camera name (used to render the image), and a camera exposure value (easy to adjust the brightness for an already exported scene:

arnoldRender -x 640 -y 480 -cam persp -g 2.2 -e 0;

Currently all perspective cameras are exported and the orthographic ones (e.g. for the viewport) are ignored. Therefore you can prepare several cameras, export a single .ass file and render different views using the command above.

Auto-Generating most of the Source Code

There is a very simple strategy to generate C++ code from built-in Arnold lights, shaders, filters, drivers, shapes, cameras, and custom nodes (of those types). Basically there are Maya nodes (classes which inherit from MPxNode) and the resulting C++ code resides in the arnold_shaders/maya/src/nodes sub-directory. Those Maya nodes need unique IDs which are stored in arnold_shaders/maya/src/nodes/AssMayaTypeIds.hpp and are responsible for storing Arnold specific settings (encapsulated in Maya nodes) within a Maya scene. Removing all the plug-in specific nodes and attributes should cleanly remove everything from a Maya scene which was created by the plug-in.

For each Maya node (which can for example be seen in the Hypershade window) you can specify a MEL script to re-order the parameters and group them in different layouts (this is especially useful for shaders with lots of parameters), but if such a MEL script doesn't exists (or Maya can't find it) the parameters are nevertheless there and the values can be edited - the layout might just look ugly.

So here is an example how I want the C++ code to look like if I have to retrieve the shader parameter values for Arnold's standard shader from Maya and make Arnold aware of individual parameter settings (using the Arnold C/C++ API) before writing the .ass file to disk:

...
        // try to retrieve all data to export ...
        AssStandard assStandard = AssStandard(shadingEngineName);
        float Kd = assStandard.getKd();
 ...
        // ... before doing Arnold stuff
        arnoldShader = AiNode("standard");
        AiNodeSetStr(arnoldShader, "name", shadingEngineName.asChar());
        AiNodeSetFlt(arnoldShader, "Kd", Kd);
 ...

Before we dig deeper into the automatically generated C++ code, let's look how those nodes and shaders parameters look like in the graphical user interface of Maya:

assExport_Maya_standard.png

On the left you see individual nodes and their connections in the Hypershade, on the right you see the shader parameters of the Arnold standard shader in the Attribute Editor. The layout was done by a MEL script (as mentioned before). The main concept is that we want to keep already existing shader nodes (for example for Maya's internal renderer or mental ray specific nodes) and just add Arnold specific nodes (so they can be removed and the Maya scene still be rendered with other renderers).

So the instance of the Maya node on the upper left (called aiStandard) was registered to Maya in arnold_shaders/maya/src/assExport.cpp and will be de-registered while un-loading the plug-in:

...
#include "nodes/AssMayaStandard.hpp"
...
DLLEXPORT
MStatus
initializePlugin(MObject object) {
  MStatus status;
...
  typeName = "aiStandard";
  status = plugin.registerNode(typeName,
                               AssMayaStandard::id,
                               AssMayaStandard::creator,
                               AssMayaStandard::initialize);
  CHECK_MSTATUS(status);
...
  return status;
}

DLLEXPORT
MStatus
uninitializePlugin(MObject object) {
  MStatus status;
...
  status = plugin.deregisterNode(AssMayaStandard::id);
  CHECK_MSTATUS(status);
...
  return status;
}
...

The C++ code for the classes AssMayaStandard and AssStandard was created automatically from a Python script called kick_info_to_cpp.py. And this is true for all supported Maya nodes and helper classes. Let's look into one of the Makefiles to see what's going on:

...
all: options atmosphere cameras lights shaders non_built_in
...
shaders: \
...
standard \
...
standard:
	python kick_info_to_cpp.py standard node_hpp > \
../src/nodes/AssMayaStandard.hpp
	python kick_info_to_cpp.py standard node_cpp > \
../src/nodes/AssMayaStandard.cpp
	python kick_info_to_cpp.py standard helper_hpp > \
../src/helpers/AssStandard.hpp
	python kick_info_to_cpp.py standard helper_cpp > \
../src/helpers/AssStandard.cpp
...

There is a rule called all which refers to other rules. One of them is called shaders and refers (again) to more rules. The standard rule finally calls the python script several times (with different) parameters and divert the output (the resulting C++ code) into header and source files for the classes AssMayaStandard and AssStandard.

Updated