Result


Here is a simple instancer procedural developed based on code by Gael Honorez.

This should be a good starting point for learning how to write an Arnold procedural since it covers reading user-declared procedural parameters, how to create a new node, set its parameters and setup motion blur matrices.

#include <ai.h>
#include <sstream>
#include <vector>

struct Instancer
{
   std::string name;
   AtArray *path_indices;
   AtArray *path_start_indices;
   AtArray *instance_matrix;
   AtUniverse *universe;
   std::vector<AtNode*> objects;
   int num_instances;
};

AI_PROCEDURAL_NODE_EXPORT_METHODS(InstancerMtd);

node_parameters
{
   AiParameterArray("path_indices"       , AiArray(0, 1, AI_TYPE_INT));
   AiParameterArray("path_start_indices" , AiArray(0, 1, AI_TYPE_INT));
   AiParameterArray("objects"            , AiArray(0, 1, AI_TYPE_NODE));
   AiParameterArray("instance_matrix"    , AiArray(0, 1, AI_TYPE_MATRIX));
}

procedural_init
{
   Instancer *instancer = new Instancer();
   *user_ptr = instancer;

   instancer->universe = AiNodeGetUniverse(node);

   // get procedural name so it's the prefix of instanced nodes' name
   instancer->name = AiNodeGetStr(node, "name");
   instancer->path_indices = AiNodeGetArray(node, "path_indices");
   instancer->num_instances = AiArrayGetNumElements(instancer->path_indices);

   // this is the start index of each instance
   instancer->path_start_indices = AiNodeGetArray(node, "path_start_indices");
   AiMsgInfo("[instancer] number of instances: %d", AiArrayGetNumElements(instancer->path_indices));

   AtArray *objects = AiNodeGetArray(node, "objects"); // to get the AtNode pointers for all the master nodes

   // allocate and create AtNode pointer array
   AiMsgInfo("[instancer] getting master node list ...");

   instancer->objects.resize(AiArrayGetNumElements(objects));
   for (unsigned int i = 0; i < AiArrayGetNumElements(objects); i++)
   {
      AtNode *master_node = reinterpret_cast<AtNode*>(AiArrayGetPtr(objects, i));
      const char* master_name = AiNodeGetName(master_node);
      AiMsgInfo("[instancer] adding node '%s' to master list", master_name);

      instancer->objects[i] = master_node;
      if (node == NULL)
         AiMsgWarning("[instancer] node '%s' does not exist!", master_name);
   }

   // get matrices pointer and number of motion keys
   AtArray *instance_matrix = AiNodeGetArray(node, "instance_matrix");
   instancer->instance_matrix = instance_matrix;
   AiMsgInfo("[instancer] instance_matrix elements: %d, motion keys: %d", AiArrayGetNumElements(instance_matrix), AiArrayGetNumKeys(instance_matrix));

   return true;
}

procedural_cleanup
{
   Instancer *instancer = (Instancer*)user_ptr;
   delete instancer;
   return true;
}

procedural_num_nodes
{
   // return how many nodes to generate
   Instancer *instancer = (Instancer*)user_ptr;
   return AiArrayGetNumElements(instancer->instance_matrix);
}

procedural_get_node
{
   Instancer *instancer = (Instancer*)user_ptr;

   int instance_index = AiArrayGetInt(instancer->path_start_indices, i); // get particle index for this instance
   int path_index = AiArrayGetInt(instancer->path_indices, instance_index);
   AtNode *object = instancer->objects[path_index];

   std::stringstream numStr; // setup node name by concatenate procedural name with instance node number
   numStr << "_" << instance_index;
   std::string currentName(instancer->name + "/" + AiNodeGetName(object) + numStr.str() );

   AtNode *currentInstance = AiNode(instancer->universe, "ginstance", currentName.c_str(), node); // initialize node as child of procedural node

   AiNodeSetPtr(currentInstance, "node", (void *)object); // setup ginstance node parameter
   AiNodeSetBool(currentInstance, "inherit_xform", false); // usually instancer doesn't move around, but maybe this should be set to true?
   AiNodeSetByte(currentInstance, "visibility", 255);

   int num_motion_keys = AiArrayGetNumKeys(instancer->instance_matrix);
   if (num_motion_keys > 1)
   {
      // allocate and assign matrices
      AtArray *matrices = AiArrayAllocate(1, num_motion_keys, AI_TYPE_MATRIX);
      for (int j = 0; j < num_motion_keys; j++)
      {
         AtMatrix matrix = AiArrayGetMtx(instancer->instance_matrix, instance_index + instancer->num_instances * j);
         AiArraySetMtx(matrices, j, matrix);
      }
      AiNodeSetArray(currentInstance, "matrix", matrices);
   }
   else
   {
      AtMatrix matrix = AiArrayGetMtx(instancer->instance_matrix, instance_index);
      AiNodeSetMatrix(currentInstance, "matrix", matrix);
   }

   return currentInstance;
}

node_loader
{
   if (i>0)
      return false;
   node->methods      = InstancerMtd;
   node->output_type  = AI_TYPE_NONE;
   node->name         = "simple_instancer";
   node->node_type    = AI_NODE_SHAPE_PROCEDURAL;
   strcpy(node->version, AI_VERSION);
   return true;
}



Example compile commands:

Compile windows
> set ARNOLD_PATH="C:/Autodesk/arnold/7.0.0.0
> cl /LD simple.cpp /I %ARNOLD_PATH%/include %ARNOLD_PATH%/lib/ai.lib /link /out:simple_instancer.dll
Compile windows
> export ARNOLD_PATH=/opt/autodesk/arnold/7.0.0.
> c++ instancer_proc.cpp -o simple_instancer.so -std=c++11 -Wall -O2 -shared -fPIC -I$ARNOLD_PATH/include -L$ARNOLD_PATH/bin -lai


After compiling the above code you will have a new node in arnold called simple_instancer 

> kick -info simple_instancer
Type          Name                              Default
------------  --------------------------------  --------------------------------
... snip ...
INT[]         path_indices                      (empty)
INT[]         path_start_indices                (empty)
NODE[]        objects                           (empty)
MATRIX[]      instance_matrix                   (empty)
STRING        name                              


Here is a simple ass file that uses this new procedural to create new ginistances of two other nodes in the scene a box  and a sphere 

instancer_test.ass
options
{
    AA_samples 3
    camera mycam
    parallel_node_init off

}

persp_camera
{
    name mycam
    position 2.8 0 30
    look_at 2.8 0 0
    fov 18
}

box
{
    name mybox
    visibility 0
    matrix
     1 0 0 0
     0 1 0 0
     0 0 1 0
     0 1.1 0 1
 }

sphere
{
    name mysphere
    visibility 0
    matrix
     1 0 0 0
     0 1 0 0
     0 0 1 0
     1 1.1 0 1
}

simple_instancer
{
    name my_instancer
    objects 2 1 NODE
     "mybox" "mysphere"
    path_indices 6 1 INT # set the node for each instance
     0 0 0 1 1 1
    path_start_indices 6 1 INT # start index in the matrices below for each instance
     0 1 2 3 4 5
    instance_matrix 6 1 MATRIX # set the positions of the instances
     1 0 0 0
     0 1 0 0
     0 0 1 0
     0 0 0 1

     1 0 0 0
     0 1 0 0
     0 0 1 0
     1.2 0 0 1

     1 0 0 0
     0 1 0 0
     0 0 1 0
     2.4 0 0 1

     1 0 0 0
     0 1 0 0
     0 0 1 0
     3.6 0 0 1

     1 0 0 0
     0 1 0 0
     0 0 1 0
     4.8 0 0 1

     1 0 0 0
     0 1 0 0
     0 0 1 0
     6 0 0 1
     operator boxParamSet # add a operator
}

set_parameter
{
    name boxParamSet
    selection "*box*"
    assignment "shader='flat1'"
    inputs sphereParamSet # input another operator
}

set_parameter
{
    name sphereParamSet
    selection "*sphere*"
    assignment "shader='flat2'"
}

flat
{
    name flat1
    color .3 1 .5
}

flat
{
    name flat2
    color .3 .5 1
}




  • No labels
Privacy settings / Do not sell my personal information / Privacy/Cookies