Default Attribute Producer Plug-ins
===================================

A number of aspects of the Katana ecosystem follow the principle of convention
over configuration. For example, shaders may contain hundreds of configurable
parameters but the shader author provides sensible default values for
most of these parameters, this saves artists' time by only requiring them to
provide values in situations where the defaults aren't suitable. Similarly,
renderers provide a vast array of configuration options with sensible defaults,
users are only required to override values in situations where the defaults
are not appropriate for their use.

One of the main problems with the convention over configuration principle is
that it often requires users to have an in-depth knowledge of the available
parameters, their default values and acceptable inputs. Default Attribute
Producers (DAPs) provide a solution to this by interrogating default values,
their acceptable inputs and then returning a description of each parameter. This
description is then used (primarily) in Katana's user interface to allow users
to introspect all configurable aspects of a shader, renderer or any other
plug-in that extends Katana.

DAPs are implemented as C++ plug-ins and called from within Ops. They are
able to query the incoming scene graph at the point they are invoked (but not
modify it) and callers may request the DAP limit the values it should provide
by specifying the :code:`attrRoot` parameter. For example a DAP for producing
shader defaults may receive :code:`attrRoot = "material"`, in which case it
should only return default values under the :code:`material.XXX` attribute
hierarchy.

DAPs are themselves evaluated within the Runtime. For each call to their
:code:`cook()` function they must generate and return an
:code:`FnAttribute::GroupAttribute` value for the requested context. The
context is defined as:

 - Op / scene graph location combination - Op and scene graph location the
   calling Op is currently being evaluated at.
 - Attribute path - an optional string which is used to scope the work of the
   DAP to a specific portion of the attribute hierarchy at the current location.

This translates to the following basic C++ signature of a DAP:

.. code-block:: c++

      class MyDefaultAttributeProducer : public FnDefaultAttributeProducer::DefaultAttributeProducer
      {
      public:

          static FnAttribute::GroupAttribute cook(
              const FnGeolibOp::GeolibCookInterface& interface,
              const std::string& attrRoot,
              const std::string& inputLocationPath,
              int32_t inputIndex);
      };

Where:
 - :code:`interface` - the same interface passed to your Op's :code:`cook()`
   function, you are free to query the incoming scene graph but not make
   modifications to the outgoing scene graph.
 - :code:`attrRoot` - allows the caller to scope the work of the DAP at a particular
   location to a sub-tree of the attribute hierarchy. For example, to only
   return values for the :code:`material` attribute.
 - :code:`inputLocationPath` - allows the caller to specify an scene graph location
   in the incoming scene where attribute queries should be directed.
 - :code:`inputIndex` - allows the caller to specify a particular branch on the
   incoming Op tree where attribute queries should be directed.

DAP Calling Conventions
-----------------------
As discussed previously, DAPs are called within Ops. You can invoke them by
calling :cpp:func:`FnGeolibCookInterfaceUtils::cookDaps()
<Foundry::Katana::FnGeolibCookInterfaceUtils::cookDaps>` and passing in the
necessary arguments (available to you in your Op's cook context).
You'll get an :code:`FnAttribute::GroupAttribute` back which you can either
query or include in your Op's :code:`cook()` output. A number of conventions
exist for certain attributes (such as UI hints); these are explained in more
detail below. However, you are free to define any format when implementing
your own DAPs and Ops.

The :code:`cookDaps()` method signature is as follows:

.. code-block:: c++

    static FnAttribute::GroupAttribute cookDaps(
        const GeolibCookInterface& interface,
        const std::string& attrRoot,
        const std::string& inputLocationPath = std::string(),
        int inputIndex = kFnKatGeolibDefaultInput,
        const FnAttribute::Attribute& cookOrderAttr = FnAttribute::Attribute());

By default, when :code:`cookDaps()` is called, the built-in Katana DAPs -
Material and GenericAssign - will be evaluated first and then the remaining
DAPs will be evaluated in an arbitrary order. You can override this default
behavior by passing an :code:`FnAttribute::StringAttribute` containing an
ordered array of DAPs to run. Any DAPs not in the list will be evaluated after
in an arbitrary order. e.g.:

.. code-block:: c++

    std::vector<std::string> newCookOrder;
    newCookOrder.push_back("MyDefaultAttributeProducer");
    newCookOrder.push_back("Material");
    newCookOrder.push_back("GenericAssign");
    FnAttribute::StringAttribute newCookOrder(newCookOrder, 1);

    GroupAttibute defaultGroupAttr =
        FnGeolibCookInterfaceUtils::cookDaps(
            interface, "", "", kFnKatGeolibDefaultInput, newCookOrder);

When are DAPs called?
~~~~~~~~~~~~~~~~~~~~~
DAPs can be called at anytime during an Op's cook call (even at render time).
Some Ops (like the `AttributePanelPolish` Op), are only appended to the Op tree
during a UI session and make explicit calls to retrieve default attribute
values. This allows the :kat:ui:`Attributes` tab to fill in the blanks when
you view attributes.

.. note::
    At present a number of Katana's built-in DAPs are not threadsafe.
    Care should be taken to invoke ``cookDaps()`` only in serial sections of code.

DAPs will be evaluated by the following built-in Katana Ops:

- AdjustScreenWindowResolve
- AttributePanelPolish
- LocalizeAttribute
- ZoomToRect

DAP Attribute Conventions
-------------------------
Like Ops, DAPs impose no restrictions on attribute hierarchy (except that
a :code:`GroupAttribute` is returned) so any conventions largely depend on the
consumer of the DAP's return value.

If you're writing a DAP for a particular purpose, such as to drive a
custom UI tab, feel free to structure your :code:`GroupAttribute` in a way most
suited to your needs.

UI Hints Convention
~~~~~~~~~~~~~~~~~~~
If you'd like to integrate with areas in the Katana UI that display attribute
values (such as the :kat:ui:`Attributes` tab) you can include UI hints in the
:code:`FnAttribute::GroupAttribute` returned by your DAP's :code:`cook()`
function, as the sample code below demonstrates:

.. code-block:: c++

    FnDapUtil::SetAttrHints(
        gb, "attributeName",
        FnAttribute::GroupBuilder()
            .set("help", FnAttribute::StringAttribute("Some help text"))
            .set("widget", FnAttribute::StringAttribute("widgetName"))
            .set("options", FnAttribute::StringAttribute("widgetOptions"))
            .build());

DAP Class Reference
-------------------

.. doxygenclass:: Foundry::Katana::DefaultAttributeProducer