Value Provider (Output Knobs)

Sometimes a node processes an intermediate calculation that you wish to expose to users. For example, NUKE’s 3D nodes find it useful to provide an “output knob” for the transformation matrix that was calculated from the translate/scale/rotate knobs.

In order to allow this, you can set a “ValueProvider” on certain knobs (namely those inheriting from the Array knob type). ValueProvider differs from standard knob linking in the sense that it provides a Model-View type interface. This means that the value of the new knob is actually directly dependent on the ValueProvider and thus the source data, as opposed to both knobs maintaining storage and state, then updating each other when they change.

As such, a ValueProvider linked knob is ‘OUTPUT_ONLY’ both in terms of the knob flag, and in that an end user is only able to view the value and not change it. Let’s take a look at some of the details:

void knobs(Knob_Callback f)
{
  Float_knob(f, &_c, "output");
  SetValueProvider(f, this);
}

The object pointed to by the second parameter of SetValueParameter has to be an implementation of the ArrayKnobI::ValueProviderI class. In the example above, it is assumed that the Op itself is an implementation of ValueProviderI. It needs to implement the following methods:

bool ArrayKnobI::ValueProviderI::provideValuesEnabled(const DD::Image::ArrayKnobI*, const DD::Image::OutputContext &oc) const

This is given the pointer to the knob in question and an OutputContext and should return true if the ValueProviderI wishes to provide the value at this context, or false if the internal value from the ArrayKnob should be used.

std::vector<double> ArrayKnobI::ValueProviderI::provideValues(const ArrayKnobI *arrayKnob, const DD::Image::OutputContext &oc) const

This function is called if provideValuesEnabled() has returned true already. It is given the arrayKnob and the context and should return a vector corresponding in size to the number of elements in the ArrayKnob (1 for a Float_knob, 2 for an XY_knob, and so on). It should calculate the values to be displayed/exposed and return them. For example:

virtual std::vector<double> provideValues(const ArrayKnobI* arrayKnob, const DD::Image::OutputContext& oc) const {
  std::vector<double> values;
  values.push_back(knob("a")->get_value_at(oc.frame(), oc.view()) * knob("b")->get_value_at(oc.frame(), oc.view()));
  return values;
}

If multiple knobs are using the same value provider, it can distinguish between them from the first parameter. Note that when provideValues() is called on an object which is also an Op, you cannot rely on the state of that Op being valid or up-to-date for the passed-in context. This is why this function fetches the knob values itself, making sure to specify the time and view from the OutputContext. Note additionally that provideValues cannot take its source from, for example, image data and so on.

The NDK contains a ‘ValueProviderOp’ sample which demonstrates a simple case using a ValueProvider to link a Color_knob and a MultiFloat_knob. If you’re looking to link non-axis knob types, you may want to check out the The knob_changed() Method section.