MDL language documentation

From FreeAllegiance Wiki
Jump to navigationJump to search

Allegiance engine uses its own files format. The main format is the MDL format, of which two flavors exists:

  • plain text MDL (mdl text format), and
  • compiled MDL (mdl format).

Both formats are supported and use the same extension (.mdl). Visual inspection of the file is required to determine the format used. There is no known tool to convert compiled mdl in plaintext mdl.

The mdl text format uses a form of definition language to define objects. This is what a common piece of MDL code looks like:

use "effect"; 

frame = ModifiableNumber(1); 

object = ImportXFile("artifact.x", frame);

This sample uses an external .x file but the mdl language is complete enough to define meshes without using another file format (see samples/bgrnd03.mdl for a mdl only textured object) The mdl language scope goes beyond 3D models. It's used to defined most aspects of the game including 3D, sounds and UI panels.


MDL textual language

Syntax

As mentioned, MDL is a declarative, case-sensitive language. Most statements take the following form:

identifier = function(parameter1, parameter2);
  • MDL is a loosely typed language. You don’t need to specify the types of variables.
  • A semi-colon should end every statement, which can span multiple text lines.
  • Integer constants can be written in two ways: normally (11) or as hex (0x0B).
  • String constants are written like: “foostring”
  • Boolean constants have two possible values, true and false.
  • Comments are preceded by //.

There is a single built-in operator, the bitwise or operator |. For example, 0x01 | 0x02 evaluates to 0x03.


Info.png
Note Even though the native Number-type is a floating-point number, it is converted (cast) to a 32-bit integer when performing the bitwise operation.


Namespaces

Namespaces are a central concept in MDL. Every MDL file is a namespace, whether textual or binary. For example, to be able to use variables, constants and functions from another file or namespace called "filename.mdl", you would use the following:

use "filename";

All use-declarations must be located at the beginning of the MDL-file. Importing a namespace like this naturally imports any other namespace that is use-d in the imported namespace.

The Allegiance engine also exports about 650 functions and constants into different namespaces. These are listed on the worksheet "From C++ exported to MDL", giving the parameters and a short note on the purpose / syntax. These exports can roughly be sorted into the following categories:

  • Object creating functions ("constructors"), like ButtonPane(Image background, Number FacesFlag, bool toggles)
  • Global constants, like pi, white, black and key constants like CommandVoteYes, CommandDropChaff
  • Built-in windows, like the inventory, or the team window (F6) exporting data to be shown to their specific namespace.

In return, they expect certain controls, buttons to be specified in the MDL-file, so they can be imported back to the engine.

Some exports are seemingly only used in binary .mdl 3d models, e.g. LightsGeo and FrameData. Note that the exported namespaces are organized in a somewhat unintuitive hierarchy of imports / use-statements.


Structs

Structs are used once in the artwork shipped with Allegiance, in quickchatheader.mdl. They are also an exception to the loose typing. In a struct, you need to define the types that it contains. To be able to use the struct in c++-code, use the mdlc utility to generate an appropriate header. You can also use the keyword “extends” to use inheritance (also used in the same file).

The syntax is:

Struct structname extends parentstruct {
  identifier1 : type1;
  identifier2 : type2;
  list3 : [type3];
};

Defining a struct allows you to use the structname as a constructor / function that creates and returns an object.

Here's an example:

variableidentifier = structname(type1object, type2object, [type3obj1, type3obj2, type3obj3])


Lists

Some functions can take a variable number of arguments. A window can have several controls, a QuickChatMenu can have several entries. In this case, a list is used as an argument.

Lists in general take the form

Identifier = [item1, item2, item3];


Info.png
Note In a function call, an empty list is simply discarded. The calls Add(5,3,[],[]) and Add(5,3) are equivalent.


Tuples

In lists and variable definitions, each expression can actually contain several expressions (which may be of different types), effectively becoming a kind of unnamed struct. This is called a pair, or more precisely a tuple -- this construct can contain any number of expressions.

Assigning a pair to an identifier / variable looks like:

Identifier = (item1, item2, item3);

Using tuples in a list then becomes

Identifier = [
              (item11, item12),
              (item21, item22),
              (item31, item32)
             ];

Tuples are extensively used at the bottom of dialog.mdl as follows:

(image, side, off point, on point, transition time, consoles modes, undetectable)


let ... in

The let-construct allows you to write a number of temporary definitions, and evaluate one expression in the context of these definitions. This allows for temporarily redefining an identifier for a certain expression and making a non-global assignment (perhaps to split up a long expression into several lines?). It is never used in the .mdl-files shipped with the game. The syntax is:

Identifier = let <definitions> in <expression>;

For example, the following assigns 11 to Identifier:

Identifier = let Foo = 5;
                 Bar = 6;
             in Add(Foo,Bar);
In practice

Following is a commented real life example: AutoDownloadDialog.mdl

[codebox]use "effect"; // import effect namespace for widgets/controls use "font"; // import fonts use "gamepanes"; // import more Panes/widgets/controls use "autodownloaddialogdata"; //import data specific for this dialog

///////////////////////////////////////////////////////////////////////////// // // AutoDownload Dialog // /////////////////////////////////////////////////////////////////////////////

// // Guts //

// Create a button AutoDownloadAbortButton = ButtonPane(

                                    ImportImage("btnautodownloadabortbmp", true), //with an image,
                                    ButtonNormal,                                 //a behavior
                                    false                                         //no toggle behavior 
                                   ); 

// Create a bunch of strings. The engine will take care of those. AutoDownloadCurrentFileStringPane = StringPane(

                                              "",             //with some content
                                              Color(1, 1, 1), //a color (?)
                                              Point(220, 13), //a position
                                              JustifyRight,   //a justification
                                              smallFont       //a font
                                             ); 

AutoDownloadApproxMinutes = StringPane(

                                      "",             // do not worry
                                      Color(1, 1, 1), // about parameters
                                      Point(120, 13), // just yet, however.
                                      JustifyLeft,    // That's documented
                                      smallFont       // elsewhere :)
                                     ); 

AutoDownloadTopBarStringPane = StringPane(

                                         "Retrieving File List", // Did you know...
                                         Color(1, 1, 1),         // a single typo will make Allegiance crash.
                                         Point(150, 13),         // You can find an MDL syntax 
                                         JustifyLeft,            // checker in Cortex's blog.
                                         smallFont               // Mind the commas!
                                        );

// all kinds of indentation, tabulations and (half-done) formatting are found in the code. // The above has been pretty printed for the purposes of this guide, but we'll stop doing this now // to dip you in the atmosphere :D

// more of the same AutoDownloadMidBarStringPane = StringPane("Analyzing Local Version", Color(1, 1, 1), Point(150, 13), JustifyLeft, smallFont); AutoDownloadLowBarStringPane = StringPane("Downloading Updated Files", Color(1, 1, 1), Point(150, 13), JustifyLeft, smallFont);


// Create the progress bars FileListGaugePane = GaugePane(

   ImportImage("autodownloadgaugebmp", false), // with an image
   FileListPercentDone,                        // a progress (which is fed by the engine)
   Color(1, 0, 0),                             // a "marker" color
   Color(0, 1, 0)                              // and a "background" color

);

VerifyGaugePane = GaugePane(

   ImportImage("autodownloadgaugebmp", false),
   VerifyPercentDone,
   Color(1, 0, 0),
   Color(0, 1, 0)

);

DownloadGaugePane = GaugePane(

   ImportImage("autodownloadgaugebmp", false),
   DownloadPercentDone,
   Color(1, 0, 0),
   Color(0, 1, 0)

);


// // AutoDownload Dialog //

//We finally create the dialog

AutoDownloadDialog =

       (
           ImagePane(
               ImportImage("autodownloaddialogbmp", false),                //with a background
               [                                                           //and a list of panes
                   (AutoDownloadCurrentFileStringPane,    Point(195,185)), //with more position parameters
                   (AutoDownloadApproxMinutes,            Point(100,185)),
                   (FileListGaugePane,                    Point(91,68)),
                   (VerifyGaugePane,                      Point(91,113)),
                   (DownloadGaugePane,                    Point(91,158)),
                   (AutoDownloadAbortButton,              Point(210,200)),
                   (AutoDownloadTopBarStringPane,         Point(100,51)),
                   (AutoDownloadMidBarStringPane,         Point(100,96)),
                   (AutoDownloadLowBarStringPane,         Point(100,140))
               ]
           )
       );

// This ImagePane-object is imported back to the engine.

[/codebox]

Tools

MDLC – the mdl compiler

The MDL compiler offers a numbers of features, each with its own syntax. Here's a list of accepted MDLC syntaxes:

  1. mdlc -convert input output.mdl
    -convert
    converts ASCII .mdl to binary .mdl
    input
    source filename without .mdl ending
    output.mdl
    destination filename with .mdl ending
  2. mdlc -optimize input output
    -optimize
    optimize 3d-models in .mdl-format. Removes diffuse/specular/emissive color information and consolidates texture information. Precalculates certain transforms, etc... in one word: optimizes.
    input
    the file (stripped of its .mdl extension) to optimize
    output
    destination filename, without .mdl ending
  3. mdlc -compressanim xframes yframes input output
    -compressanim
    applies a form of RLE (Run Length Encoding) to an animation (presumably a .bmp with the frames stored side-by-side in rows and columns).
    xframes
    the number of animation frames in one row
    yframes
    the number of animation frames in one column
    input
    the filename of the .bmp to compress
    output
    the destination filename, without the .mdl extension
  4. mdlc -convert input output
    -convert
    converts a .bmp to bmp.mdl
    input
    filename of source bitmap
    output
    destination filename (without .mdl ending)
  5. mdlc -info input
    -info
    prints some information about the model (how many triangles each LOD has)
    input
    the name of a .mdl-file (without .mdl-ending) describing a 3d-model with level-of-detail information
  6. mdlc -genheader input output
    genheader
    Generates c++ code for handling any structs defined in a .mdl-file.
    input
    the .mdl (without .mdl-ending) that contains struct definitions.
    output
    filename for writing the code to

External Links