1. Design

This document explains the internal structure of the engine, as well as several design decisions. You don’t have to read it in order to use the library, but understanding how it works never hurts. Besides, it can help figuring out bugs in your applications.

Structure

The engine revolves around scenes. An scene is an isometric pseudo-3d space, created from an XML definition file. The engine can show one scene at a time and allows fast switching betweeen scenes. An scene can be as small as one room and as big as your machine can handle. Each scene contains unlimited elements. Elements are explained further later in this document. For now lets just remenber that anything in you scene is an element. Cameras, lights and non-physical stuff are elements as well as walls, floors, and characters. Talking OOP, everything in your scene inherits form the Element object.

Elements have 3d coordinates. The Axis in the Filmation Engine go like this. Negative values are not allowed.

positive Z
|
|     / positive X
|   /
| /
(0,0,0) X/
 \
   \
     \
       \ positive Y

Elements can be roughly divided in two: structural and non-structural. Structural elements are walls and floors. Everything else is non-structural.

Each scene is internally represented as a 3d grid. Imagine your typical isometric engine, made of small squares, as a chessboard, but in three dimensions. Structural elements ( the walls and floors in the scene ) are forced to be along the axis of this grid, leaving the cells in between. That is, a wall is either left of a cell or right of a cell, but never crosses one.

The same thing applies to floors. This is a vital point in the rendering calculations, as makes it all much easier and less CPU-consuming (I’ll explain it later). You can adjust the grid’s size in the XML file that defines the scene, therefore you can have different grid sizes for different scenes in the same game. The bigger the cells, the faster the rendering, so when you design your scene take it into account. If your scene needs narrow corridors, you’ll need a small grid size, and it will affect performance. It is not a big impact, but keep that in mind.

When you attach walls and floors to your scene, their positions and sizes are corrected to fit the grid. For example: you have a 50px grid and you add a wall 140px long. It will be scaled to be 150px, so it occupies 3 cells. This applies to the height as well.

Deciding the size of your grid is a key point in designing your scene, but don’t be afraid to change it as you progress in your design. You define your elements’ size and position in pixels and the engine then generates the grid and adjusts everything to fit. Also, this restrictions only apply to structural elements. If you add a tree, for example, it can be any size and occupy any coordinate.

Non-structural elements are cameras, lights, and objects:

Cameras behave in the same way as cameras in a 3d engine. You can have several cameras per scene. The “current” camera defines what is being shown. The scene provides an interface to switch between cameras.

At this moment there are global lights and omni lights. Omni lights behave as 3dMax omni lights: a point in space projecting light in all directions with given intensity and decay. Sort of an sphere that shows itself as a circle when in reaches a surface. Global lights define what you see when no light is affecting an element. If an scene has no global light, elements without lights affecting them will be 100% black. The Sun is NOT a global light, it is an Omni light of Infinite radius and no decay. The sun can move and project shadows, global lights don’t.

Object covers pretty much anything visible that is not an structural element. Typically an obect is a normal flash Movieclip that you place in your scene. By default, objects can’t be moved and this is useful for speeding up zSorting and lighting calculations.

To create mobile objects ( people would be the most obvious example ) use characters. Characters are basically moveable objects. Characters can be created dynamically at runtime, objects can’t.

Lighting and shadowing

By far the most complex thing in the engine is the light rendering algorythm:

The main purpose of the grid is optimizing rendering calculations of lights and shadows. Rendering a light consists of two steps:

  1. Decide which elements are within reach of the light and which other elements are casting shadows over them.
  2. Project and draw the lights and shadows.

Step A takes more CPU than step B, therefore we want to reduce it as much as possible. Using the grid, we can assume that the result of the A step for any light source inside the a cell is the same. Therefore, those calculations are performed the very first time needed for each cell and then cached internally. When we render a light which is in a cell that has not yet ben used, the algorythm calculates which elements are “visible” from that cell. Then it calculates if the visible elements have other elements in front of them that either cover them completely or cast partial shadows on them. All this information is cached internally. Now, any light being rendered from a point inside this cell can reuse this information. This really speeds things up.

Step B happens everytime a light moves. It recalculates this light’s hotspot and shadow projections for all elements affected and redraws them. Whereas the visibility info is “cached” and assumed to be the same inside every cell, the projections are calculated precisely for each coordinate. If you think your shadow and lighting is not as precise as you would like to be, try decreasing your scene’s cell size, but keep in mind it will affect performance and memory usage.

Z-Sorting

Second usage of the grid is dynamic zsorting of all elements in the scene. When the engine builds an scene, it sorts all planes ( walls and floors ) and all cells inbetween. Every cell is assigned a zIndex. It is quite tricky, because a wall can be very long and still it is only a single graphic with a single zIndex: you can’t simply sort by distance to camera. Once this is complete, moving objects simply get assigned the zIndex of each new cell they enter, keeping calculations to a minimin.

Keep in mind that whereas the engine does not limit the amount, size and position of any of your elements, you could accidentally define an unsolvable situation. For example, wall A overlaps wall B, which overlaps wall C which, later on, may overlap wall A again. In these cases you don’t get an error message: it simply displays wrong. Should you end up with such malfunctions, try spliting your walls.

Dynamic scenes

You can add and remove elements from your scene after it has been created, with some limits. Structural elements ( floors and walls ) cannot be altered once the scene has been loaded. The reason is that once the scene it is loaded, lots of things are precalculated. This involves z-Sorting of all walls and spaces between them ( depending on the scene topology, it is a complex calculation ) and lots of wall-sorting that helps the light-rendering engine. If walls and floors were allowed to change, all these calculations would have to be redone. I can be implemented, but as of this release, It isn’t. All the other elements can be added and removed on the fly.

As of this release, the supported dynamic elements ( dynamic means that it can move and it can be created, destroyed ) are cameras, lights, characters and bullets.

Not being able to add or remove walls doesn’t mean you can’t implement doors and barriers in you game. The engine provides a way of defining “holes” in your structural elements, and these holes can be opened and closed, thus allowing doors, windows, traps and so on.

You can’t move elements from one scene to another. If you want an element to exist in different scenes you have to create it in all of them. This is the typical case of the main character in any game. The guy will exist as a different element in each scene it is meant to visit, and you will program your game engine to place each copy of the element in the appropiate place ( the door you used to enter the room, for example ).