/*****************************************************************************
 * $LastChangedDate: 2010-06-24 10:22:48 -0400 (Thu, 24 Jun 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Aircraft classes.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

// Enable this when editing an aircraft model's control surfaces.
// This causes the spec file to be reloaded continually after being edited.
#if DEBUG
//#define COMPILE_EDITING_MODEL 1
#endif

#define PROGRAM_AIRCRAFT_CC 1
#include "base/module.hh"
using namespace base;
#include "math/module.hh"
#include "math/funcs_trig.hh"
#include "math/funcs_vector.hh"
using namespace math;
#include "gfx/module.hh"
using namespace gfx;
#include "graph/module.hh"
#include "graph/subgraph.hh"
#include "graph/shadow_scene_graph.hh"
using namespace graph;
#include "object/module.hh"
#include "object/aircraft.hh"
using namespace object;
#include "control/module.hh"
#include "control/defs_axis.hh"
using namespace control;
#include "physics/module.hh"
using namespace physics;
#include "program/module.hh"
#include "program/aircraft_models.hh"

namespace program {

INTERN const fp THROTTLE_PROPELLER              = 0.2f;  // when propeller begins to spin
INTERN const fp THROTTLE_FLAME_LOW              = 0.3f;  // most models lack this
INTERN const fp THROTTLE_FLAME_HIGH             = 0.7f;
INTERN const fp THROTTLE_FLAME                  = THROTTLE_FLAME_HIGH;
INTERN const fp F14_AILERONS_ACTIVE_SWING_WINGS = 0.75f;

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  functions  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#if COMPILE_EDITING_MODEL
#define CODE_CLASS_GET_SPECS( CONF_FILENAME )                               \
{                                                                           \
    BEGIN_ONCE {                                                            \
    CDEBUG << "Note: COMPILE_EDITING_MODEL enabled (slows program)" << endl;\
    } END_ONCE                                                              \
                                                                            \
    /* Reload every time. */                                                \
    PERSISTENT AircraftSpecs sAircraftSpecs;                                \
    sAircraftSpecs.ReadSpecsFromConfFile( CONF_FILENAME );                  \
    return sAircraftSpecs;                                                  \
}
#else
#define CODE_CLASS_GET_SPECS( CONF_FILENAME )                               \
{                                                                           \
    /* Load specs from a .conf file once. */                                \
    PERSISTENT AircraftSpecs sAircraftSpecs;                                \
    BEGIN_ONCE {                                                            \
        sAircraftSpecs.ReadSpecsFromConfFile( CONF_FILENAME );              \
    } END_ONCE                                                              \
    return sAircraftSpecs;                                                  \
}
#endif

/*****************************************************************************
 * Make specified kind of aircraft.
 * Some 3D models were defined with its center at the nose.
 * physics_*.conf redefines an offset.
 * @param   aircraftType
 * @param   pos
 * @param   loadCopy
 *          Pass ModelCache::LOAD_SHARED or LOAD_COPY.
 *          To save memory, pass false for dummy (non-dynamic) aircraft
 *          such as those parked on the carrier.
 *****************************************************************************/
shptr<Aircraft>
MakeAircraft( eAircraftType aircraftType, const WorldVertex& pos, const bool loadCopy )
{
    shptr0<Aircraft> aircraft = NULL;
    shptr0<Graph>    graph    = NULL;

#define MAKE_AIRCRAFT( CLASS )                                                  \
{                                                                               \
    graph = Aircraft::LoadModel( CLASS::ClassGetSpecs(), loadCopy ).PTR();      \
    aircraft = new CLASS( graph.PTR(), pos );                                   \
}

    switch ( aircraftType )
    {
        case eAircraftType_Spitfire:
        {
            MAKE_AIRCRAFT( SpitfireAircraft );
        }
        break;

        case eAircraftType_Corsair:
        {
            MAKE_AIRCRAFT( CorsairAircraft );
        }
        break;

        case eAircraftType_P51:
        {
            MAKE_AIRCRAFT( P51Aircraft );
        }
        break;

        case eAircraftType_F82:
        {
            MAKE_AIRCRAFT( F82Aircraft );
        }
        break;

        case eAircraftType_F86:
        {
            MAKE_AIRCRAFT( F86Aircraft );
        }
        break;

        case eAircraftType_A4:
        {
            MAKE_AIRCRAFT( A4Aircraft );
        }
        break;

        case eAircraftType_F14:
        {
            MAKE_AIRCRAFT( F14Aircraft );
        }
        break;

        case eAircraftType_F15:
        {
            MAKE_AIRCRAFT( F15Aircraft );
        }
        break;

        case eAircraftType_F16:
        {
            MAKE_AIRCRAFT( F16Aircraft );
        }
        break;

        case eAircraftType_F18:
        {
            MAKE_AIRCRAFT( F18Aircraft );
        }
        break;

        case eAircraftType_SR71:
        {
            MAKE_AIRCRAFT( SR71Aircraft );
        }
        break;

        case eAircraftType_Mirage2000:
        {
            MAKE_AIRCRAFT( Mirage2000Aircraft );
        }
        break;

        case eAircraftType_SU37:
        {
            MAKE_AIRCRAFT( SU37Aircraft );
        }
        break;

        case eAircraftType_Sikorsky:
        {
            MAKE_AIRCRAFT( SikorskyAircraft );
        }
        break;

        case eAircraftType_SpaceShuttle:
        {
            MAKE_AIRCRAFT( SpaceShuttleAircraft );
        }
        break;

        default:
        {
            ASSERT(0);  // oops
        }
        break;
    }

    // Return Aircraft.
    return aircraft.PTR();
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  SpitfireAircraft  ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SpitfireAircraft::SpitfireAircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,SpitfireAircraft::ClassGetSpecs()),
    mSubgraphPropeller1(new Subgraph(graph,"Propeller1")),
    mSubgraphPropeller2(new Subgraph(graph,"Propeller2")),
    mSubgraphPropeller3(new Subgraph(graph,"Propeller3")),
    mSubgraphPropeller4(new Subgraph(graph,"Propeller4")),
    mSubgraphPropellerDisc(new Subgraph(graph,"noshadow.PropellerDisk")),  // transparent
    mSubgraphGear0(new Subgraph(graph,"Leg-Assembly-L")),
    mSubgraphGear1(new Subgraph(graph,"Leg-Assembly-R")),
    mSubgraphGear2(new Subgraph(graph,"Tail-Wheel-Assembly")),
    //
    mSubgraphAileronLeft(new Subgraph(graph,"Aileron-T")),
    mSubgraphAileronRight(new Subgraph(graph,"Aileron-T.001")),
    mSubgraphElevatorLeft(new Subgraph(graph,"Elevator-T.001")),
    mSubgraphElevatorRight(new Subgraph(graph,"Elevator-T")),
    mSubgraphRudder(new Subgraph(graph,"Rudder"))
{
    ShadowSceneGraph::DisableShadows( mSubgraphPropellerDisc->GetRootNode() );
}

SpitfireAircraft::~SpitfireAircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
SpitfireAircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    return SpitfireAircraft::ClassGetSpecs();
}

const AircraftSpecs&
SpitfireAircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_spitfire.conf" )
}

/*****************************************************************************
 * Tick that pulses animation.
 *****************************************************************************/
void
SpitfireAircraft::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::Tick( millisecElapsed );

    RotatePropeller();
}

/*****************************************************************************
 * Rotate propeller.
 *****************************************************************************/
// Rotate both propeller Groups.
void
SpitfireAircraft::RotatePropeller( void )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // Degree of rotation correlates to throttle.
    const Degree degree = 35.0f * GetThrottle();
    const Radian radian = Deg2Rad( -degree );
    mSubgraphPropeller1->Rotate( XX, radian );
    mSubgraphPropeller2->Rotate( XX, radian );
    mSubgraphPropeller3->Rotate( XX, radian );
    mSubgraphPropeller4->Rotate( XX, radian );
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
SpitfireAircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
        mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is increased, spin propeller.
 *****************************************************************************/
void
SpitfireAircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
    mSubgraphPropellerDisc->Enable( throttle >= THROTTLE_PROPELLER );  // disable transparent disc
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
SpitfireAircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////  CorsairAircraft  ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
CorsairAircraft::CorsairAircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,CorsairAircraft::ClassGetSpecs()),
    mSubgraphPropeller0(new Subgraph(graph,"prop_0")),
    mSubgraphPropeller1(new Subgraph(graph,"prop_1")),
    mSubgraphGear0(new Subgraph(graph,"gearp2.L")),
    mSubgraphGear1(new Subgraph(graph,"gearp2.R")),
    mSubgraphGear2(new Subgraph(graph,"gearp3.L")),
    mSubgraphGear3(new Subgraph(graph,"gearp3.R")),
    mSubgraphGear4(new Subgraph(graph,"gearcylinder.L")),
    mSubgraphGear5(new Subgraph(graph,"gearcylinder.R")),
    mSubgraphGear6(new Subgraph(graph,"gearleg.L")),
    mSubgraphGear7(new Subgraph(graph,"gearleg.R")),
    mSubgraphGear8(new Subgraph(graph,"gearleg2.L")),
    mSubgraphGear9(new Subgraph(graph,"gearleg2.R")),
    mSubgraphGear10(new Subgraph(graph,"gearp1.L")),
    mSubgraphGear11(new Subgraph(graph,"gearp1.R")),
    mSubgraphGear12(new Subgraph(graph,"gearsc1.L")),
    mSubgraphGear13(new Subgraph(graph,"gearsc2.L")),
    mSubgraphGear14(new Subgraph(graph,"tailgear1")),
    mSubgraphGear15(new Subgraph(graph,"tailgear2")),
    mSubgraphGear16(new Subgraph(graph,"stopwheel.L")),
    mSubgraphGear17(new Subgraph(graph,"stopwheel.R")),
    mSubgraphGear18(new Subgraph(graph,"wheel.L")),
    mSubgraphGear19(new Subgraph(graph,"wheel.R")),
    mSubgraphGear20(new Subgraph(graph,"tailwheel")),
    // Control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"aileron.L")),
    mSubgraphAileronRight(new Subgraph(graph,"aileron.R")),
    mSubgraphElevatorLeft(new Subgraph(graph,"elevator.L")),
    mSubgraphElevatorRight(new Subgraph(graph,"elevator.R")),
    mSubgraphRudder(new Subgraph(graph,"rudder"))
{
    // NOP
}

CorsairAircraft::~CorsairAircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
CorsairAircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return CorsairAircraft::ClassGetSpecs();
}

const AircraftSpecs&
CorsairAircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_corsair.conf" )
}

/*****************************************************************************
 * Tick that pulses animation.
 *****************************************************************************/
void
CorsairAircraft::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::Tick( millisecElapsed );

    RotatePropeller();
}

/*****************************************************************************
 * Rotate propeller.
 *****************************************************************************/
void
CorsairAircraft::RotatePropeller( void )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // Degree of rotation correlates to throttle.
    const Degree degree = 35.0f * GetThrottle();
    const Radian radian = Deg2Rad( -degree );
    mSubgraphPropeller0->Rotate( XX, radian );
    mSubgraphPropeller1->Rotate( XX, radian );
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
CorsairAircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down ); mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down ); mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down ); mSubgraphGear5->Enable( down );
        mSubgraphGear6->Enable( down ); mSubgraphGear7->Enable( down );
        mSubgraphGear8->Enable( down ); mSubgraphGear9->Enable( down );
        mSubgraphGear10->Enable( down ); mSubgraphGear11->Enable( down );
        mSubgraphGear12->Enable( down ); mSubgraphGear13->Enable( down );
        mSubgraphGear14->Enable( down ); mSubgraphGear15->Enable( down );
        mSubgraphGear16->Enable( down ); mSubgraphGear17->Enable( down );
        mSubgraphGear18->Enable( down ); mSubgraphGear19->Enable( down );
        mSubgraphGear20->Enable( down );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
CorsairAircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  P51Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
P51Aircraft::P51Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,P51Aircraft::ClassGetSpecs()),
    mSubgraphPropeller1(new Subgraph(graph,"Propeller1")),
    mSubgraphPropeller2(new Subgraph(graph,"Propeller2")),
    //
    mSubgraphLeftGearWheel(new Subgraph(graph,"LeftGearWheel")),
    mSubgraphLeftGearStrut(new Subgraph(graph,"LeftGearStrut")),
    mSubgraphLeftInnerGearCover(new Subgraph(graph,"LeftInnerGearCover")),
    mSubgraphLeftOuterGearCover(new Subgraph(graph,"LeftOuterGearCover")),
    mSubgraphLeftTailGearDoor(new Subgraph(graph,"LeftTailGearDoor")),
    //
    mSubgraphRightGearWheel(new Subgraph(graph,"RightGearWheel")),
    mSubgraphRightGearStrut(new Subgraph(graph,"RightGearStrut")),
    mSubgraphRightInnerGearCover(new Subgraph(graph,"RightInnerGearCover")),
    mSubgraphRightOuterGearCover(new Subgraph(graph,"RightOuterGearCover")),
    mSubgraphRightTailGearDoor(new Subgraph(graph,"RightTailGearDoor")),
    //
    mSubgraphTailGearWheel(new Subgraph(graph,"TailGearWheel")),
    mSubgraphTailGearStrut(new Subgraph(graph,"TailGearStrut")),
    //
    mSubgraphAileronLeft(new Subgraph(graph,"LeftAileron")),
    mSubgraphAileronRight(new Subgraph(graph,"RightAileron")),
    mSubgraphElevatorLeft(new Subgraph(graph,"LeftElevator")),
    mSubgraphElevatorRight(new Subgraph(graph,"RightElevator")),
    mSubgraphRudder(new Subgraph(graph,"Rudder")),
    //
    mSubgraphJunk0(new Subgraph(graph,"TailNavLightOff")),
    mSubgraphJunk1(new Subgraph(graph,"TailNavLightOn"))
{
    // Disable junk parts.
    mSubgraphJunk0->Enable( false );
    mSubgraphJunk1->Enable( false );
}

P51Aircraft::~P51Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
P51Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return P51Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
P51Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_p51.conf" )
}

/*****************************************************************************
 * Tick that pulses animation.
 *****************************************************************************/
void
P51Aircraft::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::Tick( millisecElapsed );

    RotatePropeller();
}

/*****************************************************************************
 * Rotate propeller.
 *****************************************************************************/
void
P51Aircraft::RotatePropeller( void )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // Rotate both propeller Groups.
    // Degree of rotation correlates to throttle.
    const Degree degree = 35.0f * GetThrottle();
    const Radian radian = Deg2Rad( -degree );
    mSubgraphPropeller1->Rotate( XX, radian );  // this p51d-jw model splits propeller
    mSubgraphPropeller2->Rotate( XX, radian );  // into two subgraphs
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
P51Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // The p51d-nw model apparently defines the origin of the landing gear
    // at the center of the fuselage instead of the strut pivot.
    // For now, just disable the nodes instead of doing rotation.

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphLeftGearWheel->Enable( down );
        mSubgraphLeftGearStrut->Enable( down );
        mSubgraphLeftInnerGearCover->Enable( down );
        mSubgraphLeftOuterGearCover->Enable( down );
        mSubgraphLeftTailGearDoor->Enable( down );

        mSubgraphRightGearWheel->Enable( down );
        mSubgraphRightGearStrut->Enable( down );
        mSubgraphRightInnerGearCover->Enable( down );
        mSubgraphRightOuterGearCover->Enable( down );
        mSubgraphRightTailGearDoor->Enable( down );

        mSubgraphTailGearWheel->Enable( down );
        mSubgraphTailGearStrut->Enable( down );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
P51Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F82Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F82Aircraft::F82Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F82Aircraft::ClassGetSpecs()),
    mSubgraphPropeller1(new Subgraph(graph,"Propeller1")),
    mSubgraphPropeller2(new Subgraph(graph,"Propeller2")),
    mSubgraphPropeller3(new Subgraph(graph,"Propeller1.002")),
    mSubgraphPropeller4(new Subgraph(graph,"Propeller2.001")),
    //
    mSubgraphLeftGearWheel(new Subgraph(graph,"LeftGearWheel")),
    mSubgraphLeftGearStrut(new Subgraph(graph,"LeftGearStrut")),
    mSubgraphLeftInnerGearCover(new Subgraph(graph,"LeftInnerGearCover")),
    mSubgraphLeftInnerGearCover2(new Subgraph(graph,"LeftInnerGearCove")),  // sic/truncation
    mSubgraphLeftOuterGearCover(new Subgraph(graph,"LeftOuterGearCover")),
    mSubgraphLeftTailGearDoor(new Subgraph(graph,"LeftTailGearDoor")),
    //
    mSubgraphRightGearWheel(new Subgraph(graph,"RightGearWheel")),
    mSubgraphRightGearStrut(new Subgraph(graph,"RightGearStrut")),
    mSubgraphRightInnerGearCover(new Subgraph(graph,"RightInnerGearCover")),
    mSubgraphRightInnerGearCover2(new Subgraph(graph,"RightInnerGearCov")),  // sic/truncation
    mSubgraphRightOuterGearCover(new Subgraph(graph,"RightOuterGearCover")),
    mSubgraphRightTailGearDoor(new Subgraph(graph,"RightTailGearDoor")),
    //
    mSubgraphTailGearWheel(new Subgraph(graph,"TailGearWheel")),
    mSubgraphTailGearStrut(new Subgraph(graph,"TailGearStrut")),
    mSubgraphTailGearWheel2(new Subgraph(graph,"TailGearWheel.001")),
    mSubgraphTailGearStrut2(new Subgraph(graph,"TailGearStrut.001")),
    //
    mSubgraphAileronLeft(new Subgraph(graph,"LeftAileron")),
    mSubgraphAileronRight(new Subgraph(graph,"RightAileron")),
    mSubgraphElevator(new Subgraph(graph,"LeftElevator.001")),  // misnomer
    mSubgraphRudderLeft(new Subgraph(graph,"Rudder")),
    mSubgraphRudderRight(new Subgraph(graph,"Rudder.001")),
    //
    mSubgraphJunk0(new Subgraph(graph,"TailNavLightOff")),
    mSubgraphJunk1(new Subgraph(graph,"TailNavLightOn"))
{
    // Disable junk parts.
    mSubgraphJunk0->Enable( false );
    mSubgraphJunk1->Enable( false );
}

F82Aircraft::~F82Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F82Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F82Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F82Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f82.conf" )
}

/*****************************************************************************
 * Tick that pulses animation.
 *****************************************************************************/
void
F82Aircraft::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::Tick( millisecElapsed );

    RotatePropeller();
}

/*****************************************************************************
 * Rotate propeller.
 *****************************************************************************/
void
F82Aircraft::TranslatePropeller( const fp dir )
{
    // In the F-82 model, the origin of the two propellers aren't centered correctly.
    // This C++ code temporarily shifts the matrix origin.
    //
    // Using constants for translation is ok as they are scaled
    // by the matrix scale set in the Object's Graph.

    const uint outwardAxis  = ZZ;
    const fp   outwardTrans = dir * 1.875f;  // constant ok
    mSubgraphPropeller1->Translate( outwardAxis,  outwardTrans );
    mSubgraphPropeller2->Translate( outwardAxis,  outwardTrans );
    mSubgraphPropeller3->Translate( outwardAxis, -outwardTrans );
    mSubgraphPropeller4->Translate( outwardAxis, -outwardTrans );

    const uint upwardAxis  = YY;
    const fp   upwardTrans = dir * 0.1f;  // constant ok
    mSubgraphPropeller1->Translate( upwardAxis, upwardTrans );
    mSubgraphPropeller2->Translate( upwardAxis, upwardTrans );
    mSubgraphPropeller3->Translate( upwardAxis, upwardTrans );
    mSubgraphPropeller4->Translate( upwardAxis, upwardTrans );
}

void
F82Aircraft::RotatePropeller( void )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // Rotate both propeller Groups.
    // Degree of rotation correlates to throttle.
    const Degree degree = 35.0f * GetThrottle();
    const Radian radian = Deg2Rad( -degree );

    TranslatePropeller( 1.0f );

    mSubgraphPropeller1->Rotate( XX, radian );  // the p51d-jw model splits propeller
    mSubgraphPropeller2->Rotate( XX, radian );  // into 2 subgraphs so the F-82 model has 4
    mSubgraphPropeller3->Rotate( XX, radian );
    mSubgraphPropeller4->Rotate( XX, radian );

    TranslatePropeller( -1.0f );  // undo translation
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F82Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // The model apparently defines the origin of the landing gear
    // at the center of the fuselage instead of the strut pivot.
    // For now, just disable the nodes instead of doing rotation.

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphLeftGearWheel->Enable( down );
        mSubgraphLeftGearStrut->Enable( down );
        mSubgraphLeftInnerGearCover->Enable( down );
        mSubgraphLeftInnerGearCover2->Enable( down );
        mSubgraphLeftOuterGearCover->Enable( down );
        mSubgraphLeftTailGearDoor->Enable( down );

        mSubgraphRightGearWheel->Enable( down );
        mSubgraphRightGearStrut->Enable( down );
        mSubgraphRightInnerGearCover->Enable( down );
        mSubgraphRightInnerGearCover2->Enable( down );
        mSubgraphRightOuterGearCover->Enable( down );
        mSubgraphRightTailGearDoor->Enable( down );

        mSubgraphTailGearWheel->Enable( down );
        mSubgraphTailGearStrut->Enable( down );
        mSubgraphTailGearWheel2->Enable( down );
        mSubgraphTailGearStrut2->Enable( down );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F82Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevator, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudderLeft,  controlFraction, LEFT_RUDDER  );
            RotateRudder( *mSubgraphRudderRight, controlFraction, RIGHT_RUDDER );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F86Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F86Aircraft::F86Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F86Aircraft::ClassGetSpecs()),
    mSubgraphGear0(new Subgraph(graph,"nosegear1")),
    mSubgraphGear1(new Subgraph(graph,"nosewheel")),
    mSubgraphGear2(new Subgraph(graph,"gear.L")),
    mSubgraphGear3(new Subgraph(graph,"gear.R")),
    mSubgraphGear4(new Subgraph(graph,"wheel.L")),
    mSubgraphGear5(new Subgraph(graph,"wheel.R")),
    // Control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"aileron.L")),
    mSubgraphAileronRight(new Subgraph(graph,"aileron.R")),
    mSubgraphElevatorLeft(new Subgraph(graph,"elevator.L")),
    mSubgraphElevatorRight(new Subgraph(graph,"elevator.R")),
    mSubgraphRudder(new Subgraph(graph,"rudder_1"))
    // (no flame)
{
    SetThrottle( 0.0f );
}

F86Aircraft::~F86Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F86Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F86Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F86Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f86.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F86Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down ); mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down ); mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down ); mSubgraphGear5->Enable( down );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F86Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  A4Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
A4Aircraft::A4Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,A4Aircraft::ClassGetSpecs()),
    mSubgraphGear0(new Subgraph(graph,"LeftWheel")),
    mSubgraphGear1(new Subgraph(graph,"LeftLowerLeg")),
    mSubgraphGear2(new Subgraph(graph,"LeftUpperLeg")),
    mSubgraphGear3(new Subgraph(graph,"LeftGearDoor")),
    mSubgraphGear4(new Subgraph(graph,"LeftGearStrut")),
    mSubgraphGear5(new Subgraph(graph,"LeftLowerLegAssmbly")),
    mSubgraphGear6(new Subgraph(graph,"RightWheel")),
    mSubgraphGear7(new Subgraph(graph,"RightLowerLeg")),
    mSubgraphGear8(new Subgraph(graph,"RightUpperLeg")),
    mSubgraphGear9(new Subgraph(graph,"RightGearDoor")),
    mSubgraphGear10(new Subgraph(graph,"RightGearStrut")),
    mSubgraphGear11(new Subgraph(graph,"RightLowerLegAssmbly")),
    mSubgraphGear12(new Subgraph(graph,"LandingLight")),    // unique to left wheel
    mSubgraphGear13(new Subgraph(graph,"NoseWheel")),       // front wheel
    mSubgraphGear14(new Subgraph(graph,"Upper-Leg-Nose")),  // front wheel
    mSubgraphGear15(new Subgraph(graph,"Lower-Leg")),       // front wheel
    mSubgraphGear16(new Subgraph(graph,"NoseGearStrut")),   // front wheel
    mSubgraphGear17(new Subgraph(graph,"NoseGearDoor")),    // front wheel
    // Control surfaces:
    // This model lacks real ailerons.
    mSubgraphAileronLeft(new Subgraph(graph,"LeftSpoiler")),
    mSubgraphAileronRight(new Subgraph(graph,"RightSpoiler")),
    mSubgraphAileronLeft2(new Subgraph(graph,"LeftFlap-New")),
    mSubgraphAileronRight2(new Subgraph(graph,"RightFlap-New")),
    mSubgraphElevatorLeft(new Subgraph(graph,"LeftElevator")),
    mSubgraphElevatorRight(new Subgraph(graph,"RightElevator")),
    mSubgraphRudder(new Subgraph(graph,"Rudder"))
// (no flame)
{
    SetThrottle( 0.0f );
}

A4Aircraft::~A4Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
A4Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return A4Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
A4Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_a4.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
A4Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );  mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );  mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down );  mSubgraphGear5->Enable( down );
        mSubgraphGear6->Enable( down );  mSubgraphGear7->Enable( down );
        mSubgraphGear8->Enable( down );  mSubgraphGear9->Enable( down );
        mSubgraphGear10->Enable( down ); mSubgraphGear11->Enable( down );
        mSubgraphGear12->Enable( down ); mSubgraphGear13->Enable( down );
        mSubgraphGear14->Enable( down ); mSubgraphGear15->Enable( down );
        mSubgraphGear16->Enable( down ); mSubgraphGear17->Enable( down );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
A4Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,   controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronLeft2,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight,  controlFraction, RIGHT_AILERON );
            RotateAileron( *mSubgraphAileronRight2, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            // Subgraph is the entire rudder.
          //RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F14Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F14Aircraft::F14Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F14Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"MainWheelL")),
    mSubgraphGear1(new Subgraph(graph,"MainWheelR")),
    mSubgraphGear2(new Subgraph(graph,"NsWheel")),
    mSubgraphGear3(new Subgraph(graph,"FwdNsGrDoor")),
    mSubgraphGear4(new Subgraph(graph,"MidNsGrDoor")),
    mSubgraphGear5(new Subgraph(graph,"CatBar")),
    mSubgraphGear6(new Subgraph(graph,"CatBarLink")),
    mSubgraphGear7(new Subgraph(graph,"NsGearStrut")),
    mSubgraphGear8(new Subgraph(graph,"Tube.006")),
    mSubgraphGear9(new Subgraph(graph,"Tube.007")),
    mSubgraphGear10(new Subgraph(graph,"Tube.013")),
    mSubgraphGear11(new Subgraph(graph,"Tube.014")),
    mSubgraphGear12(new Subgraph(graph,"NsGrRotatingSleeve")),
    mSubgraphGear13(new Subgraph(graph,"RotatingSleeve-L")),
    mSubgraphGear14(new Subgraph(graph,"RotatingSleeve-R")),
    mSubgraphGear15(new Subgraph(graph,"MnGrStrut-L")),
    mSubgraphGear16(new Subgraph(graph,"MnGrStrut.001")),
    mSubgraphGear17(new Subgraph(graph,"Brace1")),
    mSubgraphGear18(new Subgraph(graph,"Brace1.001")),
    mSubgraphGear19(new Subgraph(graph,"Brace1.002")),
    mSubgraphGear20(new Subgraph(graph,"Brace1.003")),
    mSubgraphGear21(new Subgraph(graph,"HiCompassL")),
    mSubgraphGear22(new Subgraph(graph,"HiCompassR")),
    mSubgraphGear23(new Subgraph(graph,"LShock")),
    mSubgraphGear24(new Subgraph(graph,"RShock")),
    mSubgraphGear25(new Subgraph(graph,"NsShock")),
    mSubgraphGear26(new Subgraph(graph,"LoCompassL")),
    mSubgraphGear27(new Subgraph(graph,"LoCompassR")),
    mSubgraphGear28(new Subgraph(graph,"LoNsCompass")),
    mSubgraphGear29(new Subgraph(graph,"UpNsCompass")),
    mSubgraphGear30(new Subgraph(graph,"Downlock")),
    mSubgraphGear31(new Subgraph(graph,"Downlock.001")),
    mSubgraphGear32(new Subgraph(graph,"LandingLight")),
    mSubgraphGear33(new Subgraph(graph,"WheelBrake")),
    mSubgraphGear34(new Subgraph(graph,"WheelBrake.001")),
    //
    mSubgraphWingLeft0(new Subgraph(graph,"Wing_0")),
    mSubgraphWingLeft1(new Subgraph(graph,"Wing_1")),
    mSubgraphWingLeft2(new Subgraph(graph,"Flap1_0")),
    mSubgraphWingLeft3(new Subgraph(graph,"Flap1_1")),
    mSubgraphWingLeft4(new Subgraph(graph,"Flap2_0")),
    mSubgraphWingLeft5(new Subgraph(graph,"Flap2_1")),
    mSubgraphWingLeft6(new Subgraph(graph,"Flap3_0")),
    mSubgraphWingLeft7(new Subgraph(graph,"Flap3_1")),
    mSubgraphWingLeft8(new Subgraph(graph,"Flap1Brow")),
    mSubgraphWingLeft9(new Subgraph(graph,"Flap2Brow")),
    mSubgraphWingLeft10(new Subgraph(graph,"Flap3Brow")),
    mSubgraphWingLeft11(new Subgraph(graph,"FlapSeal1_0")),
    mSubgraphWingLeft12(new Subgraph(graph,"FlapSeal1_1")),
    mSubgraphWingLeft13(new Subgraph(graph,"FlapSeal2_0")),
    mSubgraphWingLeft14(new Subgraph(graph,"FlapSeal2_1")),
    mSubgraphWingLeft15(new Subgraph(graph,"FlapSeal3_0")),
    mSubgraphWingLeft16(new Subgraph(graph,"FlapSeal3_1")),
    mSubgraphWingLeft17(new Subgraph(graph,"Spoiler1_0")),
    mSubgraphWingLeft18(new Subgraph(graph,"Spoiler1_1")),
    mSubgraphWingLeft19(new Subgraph(graph,"Spoiler2_0")),
    mSubgraphWingLeft20(new Subgraph(graph,"Spoiler2_1")),
    mSubgraphWingLeft21(new Subgraph(graph,"Spoiler3_0")),
    mSubgraphWingLeft22(new Subgraph(graph,"Spoiler3_1")),
    mSubgraphWingLeft23(new Subgraph(graph,"Spoiler4_0")),
    mSubgraphWingLeft24(new Subgraph(graph,"Spoiler4_1")),
    mSubgraphWingLeft25(new Subgraph(graph,"Slat1_0")),
    mSubgraphWingLeft26(new Subgraph(graph,"Slat1_1")),
    mSubgraphWingLeft27(new Subgraph(graph,"Slat2_0")),
    mSubgraphWingLeft28(new Subgraph(graph,"Slat2_1")),
    mSubgraphWingLeft29(new Subgraph(graph,"LWingSlimmer")),
    //
    mSubgraphWingRight0(new Subgraph(graph,"Wing.001_0")),
    mSubgraphWingRight1(new Subgraph(graph,"Wing.001_1")),
    mSubgraphWingRight2(new Subgraph(graph,"Flap1.001_0")),
    mSubgraphWingRight3(new Subgraph(graph,"Flap1.001_1")),
    mSubgraphWingRight4(new Subgraph(graph,"Flap2.001_0")),
    mSubgraphWingRight5(new Subgraph(graph,"Flap2.001_1")),
    mSubgraphWingRight6(new Subgraph(graph,"Flap3.001_0")),
    mSubgraphWingRight7(new Subgraph(graph,"Flap3.001_1")),
    mSubgraphWingRight8(new Subgraph(graph,"Flap1Brow.001")),
    mSubgraphWingRight9(new Subgraph(graph,"Flap2Brow.001")),
    mSubgraphWingRight10(new Subgraph(graph,"Flap3Brow.001")),
    mSubgraphWingRight11(new Subgraph(graph,"FlapSeal1.001_0")),
    mSubgraphWingRight12(new Subgraph(graph,"FlapSeal1.001_1")),
    mSubgraphWingRight13(new Subgraph(graph,"FlapSeal2.001_0")),
    mSubgraphWingRight14(new Subgraph(graph,"FlapSeal2.001_1")),
    mSubgraphWingRight15(new Subgraph(graph,"FlapSeal3.001_0")),
    mSubgraphWingRight16(new Subgraph(graph,"FlapSeal3.001_1")),
    mSubgraphWingRight17(new Subgraph(graph,"Spoiler1.001_0")),
    mSubgraphWingRight18(new Subgraph(graph,"Spoiler1.001_1")),
    mSubgraphWingRight19(new Subgraph(graph,"Spoiler2.001_0")),
    mSubgraphWingRight20(new Subgraph(graph,"Spoiler2.001_1")),
    mSubgraphWingRight21(new Subgraph(graph,"Spoiler3.001_0")),
    mSubgraphWingRight22(new Subgraph(graph,"Spoiler3.001_1")),
    mSubgraphWingRight23(new Subgraph(graph,"Spoiler4.001_0")),
    mSubgraphWingRight24(new Subgraph(graph,"Spoiler4.001_1")),
    mSubgraphWingRight25(new Subgraph(graph,"Slat1.001_0")),
    mSubgraphWingRight26(new Subgraph(graph,"Slat1.001_1")),
    mSubgraphWingRight27(new Subgraph(graph,"Slat2.001_0")),
    mSubgraphWingRight28(new Subgraph(graph,"Slat2.001_1")),
    mSubgraphWingRight29(new Subgraph(graph,"RWingSlimmer")),
    mSubgraphTaileronLeft(new Subgraph(graph,"Stabilator")),
    mSubgraphTaileronRight(new Subgraph(graph,"Stabilator.001")),
    mSubgraphRudderLeft(new Subgraph(graph,"RudderL")),
    mSubgraphRudderRight(new Subgraph(graph,"RudderR")),
    //
    mSubgraphLadder0(new Subgraph(graph,"Ladder")),
    mSubgraphLadder1(new Subgraph(graph,"Ladder.001")),
    mSubgraphLadder2(new Subgraph(graph,"Ladder.002")),
    mSubgraphRefuelProbe(new Subgraph(graph,"RefuelProbe")),
    mSubgraphFlameInner0(new Subgraph(graph,"InnerFlameL")),
    mSubgraphFlameInner1(new Subgraph(graph,"InnerFlameR")),
    mSubgraphFlameOuter0(new Subgraph(graph,"outerFlame")),
    mSubgraphFlameOuter1(new Subgraph(graph,"outerFlame.001")),
    mSubgraphShockWave(new Subgraph(graph,"ShockWave")),
    mSubgraphManeuverVortexLeft(new Subgraph(graph,"ManeuverVortexL")),
    mSubgraphManeuverVortexRight(new Subgraph(graph,"ManeuverVortexR")),
    //
    mGearSubgraphs(), mLeftWingSubgraphs(), mRightWingSubgraphs(),
    //
    mSwingWingsFraction(0.0f),
    mAileronFraction(0.0f)
{
    SetThrottle( 0.0f );

    // Hide superfluous parts.
    mSubgraphLadder0->Enable( false );
    mSubgraphLadder1->Enable( false );
    mSubgraphLadder2->Enable( false );
    mSubgraphRefuelProbe->Enable( false );
    mSubgraphShockWave->Enable( false );
    mSubgraphManeuverVortexLeft->Enable( false );
    mSubgraphManeuverVortexRight->Enable( false );

    // Build list of gear subgraphs.
    mLeftWingSubgraphs.reserve(  50 );
    mRightWingSubgraphs.reserve( 50 );
    #define F14_PUSH_GEAR_SUBGRAPH( NUM )                           \
        mGearSubgraphs.push_back( mSubgraphGear##NUM );
    F14_PUSH_GEAR_SUBGRAPH( 0 );  F14_PUSH_GEAR_SUBGRAPH( 1 );
    F14_PUSH_GEAR_SUBGRAPH( 2 );  F14_PUSH_GEAR_SUBGRAPH( 3 );
    F14_PUSH_GEAR_SUBGRAPH( 4 );  F14_PUSH_GEAR_SUBGRAPH( 5 );
    F14_PUSH_GEAR_SUBGRAPH( 6 );  F14_PUSH_GEAR_SUBGRAPH( 7 );
    F14_PUSH_GEAR_SUBGRAPH( 8 );  F14_PUSH_GEAR_SUBGRAPH( 9 );
    F14_PUSH_GEAR_SUBGRAPH( 10 ); F14_PUSH_GEAR_SUBGRAPH( 11 );
    F14_PUSH_GEAR_SUBGRAPH( 12 ); F14_PUSH_GEAR_SUBGRAPH( 13 );
    F14_PUSH_GEAR_SUBGRAPH( 14 ); F14_PUSH_GEAR_SUBGRAPH( 15 );
    F14_PUSH_GEAR_SUBGRAPH( 16 ); F14_PUSH_GEAR_SUBGRAPH( 17 );
    F14_PUSH_GEAR_SUBGRAPH( 18 ); F14_PUSH_GEAR_SUBGRAPH( 19 );
    F14_PUSH_GEAR_SUBGRAPH( 20 ); F14_PUSH_GEAR_SUBGRAPH( 21 );
    F14_PUSH_GEAR_SUBGRAPH( 22 ); F14_PUSH_GEAR_SUBGRAPH( 23 );
    F14_PUSH_GEAR_SUBGRAPH( 24 ); F14_PUSH_GEAR_SUBGRAPH( 25 );
    F14_PUSH_GEAR_SUBGRAPH( 26 ); F14_PUSH_GEAR_SUBGRAPH( 27 );
    F14_PUSH_GEAR_SUBGRAPH( 28 ); F14_PUSH_GEAR_SUBGRAPH( 29 );
    F14_PUSH_GEAR_SUBGRAPH( 30 ); F14_PUSH_GEAR_SUBGRAPH( 31 );
    F14_PUSH_GEAR_SUBGRAPH( 32 ); F14_PUSH_GEAR_SUBGRAPH( 33 );
    F14_PUSH_GEAR_SUBGRAPH( 34 );

    // Build list of wing subgraphs.
    mLeftWingSubgraphs.reserve(  50 );
    mRightWingSubgraphs.reserve( 50 );
    #define F14_PUSH_WING_SUBGRAPH( NUM )                           \
        mLeftWingSubgraphs.push_back(  mSubgraphWingLeft##NUM );    \
        mRightWingSubgraphs.push_back( mSubgraphWingRight##NUM );
    F14_PUSH_WING_SUBGRAPH( 0 );  F14_PUSH_WING_SUBGRAPH( 1 );
    F14_PUSH_WING_SUBGRAPH( 2 );  F14_PUSH_WING_SUBGRAPH( 3 );
    F14_PUSH_WING_SUBGRAPH( 4 );  F14_PUSH_WING_SUBGRAPH( 5 );
    F14_PUSH_WING_SUBGRAPH( 6 );  F14_PUSH_WING_SUBGRAPH( 7 );
    F14_PUSH_WING_SUBGRAPH( 8 );  F14_PUSH_WING_SUBGRAPH( 9 );
    F14_PUSH_WING_SUBGRAPH( 10 ); F14_PUSH_WING_SUBGRAPH( 11 );
    F14_PUSH_WING_SUBGRAPH( 12 ); F14_PUSH_WING_SUBGRAPH( 13 );
    F14_PUSH_WING_SUBGRAPH( 14 ); F14_PUSH_WING_SUBGRAPH( 15 );
    F14_PUSH_WING_SUBGRAPH( 16 ); F14_PUSH_WING_SUBGRAPH( 17 );
    F14_PUSH_WING_SUBGRAPH( 18 ); F14_PUSH_WING_SUBGRAPH( 19 );
    F14_PUSH_WING_SUBGRAPH( 20 ); F14_PUSH_WING_SUBGRAPH( 21 );
    F14_PUSH_WING_SUBGRAPH( 22 ); F14_PUSH_WING_SUBGRAPH( 23 );
    F14_PUSH_WING_SUBGRAPH( 24 ); F14_PUSH_WING_SUBGRAPH( 25 );
    F14_PUSH_WING_SUBGRAPH( 26 ); F14_PUSH_WING_SUBGRAPH( 27 );
    F14_PUSH_WING_SUBGRAPH( 28 ); F14_PUSH_WING_SUBGRAPH( 29 );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlameInner0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlameInner1->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlameOuter0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlameOuter1->GetRootNode() );
}

F14Aircraft::~F14Aircraft()
{
    // NOP
}

/*****************************************************************************
 * Reset.
 *****************************************************************************/
void
F14Aircraft::Reset( void )
{
    Parent::Reset();

    // Swing wings forward.
    SwingWings( 0.0f );
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F14Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F14Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F14Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f14.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F14Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        for ( uint i = 0; i < mGearSubgraphs.size(); ++i )
            mGearSubgraphs[i]->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
F14Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
    const bool flameInner = (throttle >= THROTTLE_FLAME_LOW);
    const bool flameOuter = (throttle >= THROTTLE_FLAME_HIGH);
    mSubgraphFlameInner0->Enable( flameInner );
    mSubgraphFlameInner1->Enable( flameInner );
    mSubgraphFlameOuter0->Enable( flameOuter );
    mSubgraphFlameOuter1->Enable( flameOuter );
}

/*****************************************************************************
 * Swing wings { 0.0:forward...1.0:back }
 *****************************************************************************/
void
F14Aircraft::SwingWings( const float fraction )
{
ASSERT( mLeftWingSubgraphs.size() == mRightWingSubgraphs.size() );

    mSwingWingsFraction = Clamp1( fraction );

    // Undo rotation/translation of wing subgraphs from previous call.
    for ( uint i = 0; i < mLeftWingSubgraphs.size(); ++i )
    {
        mLeftWingSubgraphs[i]->Reset();
        mRightWingSubgraphs[i]->Reset();
    }

    // deg = 0  fully forward
    // deg = 45 fully back
    const Degree deg = mSwingWingsFraction * 45.0f;
    const Radian rad = Deg2Rad( deg );

    // This particular 3D model requires translating the position
    // of the subgraphs as a function of swing-wings.
    // offset_x =   0.00 swing forward
    // offset_z =   0.00 swing forward
    // offset_x =  -1.40 swing back
    // offset_z = +-1.75 swing back (+- for left/right wing)
    const fp offset_x = mSwingWingsFraction * -1.40f;
    const fp offset_z = mSwingWingsFraction *  1.75f;
    for ( uint i = 0; i < mLeftWingSubgraphs.size(); ++i )
    {
        mLeftWingSubgraphs[i]->Translate(  XX, offset_x );  // move along fuselage
        mRightWingSubgraphs[i]->Translate( XX, offset_x );

        mLeftWingSubgraphs[i]->Translate(  ZZ,  offset_z );  // move outwards (along wings)
        mRightWingSubgraphs[i]->Translate( ZZ, -offset_z );

        mLeftWingSubgraphs[i]->Rotate(  YY,  rad );
        mRightWingSubgraphs[i]->Rotate( YY, -rad );
    }
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F14Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    const AircraftSpecs& specs = GetSpecs();

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphTaileronLeft,  controlFraction );
            RotateElevator( *mSubgraphTaileronRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            // F-14 wings are complex.

            // Tailerons:
            RotateAileron( *mSubgraphTaileronLeft,  controlFraction, LEFT_AILERON,  specs.mElevatorOffset );
            RotateAileron( *mSubgraphTaileronRight, controlFraction, RIGHT_AILERON, specs.mElevatorOffset );

            // Ailerons (outer flaps) if wings are straight-out.
            if ( mSwingWingsFraction <= F14_AILERONS_ACTIVE_SWING_WINGS )
            {
                // Specially rotate the F-14 ailerons.
                #define F14_ROTATE_AILERONS( SUBGRAPH, CONTROL_FRACTION, AILERON )              \
                {{                                                                              \
                    (SUBGRAPH).Translate( specs.mAileronOffset );                               \
                    (SUBGRAPH).Rotate( MODEL_AXIS_PITCH,                                        \
                                       specs.mAileronROF * (CONTROL_FRACTION) * int(AILERON) ); \
                    (SUBGRAPH).Translate( -specs.mAileronOffset );                              \
                }}

                // Undo previous rotation.
                F14_ROTATE_AILERONS( *mSubgraphWingLeft3,  -mAileronFraction, LEFT_AILERON );
                F14_ROTATE_AILERONS( *mSubgraphWingRight3, -mAileronFraction, RIGHT_AILERON );

                // Apply current rotation.
                F14_ROTATE_AILERONS( *mSubgraphWingLeft3,  controlFraction, LEFT_AILERON );
                F14_ROTATE_AILERONS( *mSubgraphWingRight3, controlFraction, RIGHT_AILERON );

                // Remember for next time.
                mAileronFraction = controlFraction;
            }
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudderLeft,  controlFraction, LEFT_RUDDER  );
            RotateRudder( *mSubgraphRudderRight, controlFraction, RIGHT_RUDDER );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F15Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F15Aircraft::F15Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F15Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"LeftWheel")),
    mSubgraphGear1(new Subgraph(graph,"LeftWheelTire")),
    mSubgraphGear2(new Subgraph(graph,"RightWheel")),
    mSubgraphGear3(new Subgraph(graph,"RightWheelTire")),
    mSubgraphGear4(new Subgraph(graph,"FrontWheel")),
    mSubgraphGear5(new Subgraph(graph,"FrontWheelTire")),
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"InternalFlame")),
    mSubgraphFlame1(new Subgraph(graph,"ExternalFlame")),
    // control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"AileronLeft")),
    mSubgraphAileronRight(new Subgraph(graph,"AileronRight")),
    mSubgraphElevatorLeft(new Subgraph(graph,"ElevatorLeft")),
    mSubgraphElevatorRight(new Subgraph(graph,"ElevatorRight")),
    mSubgraphRudderLeft(new Subgraph(graph,"RudderLeft")),
    mSubgraphRudderRight(new Subgraph(graph,"RudderRight"))
{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );
}

F15Aircraft::~F15Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F15Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F15Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F15Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f15.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F15Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
        mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );
        mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down );
        mSubgraphGear5->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
F15Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
    mSubgraphFlame0->Enable( throttle >= THROTTLE_FLAME_LOW );
    mSubgraphFlame1->Enable( throttle >= THROTTLE_FLAME_HIGH );  // afterburner
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F15Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudderLeft,  controlFraction, LEFT_RUDDER  );
            RotateRudder( *mSubgraphRudderRight, controlFraction, RIGHT_RUDDER );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F16Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F16Aircraft::F16Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F16Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"internal_front_gear_door")),
    mSubgraphGear1(new Subgraph(graph,"internal_right_main_door")),
    mSubgraphGear2(new Subgraph(graph,"internal_left_main_door")),
    //
    mSubgraphGear3(new Subgraph(graph,"front_tire")),
    mSubgraphGear4(new Subgraph(graph,"front_aft_strut")),
    mSubgraphGear5(new Subgraph(graph,"front_lower_strut")),
    mSubgraphGear6(new Subgraph(graph,"front_upper_strut")),
    //
    mSubgraphGear7(new Subgraph(graph,"left_main_strut")),
    mSubgraphGear8(new Subgraph(graph,"left_main_tire")),
    mSubgraphGear9(new Subgraph(graph,"left_lower_main_strut")),
    mSubgraphGear10(new Subgraph(graph,"left_upper_main_strut")),
    //
    mSubgraphGear11(new Subgraph(graph,"right_main_strut")),
    mSubgraphGear12(new Subgraph(graph,"right_main_tire")),
    mSubgraphGear13(new Subgraph(graph,"right_lower_main_strut")),
    mSubgraphGear14(new Subgraph(graph,"right_upper_main_strut")),
    //
    mSubgraphGear15(new Subgraph(graph,"external_front_gear_door")),
    mSubgraphGear16(new Subgraph(graph,"external_left_main_door")),
    mSubgraphGear17(new Subgraph(graph,"external_right_main_door")),
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"internal_flame")),
    mSubgraphFlame1(new Subgraph(graph,"external_flame")),
    // control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"left_upper_aileron")),
    mSubgraphAileronRight(new Subgraph(graph,"right_upper_aileron")),
    mSubgraphFlapLeft(new Subgraph(graph,"left_upper_flap")),
    mSubgraphFlapRight(new Subgraph(graph,"right_upper_flap")),
    mSubgraphElevatorLeft(new Subgraph(graph,"left_upper_horizontal_tail")),
    mSubgraphElevatorRight(new Subgraph(graph,"right_upper_horizontal_tail")),
    mSubgraphRudder0(new Subgraph(graph,"rudder2")),
    mSubgraphRudder1(new Subgraph(graph,"rudder3")),
    mSubgraphRudder2(new Subgraph(graph,"rudder5")),
    mSubgraphRudder3(new Subgraph(graph,"rudder6")),
    // junk:
    mSubgraphJunk0(new Subgraph(graph,"lhocondenscone")),
    mSubgraphJunk1(new Subgraph(graph,"rhocondenscone"))
{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );

    // Disable junk parts of F-16 model.
    mSubgraphJunk0->Enable( false );
    mSubgraphJunk1->Enable( false );
}

F16Aircraft::~F16Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F16Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F16Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F16Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f16.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F16Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    // Known problem: The door for the front wheel should be rotated
    // into the fuselage but isn't implemented.

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );  mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );  mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down );  mSubgraphGear5->Enable( down );
        mSubgraphGear6->Enable( down );  mSubgraphGear7->Enable( down );
        mSubgraphGear8->Enable( down );  mSubgraphGear9->Enable( down );
        mSubgraphGear10->Enable( down ); mSubgraphGear11->Enable( down );
        mSubgraphGear12->Enable( down ); mSubgraphGear13->Enable( down );
        mSubgraphGear14->Enable( down ); mSubgraphGear15->Enable( down );
      //mSubgraphGear16->Enable( down ); mSubgraphGear17->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
F16Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
//  mSubgraphFlame0->Enable( throttle >= THROTTLE_FLAME_LOW );  // see-thru problem
    mSubgraphFlame1->Enable( throttle >= THROTTLE_FLAME_HIGH );  // afterburner
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F16Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
#if 0
            RotateAileron( *mSubgraphFlapLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphFlapRight, controlFraction, RIGHT_AILERON );
#endif
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder0, controlFraction );
            RotateRudder( *mSubgraphRudder1, controlFraction );
            RotateRudder( *mSubgraphRudder2, controlFraction );
            RotateRudder( *mSubgraphRudder3, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  F18Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
F18Aircraft::F18Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,F18Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"LeftWheel")),
    mSubgraphGear1(new Subgraph(graph,"LeftDoor")),
    mSubgraphGear2(new Subgraph(graph,"RightWheel")),
    mSubgraphGear3(new Subgraph(graph,"RightDoor")),
    mSubgraphGear4(new Subgraph(graph,"FrontWheel")),
    mSubgraphGear5(new Subgraph(graph,"FrontDoor")),
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"InternalFlame")),
    mSubgraphFlame1(new Subgraph(graph,"ExternalFlame")),
    // control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"AileronLeft")),
    mSubgraphAileronRight(new Subgraph(graph,"AileronRight")),
    mSubgraphElevatorLeft(new Subgraph(graph,"ElevatorLeft")),
    mSubgraphElevatorRight(new Subgraph(graph,"ElevatorRight")),
    mSubgraphRudderLeft(new Subgraph(graph,"RudderLeft")),
    mSubgraphRudderRight(new Subgraph(graph,"RudderRight"))
{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );
}

F18Aircraft::~F18Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
F18Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return F18Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
F18Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_f18.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
F18Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) ) 
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
        mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );
        mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down );
        mSubgraphGear5->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
F18Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
  //mSubgraphFlame0->Enable( throttle >= THROTTLE_FLAME_LOW );  // see-thru problem
    mSubgraphFlame1->Enable( throttle >= THROTTLE_FLAME_HIGH );  // afterburner
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
F18Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudderLeft,  controlFraction, LEFT_RUDDER  );
            RotateRudder( *mSubgraphRudderRight, controlFraction, RIGHT_RUDDER );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  SR71Aircraft  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SR71Aircraft::SR71Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,SR71Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"roueA")),  // roue means wheel in French
    mSubgraphGear1(new Subgraph(graph,"roueD")),
    mSubgraphGear2(new Subgraph(graph,"roueG")),
    // struts:
    mSubgraphStrut0(new Subgraph(graph,"axeA")),
    mSubgraphStrut1(new Subgraph(graph,"axeD")),
    mSubgraphStrut2(new Subgraph(graph,"axeG")),
    mSubgraphStrut3(new Subgraph(graph,"barrebasA")),
    mSubgraphStrut4(new Subgraph(graph,"barrebasD")),
    mSubgraphStrut5(new Subgraph(graph,"barrebasG")),
    mSubgraphStrut6(new Subgraph(graph,"barrehautA")),
    mSubgraphStrut7(new Subgraph(graph,"barrehautD")),
    mSubgraphStrut8(new Subgraph(graph,"barrehautG")),
    mSubgraphStrut9(new Subgraph(graph,"porteD1")),
    mSubgraphStrut10(new Subgraph(graph,"porteD2")),
    mSubgraphStrut11(new Subgraph(graph,"porteG1")),
    mSubgraphStrut12(new Subgraph(graph,"porteG2")),
    mSubgraphStrut13(new Subgraph(graph,"verrinA")),
    mSubgraphStrut14(new Subgraph(graph,"verrinD")),
    mSubgraphStrut15(new Subgraph(graph,"verrinG")),
    // parachute:
    mSubgraphParachute(new Subgraph(graph,"parachute")),
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"flammeD1")),
    mSubgraphFlame1(new Subgraph(graph,"flammeD2")),
    mSubgraphFlame2(new Subgraph(graph,"flammeD3")),
    mSubgraphFlame3(new Subgraph(graph,"flammeG1")),
    mSubgraphFlame4(new Subgraph(graph,"flammeG2")),
    mSubgraphFlame5(new Subgraph(graph,"flammeG3")),
    // Control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"aileronG")),
    mSubgraphAileronRight(new Subgraph(graph,"aileronD")),
    mSubgraphElevatorLeft(new Subgraph(graph,"profondeurG")),
    mSubgraphElevatorRight(new Subgraph(graph,"profondeurD"))
    // (rudder omitted)
{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame2->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame3->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame4->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame5->GetRootNode() );

    mSubgraphParachute->Enable( false );
}

SR71Aircraft::~SR71Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
SR71Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return SR71Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
SR71Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_sr71.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
SR71Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down )) 
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
        mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );

        mSubgraphStrut0->Enable( down );
        mSubgraphStrut1->Enable( down );
        mSubgraphStrut2->Enable( down );
        mSubgraphStrut3->Enable( down );
        mSubgraphStrut4->Enable( down );
        mSubgraphStrut5->Enable( down );
        mSubgraphStrut6->Enable( down );
        mSubgraphStrut7->Enable( down );
        mSubgraphStrut8->Enable( down );
        mSubgraphStrut9->Enable( down );
        mSubgraphStrut10->Enable( down );
        mSubgraphStrut11->Enable( down );
        mSubgraphStrut12->Enable( down );
        mSubgraphStrut13->Enable( down );
        mSubgraphStrut14->Enable( down );
        mSubgraphStrut15->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
SR71Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
    const bool flame = (throttle >= THROTTLE_FLAME);
    mSubgraphFlame0->Enable( flame );
    mSubgraphFlame1->Enable( flame );
    mSubgraphFlame2->Enable( flame );
    mSubgraphFlame3->Enable( flame );
    mSubgraphFlame4->Enable( flame );
    mSubgraphFlame5->Enable( flame );
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
SR71Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

#if 0
        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
#endif
    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  Mirage2000Aircraft  //////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
Mirage2000Aircraft::Mirage2000Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,Mirage2000Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"front_tire")),
    mSubgraphGear1(new Subgraph(graph,"front_wheel")),
    mSubgraphGear2(new Subgraph(graph,"front_door_fwd")),
    mSubgraphGear3(new Subgraph(graph,"left_wheel")),
    mSubgraphGear4(new Subgraph(graph,"left_tire")),
    mSubgraphGear5(new Subgraph(graph,"left_door")),
    mSubgraphGear6(new Subgraph(graph,"right_wheel")),
    mSubgraphGear7(new Subgraph(graph,"right_tire")),
    mSubgraphGear8(new Subgraph(graph,"right_door")),
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"internal_flame")),
    mSubgraphFlame1(new Subgraph(graph,"external_flame")),
    // Control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"VoletGauche")),  // are elevators too
    mSubgraphAileronRight(new Subgraph(graph,"VoletDroit")),
    mSubgraphRudder(new Subgraph(graph,"rudder"))
{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );
}

Mirage2000Aircraft::~Mirage2000Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
Mirage2000Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return Mirage2000Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
Mirage2000Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_mirage2000.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
Mirage2000Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down )) 
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
        mSubgraphGear1->Enable( down );
        mSubgraphGear2->Enable( down );
        mSubgraphGear3->Enable( down );
        mSubgraphGear4->Enable( down );
        mSubgraphGear5->Enable( down );
        mSubgraphGear6->Enable( down );
        mSubgraphGear7->Enable( down );
        mSubgraphGear8->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
Mirage2000Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
//  mSubgraphFlame0->Enable( throttle >= THROTTLE_FLAME_LOW ); // see-thru problem
    mSubgraphFlame1->Enable( throttle >= THROTTLE_FLAME );  // afterburner
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
Mirage2000Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        // On Mirage, ailerons are also elevators.
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphAileronLeft,  controlFraction );
            RotateElevator( *mSubgraphAileronRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudder, controlFraction );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  SU37Aircraft  /////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SU37Aircraft::SU37Aircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,SU37Aircraft::ClassGetSpecs()),
    // gear:
    mSubgraphGear0(new Subgraph(graph,"UC")),  // under carriage
    // exhaust flame:
    mSubgraphFlame0(new Subgraph(graph,"L_Reheat")),
    mSubgraphFlame1(new Subgraph(graph,"R_Reheat")),
    // control surfaces:
    mSubgraphAileronLeft(new Subgraph(graph,"WNG_Left_Flaperon")),
    mSubgraphAileronRight(new Subgraph(graph,"WNG_Right_Flaperon")),
    mSubgraphElevatorLeft(new Subgraph(graph,"ELV_Elevon_Left")),
    mSubgraphElevatorRight(new Subgraph(graph,"ELV_Elevon_Right")),
    mSubgraphRudderLeft(new Subgraph(graph,"TF_Left_Rudder")),
    mSubgraphRudderRight(new Subgraph(graph,"TF_Right_Rudder"))

{
    SetThrottle( 0.0f );

    // Exhaust flame shouldn't cast shadows.
    ShadowSceneGraph::DisableShadows( mSubgraphFlame0->GetRootNode() );
    ShadowSceneGraph::DisableShadows( mSubgraphFlame1->GetRootNode() );
}

SU37Aircraft::~SU37Aircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
SU37Aircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
    return SU37Aircraft::ClassGetSpecs();
}

const AircraftSpecs&
SU37Aircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_su37.conf" )
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
SU37Aircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down )) 
    {
        Parent::SetLandingGear( down );

        mSubgraphGear0->Enable( down );
    }
}

/*****************************************************************************
 * When throttle is changed, animate jet exhaust flame.
 *****************************************************************************/
void
SU37Aircraft::SetThrottle( const fp throttle )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::SetThrottle( throttle );
    const bool flame = (throttle >= THROTTLE_FLAME);
    mSubgraphFlame0->Enable( flame );
    mSubgraphFlame1->Enable( flame );
}

/*****************************************************************************
 * Rotate control surfaces (ailerons etc).
 *****************************************************************************/
void
SU37Aircraft::RotateControlSurfaces( const uint axis, const fp controlFraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);
ASSERT_AXIS3(axis);

    switch ( axis )
    {
        case AXIS_PITCH:
        {
            RotateElevator( *mSubgraphElevatorLeft,  controlFraction );
            RotateElevator( *mSubgraphElevatorRight, controlFraction );
        }
        break;

        case AXIS_ROLL:
        {
            RotateAileron( *mSubgraphAileronLeft,  controlFraction, LEFT_AILERON );
            RotateAileron( *mSubgraphAileronRight, controlFraction, RIGHT_AILERON );
        }
        break;

        case AXIS_YAW:
        {
            RotateRudder( *mSubgraphRudderLeft,  controlFraction, LEFT_RUDDER  );
            RotateRudder( *mSubgraphRudderRight, controlFraction, RIGHT_RUDDER );
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
//////////////////////////  SpaceShuttleAircraft  //////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SpaceShuttleAircraft::SpaceShuttleAircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,SpaceShuttleAircraft::ClassGetSpecs()),
    mSubgraphBayDoorLeft(new Subgraph(graph,"bay_door_left")),
    mSubgraphBayDoorRight(new Subgraph(graph,"bay_door_right")),
    mBayDoorsFraction(0.0f)
{
    // NOP
}

SpaceShuttleAircraft::~SpaceShuttleAircraft()
{
    // NOP
}

/*****************************************************************************
 * Reset.
 *****************************************************************************/
void
SpaceShuttleAircraft::Reset( void )
{
    Parent::Reset();

    // Close bay doors.
    SwingBayDoors( 0.0f );
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
SpaceShuttleAircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    return SpaceShuttleAircraft::ClassGetSpecs();
}

const AircraftSpecs&
SpaceShuttleAircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_spaceShuttle.conf" )
}

/*****************************************************************************
 * Really open bay doors.
 * Reason for this subterfuge is to reuse keyboard control for swinging wings.
 *****************************************************************************/
void
SpaceShuttleAircraft::SwingWings( const float fraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    SwingBayDoors( fraction );
}

/*****************************************************************************
 * Open bay doors { 0.0:closed ... 1.0:open }
 *****************************************************************************/
void
SpaceShuttleAircraft::SwingBayDoors( const float fraction )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    mBayDoorsFraction = Clamp1( fraction );

    // Undo rotation/translation of wing subgraphs from previous call.
    mSubgraphBayDoorLeft->Reset();
    mSubgraphBayDoorRight->Reset();

    // deg = 0  fully closed
    // deg = 70 fully open
    const Degree deg = mBayDoorsFraction * 70.0f;
    const Radian rad = Deg2Rad( deg );

    // Translate to center the pivot at door hinges.
    fp offset0 = 0.03f;
    fp offset1 = 0.08f;
    mSubgraphBayDoorLeft->Translate(  YY,  offset0 );  // up
    mSubgraphBayDoorRight->Translate( YY,  offset0 );  // up
    mSubgraphBayDoorLeft->Translate(  XX, -offset1 );  // horz
    mSubgraphBayDoorRight->Translate( XX,  offset1 );  // horz

    mSubgraphBayDoorLeft->Rotate(  ZZ,  rad );
    mSubgraphBayDoorRight->Rotate( ZZ, -rad );

    // Undo translation.
    offset0 = -offset0;
    offset1 = -offset1;
    mSubgraphBayDoorLeft->Translate(  YY,  offset0 );  // up
    mSubgraphBayDoorRight->Translate( YY,  offset0 );  // up
    mSubgraphBayDoorLeft->Translate(  XX, -offset1 );  // horz
    mSubgraphBayDoorRight->Translate( XX,  offset1 );  // horz
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  SikorskyAircraft  ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SikorskyAircraft::SikorskyAircraft( shptr<Graph> graph, const WorldVertex& pos )
:   Parent(graph,pos,SikorskyAircraft::ClassGetSpecs()),
    mSubgraphRotor0(new Subgraph(graph,"blade1")),
    mSubgraphRotor1(new Subgraph(graph,"blade2")),
    mSubgraphRotor2(new Subgraph(graph,"blade3")),
    mSubgraphRotor3(new Subgraph(graph,"blade4")),
    mSubgraphRotor4(new Subgraph(graph,"fastblade1")),  // blurred
    mSubgraphRotor5(new Subgraph(graph,"fastblade2")),  // blurred
    mSubgraphRotor6(new Subgraph(graph,"fastblade3")),  // blurred
    mSubgraphRotor7(new Subgraph(graph,"fastblade4")),  // blurred
    mSubgraphTailRotor(new Subgraph(graph,"TailRotor"))
{
    // NOP
}

SikorskyAircraft::~SikorskyAircraft()
{
    // NOP
}

/*****************************************************************************
 * @return Aircraft specifications struct.
 *****************************************************************************/
const AircraftSpecs&
SikorskyAircraft::GetSpecs( void )  // virtual method
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    return SikorskyAircraft::ClassGetSpecs();
}

const AircraftSpecs&
SikorskyAircraft::ClassGetSpecs( void )  // class method
{
    CODE_CLASS_GET_SPECS( "physics_sikorsky.conf" )
}

/*****************************************************************************
 * Tick that pulses animation.
 *****************************************************************************/
void
SikorskyAircraft::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    Parent::Tick( millisecElapsed );

    RotateRotors();
}

/*****************************************************************************
 * Rotate propeller.
 *****************************************************************************/
void
SikorskyAircraft::RotateRotors( void )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    const uint   axisRotor     = YY;  // main rotors
//  const uint   axisTailRotor = ZZ;
  {
    const Radian radian = Deg2Rad( Degree(10.0f) );
    mSubgraphRotor0->Rotate( axisRotor, radian );
    mSubgraphRotor1->Rotate( axisRotor, radian );
    mSubgraphRotor2->Rotate( axisRotor, radian );
    mSubgraphRotor3->Rotate( axisRotor, radian );
  }
  {
    const Radian radian = Deg2Rad( Degree(5.0f) );  // blurred rotors
    mSubgraphRotor4->Rotate( axisRotor, radian );
    mSubgraphRotor5->Rotate( axisRotor, radian );
    mSubgraphRotor6->Rotate( axisRotor, radian );
    mSubgraphRotor7->Rotate( axisRotor, radian );
  }

    // The origin of the tail rotor is the center of the helicopter
    // rather than the rotor's hub, so just toggle enablement.
#if 1
    mSubgraphTailRotor->Enable( not mSubgraphTailRotor->IfEnabled() );
#else
  {
    const Radian radian = Deg2Rad( Degree(30.0f) );
    mSubgraphTailRotor->Rotate( axisTailRotor, radian );
  }
#endif
}

/*****************************************************************************
 * Retract/extend landing gear.
 *****************************************************************************/
void
SikorskyAircraft::SetLandingGear( const bool down )
{
CHECK_TYPESIG(this,TYPESIG_AIRCRAFT);

    if ( IfCanSetLandingGear( down ) )
    {
        Parent::SetLandingGear( down );

        Parent::mLandingGearDown = true;  // override
    }
}

} // namespace program
