Tuesday, March 26, 2013

Marching Orders

In my last post, I compared objects in the Mana internal directory structure to the files residing in folders on your computer. Taking that a step further, Mana objects have something else in common with files. They can be put into one of two general categories - data or executable (functional).

Most files on your computer are simply data that is read in by applications. Similarly, most objects in Mana are merely containers. Models, widgets, lights, cameras, and so forth are all basically data associated with names. But applications are driven by functions that carry out instructions on those objects.

In Mana, functions are objects like any other and, in a sense, most objects act like functions.

Confused? I don't blame you!

Mana operates something like a set of dominoes. One object of your choosing is the start of a chain of events that unfolds the purpose of your application. Every object has a set of things in common and chief among those is an Update method.

That is important! So I shall reiterate!

Every object has an Update method and all objects are linked in a hierarchy. Updating an object cascades down through all of its children and their children and so on. Like dominoes! If you want your application to run in a loop, use a looping function at the top of your chain.

Now, back to functions! Here is a simple, and rather silly, one written in Lua.

function MakeMultipleWindows( n )
    for i = 1, n, 1 do
      mkWindow( "Window" .. i, 0, i * 110, 200, 100 )
    end
end

It expects one integer parameter and makes that many windows. Functions written like this are available only in Lua and there's nothing wrong with that. Entire applications can be written this way and there's nothing stopping anyone from sharing scripts!

However, if a function is to be executed as part of a chain, it is best registered as an object. That declaration looks something like this.

MANA.RegisterFunction(
    function MakeMultipleWindows( n )
      for i = 1, n, 1 do
        mkWindow( "Window" .. i, 0, i * 110, 200, 100 )
      end
    end,
    "MyFunctions/MakeMultipleWindows",
    {MANA.META.INT},
    {}
)

The MakeMultipleWindows function is registered as a function object that can be cloned and attached to other objects and have children of its own. I'll leave the breakdown of the syntax for later. One other benefit of registering functions is that they are packaged as part of the module and that makes distribution simpler.

As you may have guessed, linking functions together in combinations can have powerful results!

Wednesday, March 20, 2013

Understanding Parents

All objects in Mana belong to a system reminiscent of a family tree, or if you prefer, a system of folders (directories) and files. The root object is always a module and can be used in combination with other modules in a variety of applications. Also, modules can be distributed on their own so that they may be shared and used in numerous projects.

Objects that are in directories or that are directly linked to other objects can be found using a path name.

    //Prototypes/Widget:Window

The above statement refers to the Window prototype object in the Prototypes/Widget directory. The two slashes at the beginning indicate that it is found in the main tree. The colon represents the end of the directory path and the start of the object path, but the parser is forgiving and will accept a slash. The following is also valid.

    //Prototypes/Widget/Window

Widgets are often made up of sub-widgets. Objects that are children of other objects can also be accessed using path names.

    MANA.Find("//Prototypes/Widget:ScrollPanel/ScrollArea")

The code above returns the scroll area of the prototype scroll panel. Mana uses a prototype/clone system for creating and extending objects. Prototypes are objects with which the end user does not directly interact, but are strictly used to define other objects. When a prototype is cloned, its properties (fields and methods) and all of its children are cloned and become part of the new object.

Here is that window definition from my last post, with some additions.

MANA.Start()
    MANA.Clone( "//Prototypes/Widget/Window", "MyWindow", -1, "//MyGui" )
    MANA.THIS:SetPosition( 50, 50 )
    MANA.THIS:SetSize( 300, 200 )
    MANA.Clone( "//Prototypes/Widget/Button", "OkButton", -1, "..MyWindow/WindowPanel" )
    MANA.THIS:SetAlignment( -1, 2, -2, -1, 1, -2 )
    MANA.THIS:SetSize( 80, 30 )
MANA.Finish()

The second line (after Start) creates a clone of the window prototype, names it "MyWindow", and attaches it to the end of the list of child objects for another object in the tree called "MyGui." Any object created using Init, Clone, or ReadXml between the Start and Finish statements is available on an initialization stack. It can be accessed immediately using the THIS field of the MANA table or using Find with two dots in front of its name.

    MANA.THIS:SetPosition( 50, 50 )

The code above sets the position of the last created object (MyWindow) to 50, 50.

    MANA.Clone( "//Prototypes/Widget/Button", "OkButton", -1, "..MyWindow/WindowPanel" )

This line creates a clone of the button prototype, names it "OkButton", and attaches it to "MyWindow." The two dots refer to an object previously created on the stack.

NOTE: The WindowPanel sub-object is referenced because windows are made of other components and attaching directly to the window would overdraw them. Your button may draw on top of the frame or title bar and that simply won't do!

Here is an alternative.

    MANA.Clone( "//Prototypes/Widget/Button", "OkButton", -1, ">>MyWindow" )

In this variant, the two dots are replaced with two right angle brackets and WindowPanel is omitted. Some objects have an attachment facility, or a set of instructions that regulate how other objects get attached. In this case, the window is being instructed to process the button and attach it to the correct location automatically.

Friday, March 15, 2013

I Love Lua

Warning: Those technical posts I promised two years ago are finally happening! :-)

Has it been five months already!?

It is time to announce that my project has an official name - Mana. The word originated in the Pacific islands and refers to an "impersonal force" that resides in all things. Gamers, especially RPG gamers, should recognize it instantly. It is often used in games to quantify magic or life force in the form of points.

It is also time to express my love for the Lua scripting language. My first stab at getting Mana and Lua working together was actually quite some time ago. It went so well that I ended up abandoning my own scripting language and embracing Lua for its popularity, speed, and ease of use. The integration is about ninety percent - almost anything can be done externally in script!

Using Lua, there are essentially three ways to create objects. There is a low-level interface, common to all objects and designed to provide consistency for all classes. On top of that, there is the MANA stack, which uses the same low-level methodology, but keeps track of a few things to make the process simpler. Then, there are the more convenient “mk” functions that are single line object initialization functions.

Most of the time, the “mk” functions will probably be the most commonly used.

tex = mkTexture( "MyTexture", 256, 256 )
--Returns 256 x 256 blank texture.

v3 = mkVec3( 0, 1, 0 )
--Returns an ‘up’ vector.

win = mkWindow( "MyWindow", 50, 50, 300, 200 )
--Returns a window widget.

They initialize objects based on whatever the currently active template calls for. But more on those later. For now, I’ll write a bit about the MANA stack. This is a low-level interface that provides a typical “start new object, modify object attributes, then finish object” methodology.

There are three standard functions for creating an object - ANY object. This applies to widgets, models, textures, materials, and their components - EVERY object that is in Mana. Those three functions are Init, Clone, and ReadXml (ReadBin may be added later).

    Init - creates a new superclass object from scratch.
    Clone - creates one from an existing object.
    ReadXml - reads the data for an object from an xml file and the return value may be a new object or a clone, depending on the file.

Each of the creation functions takes four parameters of a similar nature.

    Init( superclass, class, index, parent )

Superclass and class are strings describing the new object. Superclass refers to all of the base type objects available in Mana (Widget, for example). Class is used to define a new class or, for objects that can’t be reclassified, a name.

    Clone( path, name, index, parent )

Path is the string path to the object to be cloned, and name is the new object name.

    ReadXml( filename, path, index, parent )

Filename is, of course, the file to be read, and path is the path to the node that contains the data.

All three functions use index and parent. Parent is the string path to the object that the new object will be associated. Index is used when a new object is being created to replace another. However, the default is -1, meaning “append new object to the end.”

The following code does the same thing as the mkTexture and mkWindow statements above, only much more verbose!

MANA.Start()
    MANA.Init( "Texture", "MyTexture", -1, "++Textures" )
    MANA.THIS:SetSize( 256, 256 )
MANA.Finish()
MANA.Start()
    MANA.Clone( "//Prototypes/Widget/Window", "MyWindow", -1, "//MyGui" )
    MANA.THIS:SetPosition( 50, 50 )
    MANA.THIS:SetSize( 300, 200 )
MANA.Finish()

But why would anyone want to write all that code to do something so basic?

Why, indeed!! It is really just a common way of creating all objects of all types. Start creating an object with its default values, then change the parameters, then finish with an object that is available to be cloned and used for all sorts of things. If I had omitted the SetSize line from the texture code, for example, it would still return a 256 x 256 because those happen to be the default values.

But wait! What does that parent value mean, exactly?

All objects in Mana are linked together in a hierarchical structure in a way that resembles files and folders on your hard drive. In my next post, I’ll cover that in more detail.