Loading...
Searching...
No Matches
controllerBuilder.h
Go to the documentation of this file.
1//
2// Copyright 2026 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_EXEC_EXEC_IR_CONTROLLER_BUILDER_H
8#define PXR_EXEC_EXEC_IR_CONTROLLER_BUILDER_H
9
21
22#include "pxr/pxr.h"
23
26
28#include "pxr/base/tf/token.h"
29#include "pxr/exec/exec/builtinComputations.h"
33
34PXR_NAMESPACE_OPEN_SCOPE
35
36// Builder base class used to share common code.
37//
38class ExecIr_ControllerBuilderBase {
39protected:
40 EXECIR_API
41 ExecIr_ControllerBuilderBase(
43
44 // The destructor is protected so that it's not possible to delete through
45 // a base class pointer.
46 //
47 virtual ~ExecIr_ControllerBuilderBase();
48
49 // Registers the 'explicitDesiredValue' and 'computedDesiredValue'
50 // computations that are required to compute desired values, including
51 // desired values that are expressed as overrides.
52 //
53 // TODO: These plugin computations won't be necessary when OpenExec provides
54 // core inversion support.
55 //
56 template <typename ValueType>
57 void
58 _DesiredValueComputations(
59 const TfToken &attributeName);
60
61 // Sets the computation output to the value from the 'explicitDesiredValue'
62 // input if it provides one; otherwise sets the output to the the
63 // 'computeDesiredValue' input, if it has one; otherwise sets an empty
64 // output value.
65 //
66 // If, among all considered inputs, more than one input value is provided,
67 // an error is emitted.
68 //
69 // TODO: The compuations that use this callback won't be necessary when
70 // OpenExec provides core inversion support.
71 //
72 template <typename ValueType>
73 static void
74 _GetExactlyOneDesiredValue(
75 const VdfContext &ctx);
76
77protected:
79};
80
159class ExecIrControllerBuilder : ExecIr_ControllerBuilderBase {
160public:
161
163 using Callback = ExecIrResult(*)(const VdfContext &);
164
174 EXECIR_API
177 Callback forwardCallback,
178 Callback inverseCallback);
179
180 EXECIR_API
181 ~ExecIrControllerBuilder() override;
182
188 template <typename ValueType>
189 void
191 const TfToken &attributeName);
192
199 template <typename ValueType>
200 void
202 const TfToken &attributeName);
203
206 template <typename ValueType>
207 void
209 const TfTokenVector &attributeNames) {
210 for (const TfToken &attributeName : attributeNames) {
212 }
213 }
214
222 template <typename ValueType>
223 void
225 const TfToken &attributeName);
226
229 template <typename ValueType>
230 void
232 const TfTokenVector &attributeNames) {
233 for (const TfToken &attributeName : attributeNames) {
235 }
236 }
237
246 template <typename ValueType>
247 void
249 const TfToken &attributeName);
250
258 template <typename ValueType>
259 void
261 const TfToken &inAttributeName,
262 const TfToken &outAttributeName);
263
264private:
265
266 // These computations are registered by the controller builder, but they
267 // are not meant to be consumed by code outside the controller builder.
268 struct _PrivateComputationsType {
269 EXECIR_API
270 _PrivateComputationsType();
271
272 const TfToken computeInvertedForwardValue;
273 const TfToken forwardCompute;
274 const TfToken inverseCompute;
275 };
276
277 EXECIR_API
278 static TfStaticData<_PrivateComputationsType> _privateComputations;
279
280 ExecPrimComputationBuilder _forwardComputeReg;
281 ExecPrimComputationBuilder _inverseComputeReg;
282 TfTokenVector _invertibleOutputAttributeNames;
283 Callback _inverseCallback;
284};
285
286template <typename ValueType>
287void
289 const TfToken &attributeName)
290{
291 using namespace exec_registration;
292
293 // Invertible input attributes define 'computeDesiredValue' computations
294 // that extract their values from the inverse computation.
295 //
296 // TODO: When we have core inversion support, this plugin computation will
297 // be registered using a builtin computation token that the core will
298 // provide, as a plugin point for plugins that provide inversion behavior.
299 _self.AttributeComputation(
300 attributeName, ExecIrComputations->computeDesiredValue)
301 .Callback<ValueType>([attributeName](const VdfContext &ctx) {
302 const ExecIrResult &resultMap = ctx.GetInputValue<ExecIrResult>(
303 _privateComputations->inverseCompute);
304
305 // If the inverse computation computed a value for this attribute,
306 // return it; otherwise, set an empty result.
307 if (const auto it = resultMap.find(attributeName);
308 it != resultMap.end()) {
309 ctx.SetOutput(it->second.Get<ValueType>());
310 }
311 else {
312 ctx.SetEmptyOutput();
313 }
314 })
315 .Inputs(
317 _privateComputations->inverseCompute));
318
319 // Invertible input attributes define 'computeInvertedForwardValue'
320 // computations that get their values from the inverse computation, but only
321 // if they are evaluated in a context where the inverse is being computed
322 // (i.e., when the inverse directly or transitively receives desired value
323 // overrides). Otherwise, this computation falls back to the computed value
324 // of the input attribute.
325 //
326 // This allows for correct computation of inverses for "non-spanning"
327 // controllers. I.e., this is important in cases where controller inverses
328 // can't always satisfy all desired output values, and when one controller
329 // is dependent on another. In such situations, computing "inverted forward
330 // values" allows for controller networks to produce "best try" solutions.
331 _self.AttributeComputation(
332 attributeName, _privateComputations->computeInvertedForwardValue)
333 .Callback(+[](const VdfContext &ctx) -> ValueType {
334 // If we have a computed desired value, that takes precedence.
335 if (const ValueType *const desiredValue =
336 ctx.GetInputValuePtr<ValueType>(
337 ExecIrComputations->computeDesiredValue)) {
338 return *desiredValue;
339 }
340
341 // Otherwise, return the attribute's computed value.
342 return ctx.GetInputValue<ValueType>(
343 ExecBuiltinComputations->computeValue);
344 })
345 .Inputs(
346 Computation<ValueType>(ExecIrComputations->computeDesiredValue),
347 Computation<ValueType>(ExecBuiltinComputations->computeValue));
348
349 // Invertible input attributes provide inputs to the forward computation via
350 // the 'computeInvertedForwardValue' computation.
351 _forwardComputeReg.Inputs(
352 Attribute(attributeName)
354 _privateComputations->computeInvertedForwardValue)
355 .InputName(attributeName));
356}
357
358template <typename ValueType>
359void
361 const TfToken &attributeName)
362{
363 using namespace exec_registration;
364
365 // All input attributes (invertible or not) are inputs to the forward
366 // computation.
367 _forwardComputeReg.Inputs(AttributeValue<ValueType>(attributeName));
368
369 // Non-invertible input attributes are inputs to the inverse computation.
370 _inverseComputeReg.Inputs(AttributeValue<ValueType>(attributeName));
371}
372
373template <typename ValueType>
374void
376 const TfToken &attributeName)
377{
378 using namespace exec_registration;
379
380 _invertibleOutputAttributeNames.push_back(attributeName);
381
382 // Invertible outputs support computing desired values, for inversion.
383 _DesiredValueComputations<ValueType>(attributeName);
384
385 // Invertible outputs provide inputs to the inverse computation, getting
386 // their values from the attribute's 'computeDesiredValue' computation.
387 _inverseComputeReg.Inputs(
388 Attribute(attributeName)
390 ExecIrComputations->computeDesiredValue)
391 .InputName(attributeName));
392
393 // Register an expression on the invertible output that pulls its value
394 // from the forward computation result map.
395 _self.AttributeExpression(attributeName)
396 .Callback<ValueType>([attributeName](const VdfContext &ctx) {
397 const ExecIrResult &resultMap =
398 ctx.GetInputValue<ExecIrResult>(
399 _privateComputations->forwardCompute);
400 const auto it = resultMap.find(attributeName);
401 if (it != resultMap.end()) {
402 ctx.SetOutput(it->second.Get<ValueType>());
403 }
404 else {
406 "Failed to find a result value for output attribute '%s' "
407 "when computing %s",
408 attributeName.GetText(),
409 ctx.GetNodeDebugName().c_str());
410
411 ctx.SetOutput(
413 .GetFallback<ValueType>());
414 }
415 })
416 .Inputs(
418 _privateComputations->forwardCompute));
419}
420
421template <typename ValueType>
422void
424 const TfToken &attributeName)
425{
426 // TODO: We will have work to do here that is specific to switch attributes
427 // when we implement more invertible rigging features. E.g., switch
428 // attributes play a key role in determining which controllers contribute to
429 // the current pose, and determining contributing controllers is critical
430 // for supporing advanced authoring behaviors for invertible rigs.
431
432 // Switch attributes have all the exec registrations that non-invertible
433 // attributes have.
435}
436
437template <typename ValueType>
438void
440 const TfToken &inAttributeName,
441 const TfToken &outAttributeName)
442{
443 using namespace exec_registration;
444
445 // Passthrough attributes are inputs to the forward and inverse
446 // computations.
447 _forwardComputeReg.Inputs(AttributeValue<ValueType>(inAttributeName));
448 _inverseComputeReg.Inputs(AttributeValue<ValueType>(inAttributeName));
449
450 // Passthrough attributes pass the value from the input attribute to the
451 // output attribute.
452 _self.AttributeExpression(outAttributeName)
453 .Inputs(Prim().AttributeValue<ValueType>(inAttributeName))
454 .template Callback<ValueType>([inAttributeName](const VdfContext &ctx) {
455 ctx.SetOutputToReferenceInput(inAttributeName);
456 });
457}
458
459template <typename ValueType>
460void
461ExecIr_ControllerBuilderBase::_DesiredValueComputations(
462 const TfToken &attributeName)
463{
464 using namespace exec_registration;
465
466 // The 'explicitDesiredValue' computation only exists to provide an output
467 // where desired values can be specified as overrides passed to
468 // ComputeWithOverrides.
470 attributeName, ExecIrComputations->explicitDesiredValue)
471 .Callback<ValueType>(+[](const VdfContext &ctx) {
472 ctx.SetEmptyOutput();
473 });
474
475 // The 'computeDesiredValue' computation gets its value from the
476 // 'explicitDesiredValue' computation if it provides one, or from the
477 // 'computeDesiredValue' computation via incoming connections--but only if
478 // there is exactly one desired value present on these inputs. Otherwise, no
479 // value is returned. An error is emitted if more than one desired value is
480 // present.
482 attributeName, ExecIrComputations->computeDesiredValue)
483 .Callback<ValueType>(&_GetExactlyOneDesiredValue<ValueType>)
484 .Inputs(
486 ExecIrComputations->explicitDesiredValue),
487 IncomingConnections<ValueType>(
488 ExecIrComputations->computeDesiredValue));
489}
490
491template <typename ValueType>
492void
493ExecIr_ControllerBuilderBase::_GetExactlyOneDesiredValue(
494 const VdfContext &ctx)
495{
496 const ValueType *const inputValue = [&]() -> const ValueType*
497 {
498 const ValueType *foundInputValue =
499 ctx.GetInputValuePtr<ValueType>(
500 ExecIrComputations->explicitDesiredValue);
501
502 for (const ValueType &value :
504 ctx, ExecIrComputations->computeDesiredValue)) {
505 if (!foundInputValue) {
506 foundInputValue = &value;
507 } else {
508 // TODO: We will introduce an ExecValidationErrorType enum to
509 // specifically identifiy this error condition.
511 "Found more than one desired value for node %s",
512 ctx.GetNodeDebugName().c_str());
513 return nullptr;
514 }
515 }
516
517 return foundInputValue;
518 }();
519
520 if (inputValue) {
521 ctx.SetOutput(*inputValue);
522 } else {
523 ctx.SetEmptyOutput();
524 }
525}
526
527PXR_NAMESPACE_CLOSE_SCOPE
528
529#endif
The top-level builder object (aka, the self variable generated by EXEC_REGISTER_COMPUTATIONS_FOR_SCHE...
Builder class used to register invertible controller computations.
void SwitchAttribute(const TfToken &attributeName)
Registers a switch attribute.
EXECIR_API ExecIrControllerBuilder(ExecComputationBuilder &self, Callback forwardCallback, Callback inverseCallback)
Constructs a builder that is used to register computations that implement an invertible controller.
ExecIrResult(*)(const VdfContext &) Callback
The type for forward and inverse controller computation calbacks.
void InvertibleOutputAttribute(const TfToken &attributeName)
Registers an invertible output attribute.
void InvertibleInputAttributes(const TfTokenVector &attributeNames)
Registers multiple invertible input attributes.
void InvertibleOutputAttributes(const TfTokenVector &attributeNames)
Registers multiple invertible output attributes.
void PassthroughAttributes(const TfToken &inAttributeName, const TfToken &outAttributeName)
Registers a pair of input, output passthrough attributes.
void NonInvertibleInputAttribute(const TfToken &attributeName)
Registers a non-invertible input attribute.
void InvertibleInputAttribute(const TfToken &attributeName)
Registers an invertible input attribute.
Class used to build prim computation definitions.
This is a space efficient container that mimics the TfHashMap API that uses a vector for storage when...
iterator find(const key_type &k)
Finds the element with key k.
iterator end()
Returns an const_iterator pointing to the end of the map.
Create or return a previously created object instance of global data.
Definition staticData.h:96
Token for efficient comparison, assignment, and hashing of known strings.
Definition token.h:71
char const * GetText() const
Return the text that this token represents.
Definition token.h:179
A context is the parameter bundle passed to callbacks of computations.
Definition context.h:40
VDF_API void SetEmptyOutput() const
Sets an empty value on the output.
VdfByValueOrConstRef< T > GetInputValue(const TfToken &name) const
Returns a value from the input named name of type T.
Definition context.h:299
VDF_API std::string GetNodeDebugName() const
Returns the debug name for the node for this context.
VDF_API void SetOutputToReferenceInput(const TfToken &inputName) const
Sets the one and only output to have the same output value as the value on the output connected to in...
const T * GetInputValuePtr(const TfToken &name) const
Returns a pointer to the value from the input named name if the input has a valid value,...
Definition context.h:318
void SetOutput(const TfToken &outputName, const T &value) const
Sets the value of the output named outputName to value.
Definition context.h:375
static VDF_API VdfExecutionTypeRegistry & GetInstance()
Returns the VdfExecutionTypeRegistry singleton instance.
This class allows for construction of iterable ranges of input values.
This is a public header, but many of the symbols have private names because they are not intended for...
TfDenseHashMap< TfToken, VtValue, TfToken::HashFunctor > ExecIrResult
Map used to return results from controller forward and inverse computations.
Definition types.h:25
Derived & Callback(ReturnType(*callback)(const VdfContext &))
Registers a callback function that implements the evaluation logic for a computation.
ExecPrimComputationBuilder & Inputs(Args &&... args)
Takes one or more input registrations that specify how to source input values for a prim computation.
EXEC_API ExecAttributeComputationBuilder AttributeComputation(const TfToken &attributeName, const TfToken &computationName)
Registers an attribute computation named computationName on attributes named attributeName.
This & InputName(const TfToken &inputName)
Overrides the default input name, setting it to inputName.
#define TF_RUNTIME_ERROR(fmt, args)
Issue a generic runtime error, but continue execution.
Definition diagnostic.h:83
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition diagnostic.h:68
Attribute accessor, valid for providing input to a prim computation.
Computation value specifier, valid for providing input to any computation.
Prim accessor, valid for providing input to an attribute computation.
TfToken class for efficient string referencing and hashing, plus conversions to and from stl string c...
std::vector< TfToken > TfTokenVector
Convenience types.
Definition token.h:440