The Translator Component

The Translator Component offers a framework for building a translator upon. It facilitates scene translation and updates through its built-in mechanisms for tracking dependencies between translated elements. It also offers base classes and various helpers that take some of the translation task out of the hands of the plugin developer.

Render Session Context

This component is entirely optional. A renderer built on top of the Rendering API does not have to use the Translator Component.

The Translator

The translation system is built on the concept of a graph of translators. The translators are responsible for translating a 3ds Max entity into the rendering engine's own representation. Thus, the translators take 3ds Max scene elements as inputs and produce the rendering engine's elements as outputs. Each translator is dependent on the output of other translators, thus forming a dependency graph.

Translation

The translator's main task is to convert a 3ds Max scene element into the rendering engine's representation. As such, its foremost method is:

virtual TranslationResult Translate(
        const TimeValue translationTime,
        Interval& newValidity,             
        ITranslationProgress& translationProgress,
        KeyframeList& keyframesNeeded) = 0;

This method does the actual translation work, and may also be called to update an item which has already translated. Updates happen in the following cases:

Translator Keys

Translator Keys are the input to a Translator, and as such encapsulate everything the Translator needs to perform its task. This may be an INode, a Mtl, or else. The Translator Key is named so because it is also used as a key for a lookup table. As such, it is a unique identifier for a Translator, enabling automatic elimination of duplicates.

Class TranslatorKey is the base class for all Translator Keys, though in general it should not be used directly. Instead, it is recommended to use the built-in sub-classes which implement specializations for keys storing a pointer, a reference, or an arbitrary struct.

Class TranslatorKey uses hashing to perform efficient lookups. Its main methods are:

    TranslatorKey(const size_t hash);
        size_t get_hash() const;
    virtual bool operator==(const TranslatorKey& rhs) const = 0;
    virtual std::unique_ptr<const TranslatorKey> CreateClone() const = 0;

The CreateClone() method enables the plugin developer to allocate temporary keys, on the stack, for efficiency; the system will clone the key if and only if it needs to create a new entry in the lookup table.

The built-in specializations of class TranslatorKey are:

Translator Outputs

The call to Translate() will typically result in the creation of one or more outputs, on the Translator. Class Translator exposes these methods for managing outputs are:

    size_t GetNumOutputs() const;
    template<typename OutputType>
    std::shared_ptr<const OutputType> GetOutput(const size_t index) const;
    void SetNumOutputs(const size_t num);
    void SetOutput(const size_t index, std::shared_ptr<const ITranslatorOutput> output);
    void ResetOutput(const size_t index);
    void ResetAllOutputs();

Outputs may require construction and destruction - for example, if the output is a handle to an entity in the rendering engine's database, then destruction of the output requires the handle to be freed. For this reason, outputs derive from ITranslatorOutput - which defines a virtual destructor.

Outputs are stored using shared pointers. This is to facilitate the management of outputs which end up being shared across translators. For example, an instance translator may be dependent on a material translator; the material handle is output by the material translator and used by the instance translator. Internally, the system shares the outputs of a Translator with its dependents; using a shared pointer results in the output (e.g. the material handle) being destroyed only once it is no longer in used.

Class Translator also provides functionality for storing simple output values - i.e., outputs which contain a POD value, which does not require constructor nor destruction (mainly integers, bools, floats, etc.). These avoid creating specialized outputs for simple things:

    template<typename SimpleValueType>
    void SetOutput_SimpleValue(const size_t output_index, const SimpleValueType& value);
    template<typename SimpleValueType>
    SimpleValueType GetOutput_SimpleValue(const size_t output_index, const SimpleValueType& default_value) const;

Note that, when Translate() is being called to update an already-translated entity, the outputs need not be re-set. For example, if a material translator were to be re-translated to update a single parameter, then output (a material handle) may still remain valid, as the material handle would not have to be re-created. But if the material changed in a way that required a whole new material handle to be created, then the old output must be replaced. The system would then automatically invalidate dependent Translators, and the shared pointer would take care of garbage collecting the old material handle once it were no longer referenced by these dependents - i.e., after all dependents would be fully updates.

Acquiring Dependencies

When a Translator requires the output of another Translator to perform its own translation, it must acquire a dependency to that other Translator. Following this acquisition, the dependent Translator may use the outputs of the Translator it just acquired. The acquisition is performed using this method from class Translator:

    template<typename TranslatorType>
    const TranslatorType* AcquireChildTranslator(
        const TranslatorKey& key,
        const TimeValue t,
        ITranslationProgress& translation_progress,
        TranslationResult& result);

The dependent translator must provide a key, which it builds according to the entity that needs to be translated. The system will then automatically:

  1. Check if a Translator already exists for the given key, create one if not.
  2. Translate or update the Translator if needed (based on validity).
  3. Have the dependent Translator acquire all the outputs of the acquired Translator, such that they are not garbage collected while in use. Allocation of New Translator New Translators are allocated using a method on class TranslatorKey:
    virtual std::unique_ptr<Translator> AllocateNewTranslator(
        TranslatorGraphNode& translator_graph_node,
        const IRenderSessionContext& render_session_context,
        const TimeValue initial_time) const = 0;

Thus, in addition to encapsulating a Translator's input and being a lookup key, class TranslatorKey behaves as a factory for class Translator.

The Translation Manager

The Translation Manager is the entry point to the Translator Component. It is where the root of the translation graph is acquired. Class ITranslationManager exposes these methods for translating and updating the root of the translation graph:

    template<typename TranslatorType>
    TranslatorType* TranslateScene(
        const TranslatorKey& key,
        const TimeValue t,
        TranslationResult& result);
    virtual TranslationResult UpdateScene(
        Translator& rootTranslator,
        const TimeValue t) = 0;

The plugin developer may check whether any part of the graph is invalid, and would benefit from an update, using this method:

    virtual bool DoesSceneNeedUpdate(
        const Translator& rootTranslator,
        const TimeValue t) = 0;

When the plugin is done rendering a scene, the root translator is to be released using:

    virtual void ReleaseSceneTranslator(const Translator& rootTranslator) = 0;

An instance to a the Translation Manager may be acquired from this method on class IRenderSessionContext:

    virtual ITranslationManager& GetTranslationManager() const = 0;

Translator Base Classes

To facilitate the implementation of a full translator, the Rendering API provides base classes for commonly used translators. These perform the basic tasks of: monitoring changes, calling RenderBegin() before translating/updating, and providing basic evaluation functionality. The API currently offers base translator classes for:

Translation Helpers

The Rendering API also provides specialized helper classes that implement common, but difficult tasks. The API currently offers these helper classes: