<< Newer Article #162 Older >>

More Layouts and Rendering

Continuing on from yesterday's post, let's talk about elements. Elements are really used for artwork, and allow you to combine several primitives together to form a single element, which is then referenced by an item in a view. The reason why elements are defined separately from the items in a view is because the elements may be reused multiple times, either within a single view or across multiple views.

First, let's look at a couple of elements. The first one is taken from the built-in overlay for Solar Quest:

<element name="overlay">
    <rect>
        <bounds left="0" top="0.125" right="1" bottom="0.2" />
        <color red="1.0" green="0.125" blue="0.125" />
    </rect>
    <rect>
        <bounds left="0" top="0.2" right="1" bottom="0.875" />
        <color red="0.125" green="0.125" blue="1.0" />
    </rect>
    <disk>
        <bounds left="0.47" top="0.47" right="0.53" bottom="0.53" />
        <color red="1.0" green="1.0" blue="0.125" />
    </disk>
</element>

This element, which is given the name "overlay", has three pieces to it: a solid red rectangle across the top, a blue rectangle across the bottom, and a yellow circle in the middle. Because these pieces are drawn at runtime by the rendering engine, they are automatically rendered at the proper resolution for output. You'll notice that the coordinate system here is once again arbitrary. The combined bounding box of the entire thing is (0,0.125)-(1,0.875), which happens to be a 4:3 ratio in width/height. The rendering system will examine the bounding boxes of all the pieces within an element, and scale everything within it to fit the bounds of the view item that eventually references it. See below for a full example to understand this.

Here's a second example element, taken from my conversion of the invaders.art file:

<element name="bezel">
    <image file="invadbez.png" alphafile="invadmask.png" />
</element>

This is a nice, simple example that shows a third type of piece: the image piece. Rects, disks, and images are the three types of pieces that can currently be assembled to form an element. Here you'll notice that a filename is specified, which is a source PNG file to read the data from. The PNG file can be in any of a number of formats (including palettized, RGB, and RGB with alpha). As with the old artwork system, you can also specify a separate PNG file to contain the alpha mask, if you don't have a good editor that can output a single RGB+alpha PNG. The final thing to notice about this example is that it doesn't specify any bounds. By default, if you don't specify any bounds, the bounds are assumed to be (0,0)-(1,1). Since the image is meant to define the entire element, this is fine.

And here is a third example element, taken from my conversion of the turbo.art file:

<element name="digit" defstate="10">
    <image file="digit0.png" state="0" />
    <image file="digit1.png" state="1" />
    <image file="digit2.png" state="2" />
    <image file="digit3.png" state="3" />
    <image file="digit4.png" state="4" />
    <image file="digit5.png" state="5" />
    <image file="digit6.png" state="6" />
    <image file="digit7.png" state="7" />
    <image file="digit8.png" state="8" />
    <image file="digit9.png" state="9" />
    <image file="digitX.png" state="10" />
</element>

This is a particularly interesting element. Like the previous one, it is based on images. Unlike the previous element, it supports multiple states. By default, if you don't specify any particular state, an element piece is drawn regardless of the current state of the item that contains it. In this case, we are displaying an LED digit, which can be in any of 11 different states (0-9, plus all off). You'll note the defstate attribute in the element item itself; this specifies the default state of the element, provided the item doesn't specify any particular default state. In this case, we default to the "off" state. Inside the element are 11 bitmaps, each of which is only visible in one of the 11 different states. Thus, when the view item containing this element is set to state 7, for example, only the digit7.png image will be visible; everything else will be hidden.

Okay, so now that we've examined the types of pieces that make up an element, let's look at a real life example. In this example, we will convert the existing invaders.art file over to a layout, and show how the existing overlays, bezels, and backdrops are handled in the new world. Here is the existing .art file (stripped down to its bare elements):

backdrop:
    file = invaders.png
    layer = backdrop
    priority = -2
    visible = 1
    position = 0.0,-0.14,1.29,1.15

// we use an overlay here to change the bright white color used by
// the game down to a more pleasing blue-tinted light gray
// this has the side effect of making the game graphics appear a bit
// transluscent over the backdrop
overlay:
    file = tintover.png
    alphafile = tintmask.png
    layer = overlay
    priority = -1
    visible = 1
    position = 0.0,0.0,1.0,1.0

bezel:
    file = invadbez.png
    alphafile = invadmask.png
    layer = bezel
    priority = 0
    visible = 1
    position = -0.3333,-0.8206,1.7179,1.7851
    brightness = 1.0

So, we have three pieces of art: a backdrop piece, an overlay piece, and a bezel piece. We need to first define the necessary layout elements for these pieces, and then we need to assemble them into a view.

First let's consider the overlay element, because we're not going to translate that directly. What the old overlay element did was to apply a fake overlay over the screen that reduced the brightness of the screen so that when it was added to the backdrop graphics, it had a bit of a translucent effect. The reason we used to need this is because the old .art files didn't reference the screen directly, so there was no control over how the screen graphics were drawn. This is no longer the case. As mentioned in my last article, you can specify color and alpha values for screen items in the view directly, so we'll skip the overlay for now.

The backdrop piece is simply a single PNG image with no alpha layer, so we create a new element called "backdrop" with a single image component:

<element name="backdrop">
    <image file="invaders.png" />
</element>

The bezel piece is similar, but also has an alpha layer mask specified in a separate PNG file. We'll create a second element called "bezel" with an image component that specifies both files. This is exactly the 2nd example I showed above:

<element name="bezel">
    <image file="invadbez.png" alphafile="invadmask.png" />
</element>

Now, to construct the view. This is where things get a little tricky. In the old system, the screen was hard-coded to positions (0,0)-(1,1), and the artwork pieces were all defined relative to those coordinates. There are two problems with this. First, the screen was referenced in its native, nonrotated state; invaders normally runs rotated 270 degrees, which requires swapping the X/Y coordinates and then flipping the Y coordinate. Second, the artwork aspect ratio used to stretch based on the aspect ratio of the game; this is no longer the case -- all view items are assumed to be square pixels.

So let's tackle one problem at a time. In the old coordinate system, the backdrop was positioned at (0.0,-0.14)-(1.29,1.15). The first thing we need to do is fix the orientation. As I said before, to do this we need to swap the X/Y coordinates and then flip the Y coordinates. Swapping is easy, and gives us (-0.14,0.0)-(1.15,1.29). To flip in the Y direction, we need to replace each Y coordinate value with (1.0 - Y). This gives (-0.14,1.0)-(1.15,-0.29). The only other tweak we need to make is that the left coordinate value must be less than the right coordinate; similarly, the top must be less than the bottom. After doing the Y flip, we've violated these rules, so we need to swap the Y coordinates and end up with (-0.14,-0.29)-(1.15,1.0).

With me so far?

So now we've rotated the coordinates around so that they are in the correct location with respect to the screen, except that we are still assuming the screen goes from (0,0)-(1,1). If we use these coordinates for the screen, however, the screen graphics will be stretched out to a square, and that's incorrect. The rotated monitor has a 3:4 aspect ratio, meaning that the X coordinates need to be shrunk down by a factor of 3/4 or 0.75. So, applying a 0.75 factor to the X coordinates, we get (-0.105,-0.29)-(0.8625,1.15). Whew! Putting this into a backdrop item for our view, it would look like this:

<backdrop element="backdrop">
    <bounds left="-0.105" top="-0.29" right="0.8625" bottom="1.0" />
    <orientation rotate="270" />
</backdrop>

Wait a minute! I thought we already rotated the image! Why is there an orientation tag in there? Well, if you open the PNG file that the "backdrop" element references, you'll see that the artwork is on its side. The orientation parameter rotates the graphics (not the coordinates) into the correct position.

Applying the same transform to the bezel, we get:

<bezel element="bezel">
    <bounds left="-0.61545" top="-0.7179" right="1.338825" bottom="1.3333" />
    <orientation rotate="270" />
</bezel>

Finally, the screen itself. As I mentioned before, we can ignore the old overlay technique and directly reduce the color of the screen item in the view directly:

<screen index="0">
    <bounds left="0.0" top="0.0" right="0.75" bottom="1.0" />
    <color alpha="0.75" />
</screen>

Finally, we wrap these three items up in a view, and produce the final XML:

<?xml version="1.0"?>
<mamelayout version="2">
    <element name="backdrop">
        <image file="invaders.png" />
    </element>

    <element name="bezel">
        <image file="invadbez.png" alphafile="invadmask.png" />
    </element>

    <view name="Full Artwork">
        <backdrop element="backdrop">
            <bounds left="-0.105" top="-0.29" right="0.8625" bottom="1.0" />
            <orientation rotate="270" />
        </backdrop>

        <screen index="0">
            <bounds left="0.0" top="0.0" right="0.75" bottom="1.0" />
            <color alpha="0.75" />
        </screen>
    
        <bezel element="bezel">
            <bounds left="-0.61545" top="-0.7179" right="1.338825" bottom="1.3333" />
            <orientation rotate="270" />
        </bezel>
    </view>
</mamelayout>

You can try this today with a NEW_RENDER build and see it all work. The nice thing is that with the new rendering system, all the artwork is scaled natively to whatever resolution your screen is, so you don't lose any quality. Plus, it should be a whole lot faster because your graphics card (assuming it isn't ancient) is doing all of the heavy lifting and compositing.

Next I'll talk about Turbo a bit and show you how to vastly simplify the giant .art file that is currently used.