Palomino Modules

©2006  Jim E. Brooks   http://www.palomino3d.org   http://www.jimbrooks.org/sim

See also Palomino Design and Palomino Implementation.


Contents


Introduction

Game elements were influenced by video games such as Blue Max (Atari/C64), Zaxxon, Microprose F-19, Terminal Velocity (DOS), Rogue Squadron, and by movies such as Star Wars, Iron Eagle, and Top Gun.

Palomino is the name of the core 3D renderer (3D engine). Game code is separated as "game modules".


Game design issues

The biggest issues facing a flight-simulator video game are the player getting lost and not knowing what the goals are. These issues can be solved by a map screen and a goals screen (they could be combined). The map screen would show a flight path and targets. The goals screen would show targets destroyed, list of accomplished mission objectives, etc.


Interface between 3D Engine and Modules

Overview

Palomino is comprised of 3 major modules: the 3D engine, the base game module, and a specific game module. The modules are stacked in a hierarchy: higher modules can directly call base modules but not v.v. The idea is that base modules shouldn't know about the higher modules.

The interface is comprised of C++ classes, a few C++ global objects, and an event/listener mechanism. The event/listener mechanism allows base modules to indirectly call higher modules.

The engine supplies base C++ clases for the base game module. In turn, the base game supplies base C++ classes for specific game modules.

Interface


Classes

Dyna-derived classes


Events and Listeners

Events are broadcast to listeners. Listeners register themselves with the Event object. Uninteresting events can be ignored by simply not listerning for them.

Obviously, the event/listener mechanism is well-suited for asynchronous events, but it's useful in other ways.

Events objects can be centralized in a C++ struct which makes them immediately recognizable as a high-level interface:

class EngineEvents
{
public:
    Event         mEngineReady;       // other modules should listen for this, not gfxsysReady
    Event         mGfxsysReady;
    Event         mExit;
    Event         mAnimate;
    Event         mTick;
...
};

And it provides flexibility. The event sender doesn't need to know who is listening. This solves the problem in a module hierarchy where a base module can't know which functions to call in a higher module. In the following example, the base game module waits for the Engine to become ready. The Engine, which knows nothing about the higher modules, broadcasts an "engine ready" event.

    gEngineEvent.mEngineReady.Listen( Listener_GameBase_EngineReady, start );
...
void
Listener_GameBase_EngineReady( void )
{
    gGame.mInputQueue.Enable( &gJoy );
    gGame.Start();  // engine is ready, let's play!
}

State-machine

Rationale for a state-machine

Features of the state-machine

State-machine instructions

State-machine code is written as a C array using macros provided by gfx_machine.hh.
*_T/F means to conditionally execute the instruction if the FLAGS register is true/false.
DEF_*() macros are helpers to define SM instructions (assembly-level).
MNEM_* are placeholders for instructions the SM compiler must resolve.
OPC_* are enums of SM opcodes (machine-level).

List of state-machine opcodes (very subject-to-change).

Writing state-machine code

Documented here is a guide and the rules for writing state-machine code. For exact details, see gfx_machine.cc.

Compiling

Compiling resolves label addresses which are the targets of jump instructions.

Compiling does these steps:

Example of state-machine code

Machine::Opcode gSmprocPursue[] =
{
DEF_PROC_BEGIN( "Pursue" ),          // every procedure must begin with DEF_PROC_BEGIN()

    DEF_CALL( sSmprocPursueSub ),    // call a SM procedure

    DEF_FUNC( PursueIfRoll ),        // call a C function, returns a bool into 1-bit true/false FLAGS register
    DEF_JUMP_F( "SkipRoll" ),        // jump if flag is false (if PursueIfRoll() returns false)
    DEF_FUNC( PursueIfInlineWithTarget ),
    DEF_JUMP_T( "SkipRoll" ),
        DEF_FUNC( PursueRoll ),      // state-machine is suspended
        OPC_SUSPEND,
        DEF_RERUN(),                 // re-run this procedure
    DEF_LABEL( "SkipRoll" ),         // defines a label

    DEF_FUNC( PursueIfPitch ),
    DEF_JUMP_F( "SkipPitch" ),
        DEF_LABEL( "Pitch" ),
        DEF_FUNC( PursuePitch ),
        OPC_SUSPEND,
        DEF_RERUN(),
    DEF_LABEL( "SkipPitch" ),

    DEF_FUNC( PursueIfYaw ),
    DEF_JUMP_F( "SkipYaw" ),
        DEF_LABEL( "Yaw" ),
        DEF_FUNC( PursueYaw ),
        OPC_SUSPEND,
        DEF_RERUN(),
    DEF_LABEL( "SkipYaw" ),
    ...

DEF_PROC_END()
};

GUI

Palomino has its own GUI inside of a viewport.

GUI functionality:

The GUI is driven by a one-second timer and mouse input. It is rendered after the 3D scene in order to overlay it.

In order to co-operate with the application, the Gui class requires the application to install timer and mouse event handlers and pass those events to Gui methods. Like the 3D simulation code, the GUI code is split into gfxsys dependent/independent parts.

A "screen" is a set of GUI objects. The visibility of either an individual GUI object or a GUI screen can be turned on or off.

The font code originated from the open-source GL-117 flight simulator, and was developed into a Font and FontDesc classes (gfx/src/gfx_font*). Fonts are stored in TGA files and rendered using textures.

When the application creates a button, a callback function can be registered to be called when the button is clicked. Buttons are rendered in OpenGL 2D mode.

The Gui class is responsible for rendering GuiObject objects. GuiObject objects include buttons and messages (text that times-out). But text isn't a GuiObject, ie, Gui::Print() just renders text without creating a GuiObject for the text. To keep text showing, the application must repeatedly call Gui::Print() every frame. The reason is if a line of text were to persist across frames, the Gui class would have to return a handle which the application would later need to stop showing the text (this approach would be more complicated).


Chase plane

Since every aircraft needs its own independent chase-plane view, every Craft object has its own ChasePlaneCraft object. The ChasePlaneCraft class consists of a distance (from the target) and an ephemeral matrix derived from the target (which is synced with the target every frame if the chase-plane view is selected). Since some code manipulates Craft and Eye objects as base Dyna objects (eg sim_keyboard.cc), the Eye class has a dummy/NOP ChasePlaneBase class.


Game module: Blue Zaxx

This game module is a mixture of influences from Blue Max and Zaxxon.


Assorted game ideas


Last modified: Wed Aug 16 16:42:26 EDT 2006