Writing Xfrog 4 Plugins

This document is meant as a guide for implementing Xfrog 4 Plugins for a variety of 3D Applications (such as CINEMA 4D, Maya, 3ds max, etc.). There are a few terms used a lot throughout this document, which in this context are supposed to mean the following:

  • The Core is the main Xfrog library which is included by each Plugin and which provides the main Xfrog functionality.
  • The Host is the respective 3D Application in which Xfrog is going to be included in as a Plugin.
  • The Plugin is the actual part of the program to be written as a plugin for the Host which makes communication between Host and Core possible. In other words: it makes the functionality of the Core accessible from within the Host.

Overview

An Xfrog Plugin acts primarily as a translation layer to exchange information between the Host and the Core. This data exchange is bidirectional. This means there are functions being called by the Core to send commands to or request information from the Host as well as functions used by the Host to send commands to or request information from the Core.

This bidirectional communication can (and will) happen in a nested manner when, for example, the Host calls a Plugin function, which then calls a Core function, which then again calls a Plugin function to get information from the Host.

The following chart shows the typical data flow:

The whole lifetime of an Xfrog Object can be divided into two phases, the Initialization Phase (yellow) and the Evaluation Phase (green).

The Initialization Phase only occurs when a new Xfrog Object is generated by the Host. When the Object is Initialized or Constructed (some Host applications allow their plugins to overload a separate initialization function, others do all the initialization work in the constructor) the Plugin is supposed to call the init() function. The init function will then call functions of the Plugin which should create the datastructures necessary to store the objects' parameters in the Host. Examples of the init functions are initFloat(), initObject(), initCurve(), etc. This way all parameters are stored by the Host and the Host can do any kind of operation on these values (animation, load, save, display, edit).

The Evaluation Phase can happen as often as the Host requests it and its main purpose is to actually execute the actions the Xfrog Object is supposed to do (like generating geometry, multiplying subobjects, deforming objects, etc.). Usually in this phase a number of queries are made to the input objects and to the object from the core by the Plugin to determine the type of the object, then calls to getLinkCSys(), getObjMesh(), etc. are used to grab the object geometry, links to multiply input objects at, and so on. There is a lot of work to do for the Plugin, which is not taken care of by the Core right now. All the multiplication of Input Objects for example is done by the Plugin itself, the Core just creates the Coordinate systems the copies are placed at.

This concept limits the abilities of the Core to manipulate the Scene in the Host. Possible actions are limited to things functions are provided for, like object multiplication, geometry generation, and geometry deformation. In most cases till today this is sufficient, but the Variation Object already doesn't fit this into this. Because of this, to make sure future expansibility isn't limited as it is right now and to take some more load from the back of the Plugin author, this is probably going to change.

The chart also clearly shows that calls to init functions and get functions from the core only happen in certain contexts. This will not change and the Plugin can rely on this, if it needs to or makes things easier.

 

Implementation Details

There are a few things every Plugin author has to take care of.

Coordinate System

 

Coding Conventions

The Coding Conventions I started for the Xfrog 4 core is based on the Linux Kernel Coding Style. Go ahead, it's a good read, especially 2-4, but don't take it too seriously. There are a few situations where it makes sense to deviate from this standard, though.

I think the most important thing about Coding Conventions is consistency. The problem with a project that is aimed to be implemented for multiple APIs is that different API's are inconsistend and thus the Xfrog project has no chance of being completely consistent. My shot on this is like follows: When you are writing functions inside a Plugin using a certain API, just use the API's naming conventions, but the Xfrog Coding style. This makes things less cluttered and might have the additional benefit that it's possible to see which function calls stay on the API side and which ones go into the Xfrog Core.

  • Tabs are 8 characters, indentation is 4 characters. This setting has to be changed when working with Visual Studio (Tools/Options/Tabs). This is mainly for cross platform reasons, the default length for Tabs is 8. Here my opinion is different from the Linux Kernel Coding style, still they are right about nesting depth!
  • The opening brace is never placed on it's own line except in function definitions.
    for (i = 0; i < 3; ++i) {
        // Do stuff...
    }

    void doStuff(int parameter)
    {
        // Do stuff
    }
  • Always put a space after reserved words, but not after function calls
    while (hungry) {
        eat(food);
    }
  • Function and Variable names start lowercase, variable type names as well as class names, etc. start uppercase
    class MyClass {
    private:
        MyClass();
        virtual ~MyClass();

        void myMemberFunction(const ParameterType ¶m)
    };

    MyClass myClass;
  • Use const! If a member function doesn't alter its class, make it const. If you pass a variable of considerable size, pass it as a reference and make it const. This won't always work, depending on the API you are using, but try to do it whenever you can.