Writing an output driver

An output driver gives you access to the one filtered value per pixel, after a bucket has been completely rendered. You can use drivers for example to write to new image file formats, or to send pixels to a display device, to an application's window, etc.

You can specify a driver in an .ass file like this:

outputs 1 1 STRING
  "RGBA RGBA my_main_filter my_main_driver"

 

You can also specify multiple drivers for different AOVs, potentially using different filters and data types:

 outputs 3 1 STRING
  "RGBA RGBA my_filter my_main_driver"
  "direct_diffuse RGBA my_filter my_direct_diffuse_driver"
  "indirect_diffuse RGBA my_filter my_indirect_diffuse_driver"

 

Example Driver

The implementation of a sample driver that writes out to a file a list of all the objects in a pointer AOV would look like:

pointer_driver.cpp
#include <ai.h>
#include <strings.h>
#include <fstream>
#include <unordered_map>
// This driver will write to a file a list of all the objects in a pointer AOV
AI_DRIVER_NODE_EXPORT_METHODS(DriverPtrMtd);
namespace ASTR {
   const AtString name("name");
   const AtString filename("filename");
};
typedef struct {
   std::unordered_map<AtString, AtNode*, AtStringHash> names;
} DriverPtrStruct;
node_parameters
{
   AiParameterStr(ASTR::filename, "objects.txt");
}
node_initialize
{
   DriverPtrStruct *driver = new DriverPtrStruct();
   // initialize the driver
   AiDriverInitialize(node, false);
   AiNodeSetLocalData(node, driver);
}
driver_needs_bucket
{
   return true;
}
driver_process_bucket
{ }
node_update
{ }
driver_supports_pixel_type
{
   // this driver will only support pointer formats
   return pixel_type == AI_TYPE_POINTER || pixel_type == AI_TYPE_NODE;
}
driver_open
{ // this driver is unusual and happens to do all the writing at the end, so this function is
  // empty.
}
driver_extension
{
   static const char *extensions[] = { "txt", NULL };
   return extensions;
}
driver_prepare_bucket
{ }
driver_write_bucket
{
   DriverPtrStruct *driver = (DriverPtrStruct *)AiNodeGetLocalData(node);
   const void *bucket_data;
   // Iterate over all the AOVs hooked up to this driver
   while (AiOutputIteratorGetNext(iterator, NULL, NULL, &bucket_data))
   {
      for (int y = 0; y < bucket_size_y; y++)
      {
         for (int x = 0; x < bucket_size_x; x++)
         {
            // Get source bucket coordinates for pixel
            int sidx = y * bucket_size_x + x;
            // Because of driver_supports_pixel_type, we know pixel is a
            // pointer to an AtNode.
            AtNode* pixel_node = ((AtNode **)bucket_data)[sidx];
            const AtString name = AiNodeGetStr(pixel_node, ASTR::name);
            if (pixel_node != nullptr)
                AiMsgDebug("[driver_ptr] %s", name.c_str());
            driver->names.emplace(name, pixel_node);
         }
      }
   }
}
driver_close
{
   DriverPtrStruct *driver = (DriverPtrStruct *)AiNodeGetLocalData(node);
   std::ofstream myfile(AiNodeGetStr(node, ASTR::filename));
   for (auto &i : driver->names)
      myfile << i.first << ":\t " <<i.second << std::endl;
   myfile.close();
}
node_finish
{
   // Free local data
   DriverPtrStruct *driver = (DriverPtrStruct *)AiNodeGetLocalData(node);
   delete driver;
   AiNodeDestroy(node);
}
node_loader
{
   if (i>0)
      return false;
   node->methods = (AtNodeMethods*) DriverPtrMtd;
   node->output_type = AI_TYPE_NONE;
   node->name = "driver_ptr";
   node->node_type = AI_NODE_DRIVER;
   strcpy(node->version, AI_VERSION);
   return true;
}


 

For the above driver, we need to choose the correct output AOV to feed data to the driver. Here is an example .ass file that sets up a simple cube and the driver to output the object AOV to a file

options
{
    AA_samples 3
    outputs 2 1 STRING
    "RGBA RGBA g_filter main_driver"
    "object POINTER c_filter obj_driver" # Set the output for the driver and make sure we are feeding the object aov to it
}

closest_filter
{
    name c_filter
}
 
# our custom driver
driver_ptr
{
    name obj_driver
    filename "test_objs.txt" # write out to a file called test_objs.txt
}
gaussian_filter
{
    name g_filter
    width 3
}
driver_tiff
{
    name main_driver
    filename "testrender.tif"
}
persp_camera
{
    name mycam
    position
    3 3 3
    look_at
    0 0 0
}

box
{
    name mycube
    min
    -1 -1 -1
    max
    1 1 1
}

 

The output of the driver will then print something like the following to the output file name.

 

mycube:  0x7f5db73cb3c0
  • No labels