It's possible to link shaders to some parameters in Arnold, for example, to assign a texture to the color of an Arnold Light or an Arnold Sky. C4DtoA has a custom UI widget for this feature, called shader link GUI.

The widget is a popup button where the user can select between the following modes:

  • constant: displays an input field of the given parameter's type (e.g. color, float, etc.).
  • texture: displays a C4D shader GUI where the user can set up a texture in a native way. Does not allow to assign a shader network.
  • material: allows the user to assign an Arnold Material to the parameter which represents a network of shaders. Only texture type shaders are valid in this context.

Definition

The shader link custom GUI can be defined in a metadata (.mtd) file via the linkable flag when the UI of an Arnold node is automatically generated. It's available only for integer, float, vector or color type parameters.

[node quad_light]
 [attr color]
     linkable            BOOL    true


The shader link custom GUI can also be added manually in the resource file (.res).

myobject.h
...
MYCOLOR = 1111,
MYCOLORLINK = 1112,
...
MYLONG = 2111,
MYLONGLINK = 2112,
...
myobject.res
COLOR MYCOLOR {CUSTOMGUI ARNOLD_SHADERLINK; LINK_ID 1112;}
LINK MYCOLORLINK {CUSTOMGUI INLINE;}
...
LONG MYLONG {CUSTOMGUI LONGSLIDER; LINK_ID 2112; MINSLIDER 0; MAXSLIDER 33; MIN -100; MAX 100;}
LINK MYLONGLINK {CUSTOMGUI INLINE;}

Data structure

The shader link GUI settings are stored in a BaseContainer under the id 998800. The container is a map of parameter ids (Int32) and shader link settings (BaseContainer). The following ids are available in the settings container:

  • 101: type of the widget.
    • 1: constant
    • 2: texture
    • 3: material
  • 102: the constant value (e.g. the color vector)
  • 103: the texture which is a BaseShader of type Xbitmap. The BaseShader object is inserted into the object which owns the parameter (e.g. Arnold Light, Arnold Sky), must be allocated and deallocated by the developer.
  • 104: an Arnold Material (BaseMaterial) for the shader network mode.

The value (constant) also stored in the node data container at the parameter id.

Get the current settings

This example shows how to read the current value of the shader link GUI.

#define C4DAI_SHADERLINK_CONTAINER 9988000
#define C4DAI_SHADERLINK_TYPE 101
#define C4DAI_SHADERLINK_VALUE 102
#define C4DAI_SHADERLINK_TEXTURE 103
#define C4DAI_SHADERLINK_MATERIAL 104

#define C4DAI_SHADERLINK_TYPE__CONSTANT 1
#define C4DAI_SHADERLINK_TYPE__TEXTURE 2
#define C4DAI_SHADERLINK_TYPE__MATERIAL 3

#define C4DAI_GVC4DSHADER_BITMAP_COLOR_SPACE 10101

///
/// Returns the shader link data stored of the given node parameter.
///
const BaseContainer GetShaderLinkData(BaseList2D* node, Int32 paramId)
{
   if (node == nullptr)
      return BaseContainer();

   return node->GetData().GetContainer(C4DAI_SHADERLINK_CONTAINER).GetContainer(paramId);
}

///
/// Returns the shader node assigned to the given shader link.
///
BaseShader* GetShader(const BaseContainer& shaderLinkData)
{
   BaseLink* link = shaderLinkData.GetBaseLink(C4DAI_SHADERLINK_TEXTURE);
   if (link == nullptr)
      return nullptr;

   return static_cast<BaseShader*>(link->ForceGetLink());
}

///
/// Prints current shader link settings to the console.
///
void ReadShaderLinkGui(BaseList2D* node, Int32 paramId)
{
   if (node == nullptr)
      return;

   ApplicationOutput(FormatString("--- Shader link gui of '@' ---", node->GetName()));

   const BaseContainer& shaderLinkData = GetShaderLinkData(node, paramId);
   Int32 type = shaderLinkData.GetInt32(C4DAI_SHADERLINK_TYPE, C4DAI_SHADERLINK_TYPE__CONSTANT);

   // constant
   if (type == C4DAI_SHADERLINK_TYPE__CONSTANT)
   {
      const Vector& color = node->GetData().GetVector(paramId);

      ApplicationOutput(FormatString(" type: constant"));
      ApplicationOutput(FormatString(" value: @", color));
   }

   // texture
   else if (type == C4DAI_SHADERLINK_TYPE__TEXTURE)
   {
      BaseShader* shader = GetShader(shaderLinkData);

      Filename fn;
      String colorSpace;
      if (shader != nullptr && shader->GetType() == Xbitmap)
      {
         const BaseContainer shaderData = shader->GetData();
         fn = shaderData.GetFilename(BITMAPSHADER_FILENAME);
         colorSpace = shaderData.GetString(C4DAI_GVC4DSHADER_BITMAP_COLOR_SPACE);
      }

      ApplicationOutput(FormatString(" type: texture"));
      ApplicationOutput(FormatString(" filename: @", fn));
      ApplicationOutput(FormatString(" color space: @", colorSpace));
   }

   // shader network
   else if (type == C4DAI_SHADERLINK_TYPE__MATERIAL)
   {
      BaseMaterial* material = static_cast<BaseMaterial*>(shaderLinkData.GetLink(C4DAI_SHADERLINK_MATERIAL, node->GetDocument()));

      String materialName;
      if (material != nullptr)
      {
         materialName = material->GetName();
      }

      ApplicationOutput(FormatString(" type: material"));
      ApplicationOutput(FormatString(" name: @", materialName));
   }
}

Edit the settings

This example shows how to set up the different types of shader link settings.

#define C4DAI_SHADERLINK_CONTAINER 9988000
#define C4DAI_SHADERLINK_TYPE 101
#define C4DAI_SHADERLINK_VALUE 102
#define C4DAI_SHADERLINK_TEXTURE 103
#define C4DAI_SHADERLINK_MATERIAL 104

#define C4DAI_SHADERLINK_TYPE__CONSTANT 1
#define C4DAI_SHADERLINK_TYPE__TEXTURE 2
#define C4DAI_SHADERLINK_TYPE__MATERIAL 3

#define C4DAI_GVC4DSHADER_BITMAP_COLOR_SPACE 10101

///
/// Sets the shader link data of the given node parameter.
///
/// Use the function SetupColor(), SetupTexture() or SetupMaterial()
/// to create a shader link data with proper settings.
///
void SetShaderLinkData(BaseList2D* node, Int32 paramId, const BaseContainer& shaderLinkData)
{
   if (node == nullptr)
      return;

   Int32 type = shaderLinkData.GetInt32(C4DAI_SHADERLINK_TYPE, C4DAI_SHADERLINK_TYPE__CONSTANT);

   // set the constant value on the node
   if (type == C4DAI_SHADERLINK_TYPE__CONSTANT)
   {
      const Vector& color = shaderLinkData.GetVector(C4DAI_SHADERLINK_VALUE);
      node->SetParameter(paramId, color, DESCFLAGS_SET::NONE);
   }

   // assign bitmap shader to the node
   else if (type == C4DAI_SHADERLINK_TYPE__TEXTURE)
   {
      // remove the old shader
      const BaseContainer& currentShaderLinkData = GetShaderLinkData(node, paramId);
      BaseShader* currentShader = GetShader(currentShaderLinkData);
      if (currentShader != nullptr)
      {
         currentShader->Remove();
         BaseShader::Free(currentShader);
      }

      // assign the new shader
      BaseShader* shader = GetShader(shaderLinkData);
      if (shader != nullptr)
      {
         node->InsertShader(shader);
      }
   }

   // set the shader link data to the node
   BaseContainer shaderLinkContainer = node->GetData().GetContainer(C4DAI_SHADERLINK_CONTAINER);
   shaderLinkContainer.SetContainer(paramId, shaderLinkData);
   node->SetParameter(C4DAI_SHADERLINK_CONTAINER, shaderLinkContainer, DESCFLAGS_SET::NONE);
}

///
/// Sets the shader link type to 'constant' and sets the color value.
///
void SetupColor(BaseContainer& shaderLinkData, Vector& color)
{
   shaderLinkData.SetInt32(C4DAI_SHADERLINK_TYPE, C4DAI_SHADERLINK_TYPE__CONSTANT);
   shaderLinkData.SetVector(C4DAI_SHADERLINK_VALUE, color);
}

///
/// Sets the shader link type to 'texture' and sets the filename and color space.
///
void SetupTexture(BaseContainer& shaderLinkData, const Filename& fn, const String& colorSpace)
{
   // create a new bitmap shader
   BaseShader* shader = BaseShader::Alloc(Xbitmap);
   shader->GetDataInstance()->SetFilename(BITMAPSHADER_FILENAME, fn);
   shader->GetDataInstance()->SetString(C4DAI_GVC4DSHADER_BITMAP_COLOR_SPACE, colorSpace);

   shaderLinkData.SetInt32(C4DAI_SHADERLINK_TYPE, C4DAI_SHADERLINK_TYPE__TEXTURE);
   shaderLinkData.SetLink(C4DAI_SHADERLINK_TEXTURE, shader);
}

///
/// Sets the shader link type to 'material' and liks the given material to the parameter.
///
void SetupMaterial(BaseContainer& shaderLinkData, BaseMaterial* material)
{
   shaderLinkData.SetInt32(C4DAI_SHADERLINK_TYPE, C4DAI_SHADERLINK_TYPE__MATERIAL);
   shaderLinkData.SetLink(C4DAI_SHADERLINK_MATERIAL, material);
}

Assign Texture to Arnold Sky and Arnold quad light

This example creates an Arnold Sky and Arnold quad light object and assigns an image to the color parameter.

// from res/c4dtoa_symbols.h
#define ARNOLD_SKY 1034624
#define ARNOLD_LIGHT 1030424

// from res/description/arnold_sky.h
#define C4DAIPS_SKYDOME_LIGHT_COLOR 268620635

// from res/description/ainode_quad_light.h
#define C4DAIP_QUAD_LIGHT_COLOR 2010942260

// from res/description/arnold_light.h
#define C4DAI_LIGHT_TYPE 101

// from api/include/util/NodeIds.h
#define C4DAIN_QUAD_LIGHT 1218397465

///
/// Assigns the given texture to the given node parameter.
///
void AssignTexture(BaseList2D* node, int paramId, const Filename& filename, const String& colorSpace)
{
   if (node == nullptr)
      return;

   // get the current shader link data to keep the current settings from other types
   BaseContainer shaderLinkData = GetShaderLinkData(node, paramId);
   // set the type to 'texture' and set values
   SetupTexture(shaderLinkData, filename, colorSpace);
   // set the new settings to the node
   SetShaderLinkData(node, paramId, shaderLinkData);
}

bool CreateArnoldSky(BaseDocument* doc, const Filename& filename, const String& colorSpace)
{
   // create an Arnold Sky object
   BaseObject* arnoldSky = BaseObject::Alloc(ARNOLD_SKY);
   if (arnoldSky == nullptr)
      return false;

   arnoldSky->SetName(String("Arnold Sky"));

   // add the new Arnold Sky object to the scene
   doc->InsertObject(arnoldSky, nullptr, nullptr);

   // update the UI
   EventAdd();

   // assign a texture to the sky color
   AssignTexture(arnoldSky, C4DAIPS_SKYDOME_LIGHT_COLOR, filename, colorSpace);

   return true;
}

bool CreateArnoldQuadLight(BaseDocument* doc, const Filename& filename, const String& colorSpace)
{
   // create an Arnold Light object
   BaseObject* arnoldLight = BaseObject::Alloc(ARNOLD_LIGHT);
   if (arnoldLight == nullptr)
      return false;

   arnoldLight->SetName(String("Arnold quad light"));

   // set the light type to 'quad'
   arnoldLight->GetDataInstance()->SetInt32(C4DAI_LIGHT_TYPE, C4DAIN_QUAD_LIGHT);

   // add the new Arnold quad light object to the scene
   doc->InsertObject(arnoldLight, nullptr, nullptr);

   // update the UI
   EventAdd();

   // assign a texture to the light color
   AssignTexture(arnoldLight, C4DAIP_QUAD_LIGHT_COLOR, filename, colorSpace);

   return true;
}

void main(BaseDocument* doc)
{
   // create an Arnold Sky object and assign a texture to the color
   if (!CreateArnoldSky(doc, Filename("mytexture.hdr"), String("linear sRGB")))
   {
      ApplicationOutput(String("[ERROR] Failed to create an Arnold Sky object"));
   }

   // create an Arnold quad ligth and assign a texture to the color
   if (!CreateArnoldQuadLight(doc, Filename("mytexture.hdr"), String("linear sRGB")))
   {
      ApplicationOutput(String("[ERROR] Failed to create an Arnold Sky object"));
   }
}


Download the complete example: shaderlink_test.zip

  • No labels