Frankeincoder 2

a practical .dll writing tutorial for Orbiter ships

by Art Eaton


Prerequisites:

This is a more advanced version of the Basic Template with some more elements introduced. For this Intermediate Template, multiple tanks, altered Hover thruster thrust vectors, and Pressure Dependency are added.

Introduction


Just to re-iterate what we are doing from the Basic Tutorial:


Code type 1: Stuff we will not change in the .cpp file template. This stuff will be in plain black text in the template. You must follow the format of these things copying all usages of punctuation, and capitalization...for now.


Code type 2. This stuff is highlighted in yellow. These are variables that will be different for every ship. A lot of these variables will be recognizable to those with experience in .cfg files. Ship and mesh file names, landing points, cross sections, and pmi are some of the variables you can modify.


Code type 3. This stuff is highlighted in a variety of colors that indicate what ship systems it applies to. If the stuff is used by two or more systems, the code will be striped with the applicable colors. These are optional components that may or may not be included in a ship design. The colors include: Red=main thrusters, Blue=hover thrusters, Brown=Retro thrusters, Green=RCS thrusters.

An example of a multicolored variable would be the name of a tank that supplies fuel to both main engines and RCS thrusters.


Writing the Module


All right, to help out your eyes, from this part on all tutorial instructions will be written in Purple. Note that in a compiler text editor, any notes in it that are not part of the code will be preceeded by //. Data that will not be compiled will then (usually) be highlighted green in your editor program. You can see this by opening an orbiter sample .cpp file with your compiler editor program under Orbiter/OrbiterSDK/Samples/ShuttlePB/Shuttlepb.cpp.

OK, take a deep breath. Let's put in some code. Copy the following to your .cpp file:



#define STRICT

#define ORBITER_MODULE


#include "orbitersdk.h"



That is it for now, we will put in more at the end of the file. Now lets put in some stuff just for your ship. Do you want an airframe for lift in atmosphere? If you do, we will include this lift calculating and defining code:





// 1. vertical lift component (wings and body)


void VLiftCoeff (double aoa, double M, double Re, double *cl, double *cm, double *cd)

{

const int nabsc = 9;

static const double AOA[nabsc] = {-180*RAD,-60*RAD,-30*RAD,-2*RAD, 15*RAD,20*RAD,25*RAD,60*RAD,180*RAD};

static const double CL[nabsc] = { 0, 0, -0.4, 0, 0.7, 1, 0.8, 0, 0};

static const double CM[nabsc] = { 0, 0, 0.014, 0.004,-0.0074,-0.094,-0.012, 0, 0};

for (int i = 0; i < nabsc-1 && AOA[i+1] < aoa; i++);

double f = (aoa-AOA[i]) / (AOA[i+1]-AOA[i]);

*cl = CL[i] + (CL[i+1]-CL[i]) * f; // aoa-dependent lift coefficient

*cm = CM[i] + (CM[i+1]-CM[i]) * f; // aoa-dependent moment coefficient

double saoa = sin(aoa);

double pd = 0.015 + 0.4*saoa*saoa; // profile drag

*cd = pd + oapiGetInducedDrag (*cl, 1.5, 0.7) + oapiGetWaveDrag (M, 0.75, 1.0, 1.1, 0.04);

// profile drag + (lift-)induced drag + transonic/supersonic wave (compressibility) drag

}


// 2. horizontal lift component (vertical stabilisers and body)


void HLiftCoeff (double beta, double M, double Re, double *cl, double *cm, double *cd)

{

const int nabsc = 8;

static const double BETA[nabsc] = {-180*RAD,-135*RAD,-90*RAD,-45*RAD,45*RAD,90*RAD,135*RAD,180*RAD};

static const double CL[nabsc] = { 0, +0.3, 0, -0.3, +0.3, 0, -0.3, 0};

for (int i = 0; i < nabsc-1 && BETA[i+1] < beta; i++);

*cl = CL[i] + (CL[i+1]-CL[i]) * (beta-BETA[i]) / (BETA[i+1]-BETA[i]);

*cm = 0.0;

*cd = 0.015 + oapiGetInducedDrag (*cl, 1.5, 0.6) + oapiGetWaveDrag (M, 0.75, 1.0, 1.1, 0.04);

}



That stuff defined the lift your ships creates as a function of airfoil angle of attack in two different catagories, Vertical lift, such as wings, lifting body, and horizontal stabilizers, and Horizontal lift, such as is caused by the rudder and vertical stabilizers. Next, we are going to stick in some of the Code Type 2 material, in which you will provide the entries in yellow. In this example, the name of the ship, and the name of the mesh are “STARSHIP”. You can see that most of this stuff correlates directly to .ini files. Some of the things you do not actually need in the file. Other things such as SetWingAspect and SetWingEffectiveness are obsolete and are only included for backwards compatability. They might not be supported in the next verion of Orbiter. You can refer to the Orbiter API reference for more information on all of these perameters. Some of this stuff refers to other files to give this ship pre-made contol definitions, such as the standard orbiter designations of main, hover, retro and rcs thrusters, and the keyboard controls they use.





class STARSHIP: public VESSEL2 {

public:

STARSHIP (OBJHANDLE hVessel, int flightmodel)

: VESSEL2 (hVessel, flightmodel) {}

void clbkSetClassCaps (FILEHANDLE cfg);

};



void STARSHIP::clbkSetClassCaps (FILEHANDLE cfg)

{

SetSize (25);

SetEmptyMass (100000);

SetCW (0.09, 1.0, 1.8, 1.8);

SetReentryTexture (NULL);

SetWingAspect (0.7);

SetWingEffectiveness (2.5);

SetCrossSections (_V(10.5,15.0,5.8));

SetRotDrag (_V(0.6,0.6,0.35));

if (GetFlightModel() >= 1) {

SetPitchMomentScale (5e-5);

SetBankMomentScale (5e-5);

}

SetPMI (_V(131.19,156.2,31.43));

SetTrimScale (0.05);

SetCameraOffset (_V(0,0.0,5.0));

SetDockParams (_V(0,0,-20.5), _V(0,0,-1), _V(0,0,-1));

SetTouchdownPoints (_V(0,-20,-20.5), _V(-10,20,-20.5), _V(10,20,-20.5));


Ok, Now if you added the Lift functions to your .cpp file, you will want to describe your ship's airfoils. If you like, you can just use the variables as they are. These variables belong to a delta Glider. Paste and modify this section of code into your .cpp:



// ********************* aerodynamics ***********************


CreateAirfoil (LIFT_VERTICAL, _V(0,0,-0.2), VLiftCoeff, 5, 90, 1.5);

// wing and body lift+drag components


CreateAirfoil (LIFT_HORIZONTAL, _V(0,0,-4), HLiftCoeff, 5, 15, 1.5);

// vertical stabiliser and body lift and drag components


CreateControlSurface (AIRCTRL_ELEVATOR, 1.2, 1.5, _V( 0,0,-7.2), AIRCTRL_AXIS_XPOS);

CreateControlSurface (AIRCTRL_RUDDER, 0.8, 1.5, _V( 0,0,-7.2), AIRCTRL_AXIS_YPOS);

CreateControlSurface (AIRCTRL_AILERON, 0.2, 1.5, _V( 7.5,0,-7.2), AIRCTRL_AXIS_XPOS);

CreateControlSurface (AIRCTRL_AILERON, 0.2, 1.5, _V(-7.5,0,-7.2), AIRCTRL_AXIS_XNEG);

CreateControlSurface (AIRCTRL_ELEVATORTRIM, 0.1, 1.5, _V( 0,0,-7.2), AIRCTRL_AXIS_XPOS);



Now we are going to put in some important performance variables. In this section of Type Three code, we will give names and values to things that Orbiter will be looking for. There are some standard names, or “handles” that Orbiter will be looking for. It is looking for the following things:



1. FUELMASS -This name will be assigned a second name if there is more than one tank on board. In the following example, you will find ZENI_FUELMASS, and PJET_FUELMASS. ZENI is the name of the main engine (using Orbiters' main engine controlls) type the example file is using. PJET is the name of the thrusters that will be using the Hover controls. You could make up any name you want, just write it in capital letters, and use it anywhere you see the word ZENI or PJET. You can also have more FUELMASS constants defined, or maybe just one that is used everywhere.

Paste and modify the following code to your .cpp. Assign as many FUELMASS names as you like. Here I am using two:


const double ZENI_FUELMASS = 5500.0;

const double PJET_FUELMASS = 29800.0;


In this example it can be seen that I am using the ZENI_FUELMASS for both main engines and rcs thrusters, thus the red and green colors. I am not using brown, as there will be no retro engines on this ship.



2. ISP -In the following example, you can see that the name assigned to the FUELMASS constant is used to define what the ISP of that fuel is. You could just have these values read ZENI_ISP, or you could use the pressure dependency functions that reduce the power of the engines in atmosphere. You do that by giving two ISP values for each fuel; VAC and NML. VAC is the ISP you will be using with the thruster when the ship is in space, NML is the value you will use at a specific pressure. You define that pressure later. If you do not want the thruster to work in atmosphere at all, such as with an ion engine, you set the pressure very low. If you want the engine to have a tapering efficiency, as rocket engines typically do, you might use normal sea level pressure. In this example you will see that the value for VAC_ZENI_ISP is used by the reaction thrusters all the time, thus they work in atmosphere. You will also see that the PJET fuel value stays the same, so it could be done with a single line describing PJET_ISP. If you like, you only really need one constant defining ISP, and you could use that one anywhere these show up:


const double VAC_ZENI_ISP = 6.25e8;

const double NML_ZENI_ISP = 1e1;

const double VAC_PJET_ISP = 4e4;

const double NML_PJET_ISP = 4e4;



3. MAXMAINTHRUST, MAXHOVERTHRUST, MAXRCSTHRUST, MAXRETROTHRUST. - You will see in this section that I added a name to each thrust value that links them to all other handles using the same name. This helps tie the thrust values, the isp values and the mass value of the fuel all to each other. Later, another value will create an actual tank that the fuels will come from, and the names for the tanks will be linked to each thruster in the thruster description. You will see here that the RCS thruster, which uses ZENI fuel, will finally be defined. It is called STARSHIP_MAXRCSTHRUST just to give it a different name. You can change that name too. Now paste these in and modify them, adding retros or dis-including anything you like:



const double ZENI_MAXMAINTH = 5.5e6;

const double PJET_MAXHOVERTH = 2.5e6;

const double STARSHIP_MAXRCSTH = 1e6;


4. P_NML -We will now define that pressure value that affects the ISP values. If you didn't include the VAC and NML names in the ISP descriptions, you do not need this. You also do not need it if you want it to just be Earth normal surface pressure. This pressure is defined in BAR, which is 1014 (?) millibar. In the example, it is set to just 1 millibar (1e1) which reduces the ZENI thruster (an ion engine) output to nothing at about 75km of altitude. Paste and modify this:



const double P_NML = 1e1;



All right! Now those pesky constants are out of the way we can get on to creating the ship. Lets define the thruster handles. We are going to make one for each thruster type, indicating the number of each thruster type, and we are going to make one for each thruster group. A thruster group is later defined by the CreateThrusterGroup. This is most commonly used for groups and pairs of RCS thrusters, but you can define more than one main engine etc... instead of just calling it one engine and making two exhausts up like we will later in this example. Here we have defined handles for one main virtual thruster, one hover, 14 RCS, and 4 groups of rcs thrusters. Paste and modify this into your .cpp remembering to add a retro handle th_retro if you want retro thrusters. The numbers in brackets after the RCS handle means that 14 different thrusters will be named, starting with th_rcs [0] and ending with th_rcs[13]. Most number sets like that start with 0 instead of 1. th_group are the groups of RCS thrusters that are temporarily re-named when they are part of various thruster groups. RCS thrusters are complex, but you don't really need to do a lot of modifications to this code to make your ships work. Just drop it in if you like.


THRUSTER_HANDLE th_main, th_hover, th_rcs[14], th_group[4];



Cool Beans, this is fun...I hope we havn't screwed anything up yet! Now remember how we were going to define fuel tanks and then put our ZENI_FULEMASS and PJET_FUELMASS in them? Let's do that now. The handles for the two tanks are hpr and hpp, but you can change that to what you want, just like you can change the names ZENI, PJET, and STARSHIP. You can also put in multiple tanks and put any fuel in you want. You could have multiple RCS tanks for instance, all with ZENI or PJET fuel in them, and if you used one particular thruster too much, you would run out and lose manueverability! Let's put in two tanks, filling one with ZENI fuel and the other with PJET fuel:



PROPELLANT_HANDLE hpr, hpp;

hpr = CreatePropellantResource (ZENI_FUELMASS);

hpp = CreatePropellantResource (PJET_FUELMASS);


Now let's do our particle streams. I myself have not really gone over this too much, but I know the variables in the list correspond to ones in Vinka's .INI files. More information can also be found in the Orbiter SDK. Here we are creating a diffuse and emissive particle stream for the mains and the hover engines. Add all you like up to the number that orbiter supports (there IS a limit). Paste and modify the following:



PARTICLESTREAMSPEC contrail_main = {

0, 5.0, 16, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,

PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,

PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1

};

PARTICLESTREAMSPEC contrail_hover = {

0, 5.0, 8, 200, 0.15, 1.0, 5, 3.0, PARTICLESTREAMSPEC::DIFFUSE,

PARTICLESTREAMSPEC::LVL_PSQRT, 0, 2,

PARTICLESTREAMSPEC::ATM_PLOG, 1e-4, 1

};

PARTICLESTREAMSPEC exhaust_main = {

0, 2.0, 20, 200, 0.05, 0.1, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,

PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,

PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1

};

PARTICLESTREAMSPEC exhaust_hover = {

0, 2.0, 10, 200, 0.05, 0.05, 8, 1.0, PARTICLESTREAMSPEC::EMISSIVE,

PARTICLESTREAMSPEC::LVL_SQRT, 0, 1,

PARTICLESTREAMSPEC::ATM_PLOG, 1e-5, 0.1

};



On to creating the thrusters. You will have some work to do here, but you wil see that when a thruster is created, all the variables it needs are called by the handles we have created beforehand, which you can now identify. With the exception of the names for the tanks (ex. hpr,hpp) and the ISP values, you can mostly just leave the RCS thruster definitions alone. They come directly from the PB shuttle code, with only those two variables modified. You can also add in retro thrusters if you want to, and assign particle streams to them. The final thing of note is that this is the first point at which the example here varies from something you can do with a config or .ini file. In this example the orientation of the hover thruster, the PJET's, the orientation of the thrust vector they produce is Z- making them parallel to the main engines. For this ship, they are actually rockets used as propulsion in atmosphere where the Ion (ZENI) engines won't function. Normal Orbiter ship Hover engines are aligned 0,-1,0. The example is done this way to help demonstrate how much more control you have by writing a .dll. Paste in the following and modify for your ship. Remember, if you don't want to have variable pressure, don't include the VAC and NML named stuff! You can see that the RCS thrusters only use one ISP value. Just use that example for defining the other thrusters if you like.




th_main = CreateThruster (_V(0,0,-20.5), _V(0,0,1), ZENI_MAXMAINTH, hpr, VAC_ZENI_ISP, NML_ZENI_ISP, P_NML);

CreateThrusterGroup (&th_main, 1, THGROUP_MAIN);

AddExhaust (th_main, 8, 1, _V(20,0,-18.3), _V(0,0,-1));

AddExhaust (th_main, 8, 1, _V(-20,0,-18.3), _V(0,0,-1));


th_hover = CreateThruster (_V(0,0,-18), _V(0,0,1), PJET_MAXHOVERTH, hpp, VAC_PJET_ISP, NML_PJET_ISP, P_NML);

CreateThrusterGroup (&th_hover, 1, THGROUP_HOVER);

AddExhaust (th_hover, 20, 1, _V(10,0,-20.5), _V(0,0,-1));

AddExhaust (th_hover, 20, 1, _V(-10,0,-20.5), _V(0,0,-1));


AddExhaustStream (th_hover, _V(10,0, -30), &contrail_hover);

AddExhaustStream (th_hover, _V(-10,0,-30), &contrail_hover);

AddExhaustStream (th_main, _V(20,0,-28), &contrail_main);

AddExhaustStream (th_main, _V(-20,0,-28), &contrail_main);

AddExhaustStream (th_hover, _V(10,0, -20.5), &exhaust_hover);

AddExhaustStream (th_hover, _V(-10,0,-20.5), &exhaust_hover);

AddExhaustStream (th_main, _V(20,0,-18.3), &exhaust_main);

AddExhaustStream (th_main, _V(-20,0,-18.3), &exhaust_main);


th_rcs[ 0] = CreateThruster (_V( 1,0, 3), _V(0,1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 1] = CreateThruster (_V( 1,0, 3), _V(0,-1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 2] = CreateThruster (_V(-1,0, 3), _V(0,1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 3] = CreateThruster (_V(-1,0, 3), _V(0,-1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 4] = CreateThruster (_V( 1,0,-3), _V(0,1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 5] = CreateThruster (_V( 1,0,-3), _V(0,-1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 6] = CreateThruster (_V(-1,0,-3), _V(0,1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 7] = CreateThruster (_V(-1,0,-3), _V(0,-1,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 8] = CreateThruster (_V( 1,0, 3), _V(-1,0,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[ 9] = CreateThruster (_V(-1,0, 3), _V(1,0,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[10] = CreateThruster (_V( 1,0,-3), _V(-1,0,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[11] = CreateThruster (_V(-1,0,-3), _V(1,0,0), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[12] = CreateThruster (_V( 0,0,-3), _V(0,0,1), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);

th_rcs[13] = CreateThruster (_V( 0,0, 3), _V(0,0,-1), STARSHIP_MAXRCSTH, hpr, VAC_ZENI_ISP);


th_group[0] = th_rcs[0];

th_group[1] = th_rcs[2];

th_group[2] = th_rcs[5];

th_group[3] = th_rcs[7];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHUP);


th_group[0] = th_rcs[1];

th_group[1] = th_rcs[3];

th_group[2] = th_rcs[4];

th_group[3] = th_rcs[6];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHDOWN);


th_group[0] = th_rcs[0];

th_group[1] = th_rcs[4];

th_group[2] = th_rcs[3];

th_group[3] = th_rcs[7];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKLEFT);


th_group[0] = th_rcs[1];

th_group[1] = th_rcs[5];

th_group[2] = th_rcs[2];

th_group[3] = th_rcs[6];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKRIGHT);


th_group[0] = th_rcs[0];

th_group[1] = th_rcs[4];

th_group[2] = th_rcs[2];

th_group[3] = th_rcs[6];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_UP);


th_group[0] = th_rcs[1];

th_group[1] = th_rcs[5];

th_group[2] = th_rcs[3];

th_group[3] = th_rcs[7];

CreateThrusterGroup (th_group, 4, THGROUP_ATT_DOWN);


th_group[0] = th_rcs[8];

th_group[1] = th_rcs[11];

CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWLEFT);


th_group[0] = th_rcs[9];

th_group[1] = th_rcs[10];

CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWRIGHT);


th_group[0] = th_rcs[8];

th_group[1] = th_rcs[10];

CreateThrusterGroup (th_group, 2, THGROUP_ATT_LEFT);


th_group[0] = th_rcs[9];

th_group[1] = th_rcs[11];

CreateThrusterGroup (th_group, 2, THGROUP_ATT_RIGHT);


CreateThrusterGroup (th_rcs+12, 1, THGROUP_ATT_FORWARD);

CreateThrusterGroup (th_rcs+13, 1, THGROUP_ATT_BACK);



Done yet? Ok, just one more bit to go! Since we arn't defining any animations today, we are basically done. Let's just tell the module about our mesh file for the ship, and initialize the ship, then give it the ability to end. Paste and modify this into your .cpp:



AddMesh ("STARSHIP");

}


DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)

{

return new STARSHIP (hvessel, flightmodel);

}


DLLCLBK void ovcExit (VESSEL *vessel)

{

if (vessel) delete (STARSHIP*)vessel;

}


Now compile it!