Orbiter-Forum  

Go Back   Orbiter-Forum > Orbiter Space Flight Simulator > Tutorials & Challenges
Register Blogs Orbinauts List Social Groups FAQ Projects Mark Forums Read

Tutorials & Challenges Feel free to publish your tutorials, challenges, & flight scenarios in this forum.

Reply
 
Thread Tools
Old 01-03-2013, 05:57 PM   #16
DaveS
Addon Developer
 
DaveS's Avatar


Default

Quote:
Originally Posted by Hlynkacg View Post
 I just tested your code and yes that is very weird.

I do not know why the hatch would work but not the gear but I have a ideas for a work-around.
Seems like there's a bug with SetAnimation for the landing gear. Gear status doesn't seem to be passed to SetAnimation so the animation is set to its default state of 1.


Last edited by DaveS; 01-03-2013 at 06:01 PM. Reason: Added screenshot with modified debug string
DaveS is online now   Reply With Quote
Thanked by:
Old 01-03-2013, 09:58 PM   #17
Hlynkacg
Aspiring rocket scientist
 
Hlynkacg's Avatar


Default

Quote:
Originally Posted by DaveS View Post
 Seems like there's a bug with SetAnimation for the landing gear. Gear status doesn't seem to be passed to SetAnimation so the animation is set to its default state of 1.
I suspected that this was the case, but my initial idea for a workaround failed so now I'm experimenting.

For the moment I'm going to chalk it up as a "known issue" and start pushing forward on custom vessel states and staging.

---------- Post added at 13:50 ---------- Previous post was at 12:15 ----------

PART 8: Custom Functions and Sub-routines

This is a subject that often intimidates newbies but once you realise just how easy it is to do there really is no going back.

Thus far we have been overloading functions orbiter's core functions. These functions all existed in Orbiter prior to our calling them. Now it's time to start creating entirely new functions. We'll start with something easy.

Go to your class interface in your header file and add the following lines.

Code:
class LM: public VESSEL3 {
public:
	LM (OBJHANDLE hVessel, int flightmodel);
	~LM ();

	// Custom vessel functions
	void	DefineAnimations (void);			
	
	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
	void	clbkLoadStateEx (FILEHANDLE scn, void *status);					// Read status from scenario file
We have now declared a new (currently non-existant) function that will be unique to our vessel class.

It's keyword is "void" because, clbkConsumeBufferedKey, we don't expect or need it to return a value. It's input ( in perenthesis) is also "void" because it's instructions will not be dependant on any external function or variables.

Now that the function has been declared we need to define it. Let's go back to our source file and add a new section to it below our destructor but above our overloaded callback functions.

Code:
LM::~LM ()
{
}

// ==============================================================
// Custom Vessel Functions
// ==============================================================

// --------------------------------------------------------------
// Define meshgroup transformations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{

}

// ==============================================================
// Overloaded callback functions
// ==============================================================
Our function is now a "stub", meaning that it has been properly declared/defined but it doesn't do anything yet.

Let's change that. Go down and cut/paste all of our animation components from clbkPostCreation to our new animation function.

Code:
// --------------------------------------------------------------
// Define mesh animations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{
	// EVA Hatch animation
	static UINT	meshgroup_Hatch		= AS_GRP_EvaHatch;				// participating groups
	static UINT	meshgroup_Handle	= AS_GRP_HatchHandle;

	static MGROUP_ROTATE	mgt_Hatch (mesh_Ascent, &meshgroup_Hatch, 1, _V( 0.394,-0.578, 1.661), _V( 0.0, 1.0, 0.0), (float)-90*RAD); 
	static MGROUP_ROTATE	mgt_HatchHandle (mesh_Ascent, &meshgroup_Handle, 1, _V(-0.40279,-0.55598, 1.69760), _V( 0.0, 0.0, 1.0), (float) 90*RAD);

	anim_Hatch		= CreateAnimation (0.0);
	ach_Hatch		= AddAnimationComponent (anim_Hatch, 0.3f, 1.0f, &mgt_Hatch);
	ach_HatchHandle	= AddAnimationComponent (anim_Hatch, 0.0f, 0.2f, &mgt_HatchHandle, ach_Hatch);

	// Landing Gear Animation
	static UINT meshgroup_Legs[4][3] = {
		{DS_GRP_LandingFoot_FWD,	DS_GRP_ShockStrut_FWD,	DS_GRP_PrimaryStrut_FWD}, 
		{DS_GRP_LandingFoot_AFT,	DS_GRP_ShockStrut_AFT,	DS_GRP_PrimaryStrut_AFT},
		{DS_GRP_LandingFoot_PORT,	DS_GRP_ShockStrut_PORT,	DS_GRP_PrimaryStrut_PORT}, 
		{DS_GRP_LandingFoot_STBD,	DS_GRP_ShockStrut_STBD,	DS_GRP_PrimaryStrut_STBD}};
	static UINT meshgroup_Struts[4] = { DS_GRP_SecondaryStruts_FWD, DS_GRP_SecondaryStruts_AFT, DS_GRP_SecondaryStruts_PORT, DS_GRP_SecondaryStruts_STBD};
	static UINT meshgroup_Locks[4] = { DS_GRP_Downlock_FWD, DS_GRP_Downlock_AFT, DS_GRP_Downlock_PORT, DS_GRP_Downlock_STBD};
	static UINT meshgroup_Ladder = DS_GRP_Ladder;

	anim_Gear = CreateAnimation (1.0);

	for (int i = 0; i < 4; i++)
	{
		// Animation components
		mgt_Leg[i]		= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Legs[i][0], 3, LM_LegPivot[i], LM_LegAxis[i], (float) 45*RAD);		// Animate landing legs
		mgt_Strut[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Struts[i], 1, LM_StrutPivot[i], LM_LegAxis[i], (float)-63*RAD);		// Animate Support Struts attatched to legs
		mgt_Downlock[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Locks[i], 1, LM_DownlockPivot[i], LM_LegAxis[i], (float) 150*RAD);	// Animate Locking mechanism joining support struts to body

		// Add individual components to 'anim_Gear' 
		ach_GearLeg[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Leg[i]);
		ach_GearStrut[i]	= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Strut[i], ach_GearLeg[i]);
		ach_GearLock[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Downlock[i], ach_GearStrut[i]);
	}

	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);}
Then in clbkPostCreation add a call to "DefineAnimations"

Code:
// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	DefineAnimations ();

} // End "LM::clbkPostCreation"
Compile your code and test it.

Functionally speaking you shouldn't notice anything different but under the hood this represents a signifigant change. It may not seem like much now but this will save us huge amounts of time and effort down the line.

In fact, lets plan ahead and add a few more stubs to our source file...

Code:
	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);

} // End "LM::DefineAnimations"

// --------------------------------------------------------------
// Define local light sources
// --------------------------------------------------------------
void LM::DefineLighting (void)
{

} // End "LM::DefineLighting"

// --------------------------------------------------------------
// Define exhaust and particle streams
// --------------------------------------------------------------
void LM::DefineParticleStreams (void)
{

} // End "LM::DefineParticleStreams"

// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{

} // End "LM::DefineSounds"
Declare them in the header file the same way we declared "DefineAnimations" and add them to clbkPostCreation.

Code:
// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	DefineAnimations ();
	DefineLighting ();
	DefineParticleStreams ();
	DefineSounds ();

} // End "LM::clbkPostCreation"
Witness the power of this fully armed and operational object-oriented programming language!

The ability to break large blocks of our vessel's code into seperate sub-functions will be incredibly important moving forward.

---------- Post added at 13:58 ---------- Previous post was at 13:50 ----------

CURRENT STATE OF HEADER AND SOURCE

Header...
Code:
// ==============================================================
//                    ORBITER MODULE: LM
//                  Part of the ORBITER SDK
//               Copyright (C) 2012 Greg Hlynka
//                    All rights reserved
//
// LM.h
// Interface for LM vessel class
//
// Notes: This file exists
// ==============================================================

#define STRICT
#define ORBITER_MODULE

// Orbiter SDK files
#include "orbitersdk.h"

// Mesh resource files
#include "_ascentmesh.h"
#include "_descentmesh.h"
#include "_cockpitmesh.h"

// ==============================================================
// Vessel parameters
// ==============================================================

const VECTOR3	LM_ASC_OFFSET		= { 0.00, 1.44, 0.00};	// Offset of Ascent stage COG from combined COG
const VECTOR3	LM_DES_OFFSET		= { 0.00,-0.80, 0.00};	// Offset of Descent stage COG from combined COG
const double	LM_SIZE				= 5.5;					// Vessel's mean radius [m]
const double	LM_ASC_SIZE			= 2.5;					// Mean radius [m] after staging
const VECTOR3	LM_CS				= { 19.0, 19.2, 18.8};	// x,y,z cross sections [m^2]
const VECTOR3	LM_ASC_CS			= { 9.62, 12.6, 9.25};	// Cross sections [m^2] after staging
const VECTOR3	LM_PMI				= { 2.68, 2.10, 2.64};	// Principal moments of inertia (mass-normalised) [m^2]
const VECTOR3	LM_ASC_PMI			= {	1.31, 1.68, 1.25};	// Principal moments of inertia (mass-normalised) [m^2] after staging
const VECTOR3	LM_RD				= {	0.04, 0.05, 0.04};	// Rotation drag coefficients
const VECTOR3	LM_ASC_RD			= {	0.03, 0.07, 0.09};	// Rotation drag coefficients after staging
const double	LM_ASC_EMPTYMASS	= 1922.78;				// Ascent stage empty mass [kg] (4239 lbs, page 119)
const double	LM_DES_EMPTYMASS	= 1798.95;				// Descent stage empty mass [kg] (3,966 lbs, page 119)
const double	LM_ASC_FUELMASS		= 2353.0;				// Ascent stage fuel mass [kg] 
const double	LM_DES_FUELMASS		= 8480.81;				// Descent stage fuel mass [kg] (18,697 lbs, LMFM pg 58)
const double	LM_RCS_FUELMASS		= 750.0;				// RCS fuel mass [kg] (633 lbs, LMFM pg 72)
const double	LM_ASC_WATERMASS	= 19.55;				// max fuel mass [kg] (page 177)
const double	LM_DES_WATERMASS	= 135.64;				// Descent stage water storage mass [kg] (299 lbs, page 177)
const double	LM_ASC_O2MASS		= 1.102;				// max fuel mass [kg] (2.43 lbs)
const double	LM_DES_O2MASS		= 20.50;				// Descent stage O2 reserve mass [kg] (45.2 lbs)
const double	LM_ASC_ISP			= 3050.91;				// Ascent stage specific impulse [m/s] (311 sec, Astronautix.com)
const double	LM_DES_ISP			= 3075.44;				// Descent stage specific impulse [m/s] (313.5 sec, Astronautix.com)
const double	LM_RCS_ISP			= 2844.90;				// RCS specific impulse [m/s] (290 sec, Astronautix.com)
const VECTOR3	LM_TDP[3]			= {{ 0.0,-3.1, 4.4}, {-3.8,-3.1,-2.2}, { 3.8,-3.1,-2.2}};	// touchdown points [m]
const VECTOR3	LM_ASC_TDP[3]		= {{ 0.0,-1.4, 1.7}, {-0.9,-1.4,-1.5}, { 0.9,-1.4,-1.5}};	// touchdown points [m] after staging
const double	LM_ASC_MAXTHRUST	= 15568.8;				// Ascent stage max thrust [n] (3,500 lbf, LMFM pg 58)
const double	LM_DES_MAXTHRUST	= 44482.2;				// Descent stage max thrust	[n] (10,000 lbf, LMFM pg 58)
const double	LM_RCS_MAXTHRUST	= 440.0;				// RCS thruster max thrust [n] (100 lbf)
const VECTOR3	LM_ASC_ENGINEPOS	= {	0.00,-0.64, 0.00};	// Ascent stage engine position [m]
const VECTOR3	LM_DES_ENGINEPOS	= { 0.00,-1.18, 0.00};	// Descent stage  engine position [m]
const VECTOR3	LM_RCS_ENGINEPOS[8]	= {{-1.68, 1.44, 1.68}, {-1.562, 1.44, 1.562}, {-1.68, 1.44,-1.68}, {-1.562, 1.44,-1.562}, { 1.68, 1.44,-1.68}, { 1.562, 1.44,-1.562}, { 1.68, 1.44, 1.68}, { 1.562, 1.44, 1.562}}; // RCS engine positions [m]

const VECTOR3	LM_DOCK_POS			= { 0.00, 2.93, 0.00};	// docking port location [m]
const VECTOR3	LM_DOCK_DIR			= { 0, 1, 0};			// docking port approach direction
const VECTOR3	LM_DOCK_ROT			= { 0, 0, 1};			// docking port alignment direction

// ==============================================================
// Lunar Module class interface
// ==============================================================

class LM: public VESSEL3 {
public:
	LM (OBJHANDLE hVessel, int flightmodel);
	~LM ();

	// Custom vessel functions
	void	DefineAnimations (void);			// Define mesh animations
	void	DefineLighting (void);				// Define local light sources
	void	DefineParticleStreams (void);		// Define exhaust and particle streams
	void	DefineSounds (void);				// Define sound effects

	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
	void	clbkLoadStateEx (FILEHANDLE scn, void *status);					// Read status from scenario file
	void	clbkSaveState (FILEHANDLE scn);									// Write status to scenario file
	void	clbkPostCreation ();											// Finalise vessel creation
	void	clbkPostStep (double simt, double simdt, double mjd);			// Manage Animations and Post-Step processes
	int		clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);	// Process keyboard inputs

	// Thruster and Propellant handles
	PROPELLANT_HANDLE	ph_rcsA, ph_rcsB, ph_ascentFuel, ph_descentFuel;			// Functional Propellant tanks
	PROPELLANT_HANDLE	ph_ascentO2, ph_ascentH2O, ph_descentO2, ph_descentH2O;		// Represenative propellant tanks (consumables)
	
	THRUSTER_HANDLE		th_rcsA[8], th_rcsB[8], th_ascent, th_descent;				// Functional thrusters
	THRUSTER_HANDLE		th_dust, th_vent;											// Represenative thrusters (particle streams)

private:
	// Vessel status variables
	enum	vesselstate	{LAUNCH, DESCENT, ASCENT, WRECKED}	VesselStatus;	
	enum	doorstate	{CLOSED, OPEN, CLOSING, OPENING}	HatchStatus, GearStatus;
	
	double	thrust_mod, dv_mod, consumables_mod;

	// Animations
	UINT						anim_Hatch, anim_Gear;
	double						hatch_proc, gear_proc;
	ANIMATIONCOMPONENT_HANDLE	ach_GearLeg[4], ach_GearStrut[4], ach_GearLock[4], ach_Hatch, ach_HatchHandle;
	MGROUP_TRANSFORM			*mgt_Leg[4], *mgt_Strut[4], *mgt_Downlock[4];
	
	// Meshes
	MESHHANDLE	mh_descent, mh_ascent;	// Mesh handles
	UINT		mesh_Descent;			// Descent stage mesh index
	UINT		mesh_Ascent;			// Ascent stage mesh index

}; // End "class LM:"
Source...
Code:
// ==============================================================
//                    ORBITER MODULE: LM
//                  Part of the ORBITER SDK
//               Copyright (C) 2012 Greg Hlynka
//                    All rights reserved
//
// LM.cpp
// Control module for LM vessel class
//
// Notes: Writing Tutorials is hard work
// ==============================================================

#include "LM.h"

// ==============================================================
// Class Constructor and Destructor
// ==============================================================

LM::LM (OBJHANDLE hVessel, int flightmodel)
: VESSEL3 (hVessel, flightmodel)
{
	// Config file difficulty modifiers (1.0 = 100% of default value)
	thrust_mod		= 1.0;
	dv_mod			= 1.0;
	consumables_mod = 1.0;

	// Initial vessel state
	VesselStatus	= DESCENT;

	// Load Exterior meshes
	mh_descent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_DescentStage");
	mh_ascent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_AscentStage");
}

LM::~LM ()
{
}

// ==============================================================
// Custom Vessel Functions
// ==============================================================

// --------------------------------------------------------------
// Define mesh animations
// --------------------------------------------------------------
void LM::DefineAnimations (void)
{
	// EVA Hatch animation
	static UINT	meshgroup_Hatch		= AS_GRP_EvaHatch;				// participating groups
	static UINT	meshgroup_Handle	= AS_GRP_HatchHandle;

	static MGROUP_ROTATE	mgt_Hatch (mesh_Ascent, &meshgroup_Hatch, 1, _V( 0.394,-0.578, 1.661), _V( 0.0, 1.0, 0.0), (float)-90*RAD); 
	static MGROUP_ROTATE	mgt_HatchHandle (mesh_Ascent, &meshgroup_Handle, 1, _V(-0.40279,-0.55598, 1.69760), _V( 0.0, 0.0, 1.0), (float) 90*RAD);

	anim_Hatch		= CreateAnimation (0.0);
	ach_Hatch		= AddAnimationComponent (anim_Hatch, 0.3f, 1.0f, &mgt_Hatch);
	ach_HatchHandle	= AddAnimationComponent (anim_Hatch, 0.0f, 0.2f, &mgt_HatchHandle, ach_Hatch);

	// Landing Gear Animation
	static UINT meshgroup_Legs[4][3] = {
		{DS_GRP_LandingFoot_FWD,	DS_GRP_ShockStrut_FWD,	DS_GRP_PrimaryStrut_FWD}, 
		{DS_GRP_LandingFoot_AFT,	DS_GRP_ShockStrut_AFT,	DS_GRP_PrimaryStrut_AFT},
		{DS_GRP_LandingFoot_PORT,	DS_GRP_ShockStrut_PORT,	DS_GRP_PrimaryStrut_PORT}, 
		{DS_GRP_LandingFoot_STBD,	DS_GRP_ShockStrut_STBD,	DS_GRP_PrimaryStrut_STBD}};
	static UINT meshgroup_Struts[4] = { DS_GRP_SecondaryStruts_FWD, DS_GRP_SecondaryStruts_AFT, DS_GRP_SecondaryStruts_PORT, DS_GRP_SecondaryStruts_STBD};
	static UINT meshgroup_Locks[4] = { DS_GRP_Downlock_FWD, DS_GRP_Downlock_AFT, DS_GRP_Downlock_PORT, DS_GRP_Downlock_STBD};
	static UINT meshgroup_Ladder = DS_GRP_Ladder;

	anim_Gear = CreateAnimation (1.0);

	for (int i = 0; i < 4; i++)
	{
		// Animation components
		mgt_Leg[i]		= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Legs[i][0], 3, LM_LegPivot[i], LM_LegAxis[i], (float) 45*RAD);		// Animate landing legs
		mgt_Strut[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Struts[i], 1, LM_StrutPivot[i], LM_LegAxis[i], (float)-63*RAD);		// Animate Support Struts attatched to legs
		mgt_Downlock[i]	= new MGROUP_ROTATE (mesh_Descent, &meshgroup_Locks[i], 1, LM_DownlockPivot[i], LM_LegAxis[i], (float) 150*RAD);	// Animate Locking mechanism joining support struts to body

		// Add individual components to 'anim_Gear' 
		ach_GearLeg[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Leg[i]);
		ach_GearStrut[i]	= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Strut[i], ach_GearLeg[i]);
		ach_GearLock[i]		= AddAnimationComponent (anim_Gear, 0.0, 1.0, mgt_Downlock[i], ach_GearStrut[i]);
	}

	static MGROUP_ROTATE mgt_Ladder (mesh_Descent, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0.0, 1, &mgt_Ladder);

} // End "LM::DefineAnimations"

// --------------------------------------------------------------
// Define local light sources
// --------------------------------------------------------------
void LM::DefineLighting (void)
{

} // End "LM::DefineLighting"

// --------------------------------------------------------------
// Define exhaust and particle streams
// --------------------------------------------------------------
void LM::DefineParticleStreams (void)
{

} // End "LM::DefineParticleStreams"

// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{

} // End "LM::DefineSounds"

// ==============================================================
// Overloaded callback functions
// ==============================================================

// --------------------------------------------------------------
// Set the capabilities of the vessel class
// --------------------------------------------------------------
void LM::clbkSetClassCaps (FILEHANDLE cfg)
{
	// Read dificulty modifiers from config file
	oapiReadItem_float (cfg, "THRUST_MODIFIER", thrust_mod);			// Max thrust multiplier 
	oapiReadItem_float (cfg, "DV_MODIFIER", dv_mod);					// Exhaust velocity multiplier
	oapiReadItem_float (cfg, "CONSUMABLES_MODIFIER", consumables_mod);	// Consumables consumption rate (1 = 100% of historical rate)

	// Physical vessel parameters
	SetSize (LM_SIZE);
	SetEmptyMass (LM_ASC_EMPTYMASS + LM_DES_EMPTYMASS);
	SetPMI (LM_PMI);
	SetCrossSections (LM_CS);
	SetRotDrag (LM_RD);
	SetTouchdownPoints (LM_TDP[0], LM_TDP[1], LM_TDP[2]);

	// Docking port definition
	SetDockParams (LM_DOCK_POS, LM_DOCK_DIR, LM_DOCK_ROT);
	
	// Attachment point definition
	CreateAttachment (true, _V( 0.0, 0.4, 0.0), _V( 0, 1, 0), _V( 0, 0, 1), "X");	// for attachment to launch vehicle

	// Propellant resources
	ph_rcsA			= CreatePropellantResource (LM_RCS_FUELMASS/2);		// RCS propellant tank 'A'
	ph_rcsB			= CreatePropellantResource (LM_RCS_FUELMASS/2);		// RCS propellant tank 'B'
	ph_ascentFuel	= CreatePropellantResource (LM_ASC_FUELMASS);		// Ascent stage propellant tank
	ph_ascentO2		= CreatePropellantResource (LM_ASC_O2MASS);			// Ascent stage O2 tank
	ph_ascentH2O	= CreatePropellantResource (LM_ASC_WATERMASS);		// Ascent stage water tank

	ph_descentFuel	= CreatePropellantResource (LM_DES_FUELMASS);		// Descent stage propellant tank
	ph_descentO2	= CreatePropellantResource (LM_DES_O2MASS);			// Descent stage O2 tank
	ph_descentH2O	= CreatePropellantResource (LM_DES_WATERMASS);		// Descent stage water tank

	// RCS engines: Quadrant 1 (Port Fwd)
	th_rcsA[0]	= CreateThruster (LM_RCS_ENGINEPOS[0], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Down
	th_rcsA[1]	= CreateThruster (LM_RCS_ENGINEPOS[1], _V( 0, 0,-1), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Back
	th_rcsB[0]	= CreateThruster (LM_RCS_ENGINEPOS[0], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Up
	th_rcsB[1]	= CreateThruster (LM_RCS_ENGINEPOS[1], _V( 1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Right

	// RCS engines: Quadrant 2 (Port Aft)
	th_rcsA[2]	= CreateThruster (LM_RCS_ENGINEPOS[2], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Up
	th_rcsA[3]	= CreateThruster (LM_RCS_ENGINEPOS[3], _V( 0, 0, 1), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Fwd
	th_rcsB[2]	= CreateThruster (LM_RCS_ENGINEPOS[2], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Down 
	th_rcsB[3]	= CreateThruster (LM_RCS_ENGINEPOS[3], _V( 1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Right

	// RCS engines: Quadrant 3 (Stbd Aft)
	th_rcsA[4]	= CreateThruster (LM_RCS_ENGINEPOS[4], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Down
	th_rcsA[5]	= CreateThruster (LM_RCS_ENGINEPOS[5], _V(-1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Left
	th_rcsB[4]	= CreateThruster (LM_RCS_ENGINEPOS[4], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Up
	th_rcsB[5]	= CreateThruster (LM_RCS_ENGINEPOS[5], _V( 0, 0, 1), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Fwd

	// RCS engines: Quadrant 4 (Stbd Fwd)
	th_rcsA[6]	= CreateThruster (LM_RCS_ENGINEPOS[6], _V( 0, 1, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Up
	th_rcsA[7]	= CreateThruster (LM_RCS_ENGINEPOS[7], _V(-1, 0, 0), LM_RCS_MAXTHRUST, ph_rcsA, LM_RCS_ISP*dv_mod); // Left
	th_rcsB[6]	= CreateThruster (LM_RCS_ENGINEPOS[6], _V( 0,-1, 0), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Down
	th_rcsB[7]	= CreateThruster (LM_RCS_ENGINEPOS[7], _V( 0, 0,-1), LM_RCS_MAXTHRUST, ph_rcsB, LM_RCS_ISP*dv_mod); // Back

	// Ascent engine
	th_ascent	= CreateThruster (LM_ASC_ENGINEPOS, _V( 0, 1, 0), LM_ASC_MAXTHRUST*thrust_mod, ph_ascentFuel, LM_ASC_ISP*dv_mod);

	// Descent engine
	th_descent	= CreateThruster (LM_DES_ENGINEPOS, _V( 0, 1, 0), LM_DES_MAXTHRUST*thrust_mod, ph_descentFuel, LM_DES_ISP*dv_mod);	
	
	// Camera parameters
	SetCameraOffset (_V( 0.0, 2.1, 0.0));

	// Associate meshes for the visual
	mesh_Descent	= AddMesh (mh_descent, &LM_DES_OFFSET);	// Descent stage mesh
	mesh_Ascent		= AddMesh (mh_ascent, &LM_ASC_OFFSET);	// Ascent stage mesh

	SetMeshVisibilityMode (mesh_Descent, MESHVIS_ALWAYS);	// I want the the descent stage's legs to be visible from the cockpit, so I've set the mesh_Descent to be always visible. By default a vessel's own meshes are only visible when in an external view.
} // End "LM::clbkSetClassCaps"

// --------------------------------------------------------------
// Read status from scenario file
// --------------------------------------------------------------
void LM::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	int i = 0;
	char *cbuf; 

	while (oapiReadScenario_nextline (scn, cbuf)) 
	{
		// Load Vessel State
		if (!_strnicmp( cbuf, "STATE", 5))  // find line labeled "STATE" in scn file
		{
			sscanf(cbuf+5, "%i", &VesselStatus);	// Read value stored there to VesselStatus
		}

		// Load animation states
        else if (!_strnicmp (cbuf, "HATCH", 5)) // find line labeled "HATCH" in scn file
		{
			sscanf (cbuf+5, "%i %lf", &HatchStatus, &hatch_proc);	// Read values stored there to HatchStatus and hatch_proc
			SetAnimation (anim_Hatch, hatch_proc);					// Apply process value to animation.
		}		
		else if (!_strnicmp (cbuf, "GEAR", 4)) // find line labeled "GEAR" in scn file
		{
			sscanf (cbuf+4, "%i %lf", &GearStatus, &gear_proc);		// Read values stored there to GearStatus and gear_proc
			SetAnimation (anim_Gear, gear_proc);					// Apply process value to animation.
		}

		// Load default parameters
		else ParseScenarioLineEx (cbuf, status);	

	} // End "while (oapiReadScenario_nextline (scn, cbuf))"

} // End "LM::clbkLoadStateEx"

// --------------------------------------------------------------
// Write status to scenario file
// --------------------------------------------------------------
void LM::clbkSaveState (FILEHANDLE scn)
{
	int i = 0;
	char cbuf[256];
	
	// Write default vessel parameters to scenario file
	VESSEL3::clbkSaveState (scn);

	// Write custom parameters to scenario file
	sprintf (cbuf, "%i", VesselStatus);						// Vessel status
	oapiWriteScenario_string (scn, "STATE", cbuf);

	sprintf (cbuf, "%i %0.4f", HatchStatus, hatch_proc);	// EVA Hatch status and animation state
	oapiWriteScenario_string (scn, "HATCH", cbuf);

	sprintf (cbuf, "%i %0.4f", GearStatus, gear_proc);		// Landing Gear status and animation state
	oapiWriteScenario_string (scn, "GEAR", cbuf);

} // End "LM::clbkSaveState"

// --------------------------------------------------------------
// Finalise vessel creation
// --------------------------------------------------------------
void LM::clbkPostCreation ()
{
	DefineAnimations ();
	DefineLighting ();
	DefineParticleStreams ();
	DefineSounds ();

} // End "LM::clbkPostCreation"

// --------------------------------------------------------------
// Manage Animations and Post-Step processes
// --------------------------------------------------------------
void LM::clbkPostStep (double simt, double simdt, double mjd)
{
	// Debuggery
	//sprintf (oapiDebugString(), "Vessel Status %0.0f", (float)VesselStatus);

	// EVA hatch control logic
	if (hatch_proc < 0)			// If process value is less than 0...
	{
		HatchStatus = CLOSED;	// ...set status to "CLOSED"
		hatch_proc = 0;			// and process value to 0
	}

	else if (hatch_proc > 1)	// If process value is greater than 1...
	{
		HatchStatus = OPEN;		// ...set status to "OPEN"
		hatch_proc = 1;			// and process value to 1
	}

	if (HatchStatus > CLOSED)	
	{
		double	delta = simdt / 3;	

		if (HatchStatus == OPENING)				// if Status equals "OPENING"...
		{
			hatch_proc += delta;				// ...add delta to process value
		}
		
		if (HatchStatus == CLOSING)				// if Status equals "CLOSING"...
		{
			hatch_proc -= delta;				// ...subtract it.
		}

		SetAnimation (anim_Hatch, hatch_proc);	// Apply process value to animation.
	}

	// Debuggery
	//sprintf (oapiDebugString (), "Hatch Status %0.0f, hatch_proc %0.3f", (float)HatchStatus, hatch_proc);

	// Landing Gear control logic
	if (gear_proc < 0)			// If process value is less than 0...
	{
		GearStatus = CLOSED;	// ...set status to "CLOSED"
		gear_proc = 0;			// and process value to 0
	}

	else if (gear_proc > 1)		// If process value is greater than 1...
	{
		GearStatus = OPEN;		// ...set status to "OPEN"
		gear_proc = 1;			// and process value to 1
	}

	if (GearStatus > CLOSED)	
	{
		double	delta = simdt / 0.3;	// delta = simdt divided by duration of animation in seconds

		if (GearStatus == OPENING)		// if Status equals "OPENING"...
		{
			gear_proc += delta;			// ...add delta to process value
		}
		
		if (GearStatus == CLOSING)		// if Status equals "CLOSING"...
		{
			gear_proc -= delta;			// ...subtract it.
		}

		SetAnimation( anim_Gear, gear_proc);	// apply process value to animation
	}

	// Debuggery
	sprintf (oapiDebugString (), "Gear Status %0.0f, gear_proc %0.3f", (float)GearStatus, gear_proc);

} // End "LM::clbkPostStep"

// --------------------------------------------------------------
// Process keyboard inputs
// --------------------------------------------------------------
int  LM::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{
	// Open hatch when [K] is pressed.
	if (key == OAPI_KEY_K  && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && !KEYMOD_ALT(kstate)) // [K] is down, no [shift], no [ctrl], no [alt]
	{
		if (HatchStatus == CLOSED) HatchStatus = OPENING;	// If the hatch is closed, open it
		else HatchStatus = CLOSING;							// If not, close it 
		return 1;											// return '1' to indicate that the input has been processed
	}

	// Deploy Ganding Gear when [G] is pressed.
	if (key == OAPI_KEY_G  && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && !KEYMOD_ALT(kstate)) // [K] is down, no [shift], no [ctrl], no [alt]
	{
		if (GearStatus != OPEN) GearStatus = OPENING;		// If landing gear have not been deployed, deploy them
		return 1;
	}

	return 0;	// if no keys are pressed the function returns '0' and nothing happens

} // End "LM::clbkConsumeBufferedKey"

// ==============================================================
// API callback interface
// ==============================================================

// --------------------------------------------------------------
// Vessel initialisation
// --------------------------------------------------------------
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new LM (hvessel, flightmodel);
}

// --------------------------------------------------------------
// Vessel cleanup
// --------------------------------------------------------------
DLLCLBK void ovcExit (VESSEL *vessel)
{
	if (vessel) delete (LM*)vessel;
}
NOTE: VesselStatus enumerator will be subject of part 10.

Last edited by Hlynkacg; 01-03-2013 at 08:17 PM.
Hlynkacg is offline   Reply With Quote
Thanked by:
Old 01-05-2013, 01:12 AM   #18
Hlynkacg
Aspiring rocket scientist
 
Hlynkacg's Avatar


Default A note before we proceed...

Ordinarily Orbiter does not track a vessel's physical capabilities and attributes on a frame-by-frame basis. It simply takes those declared in clbkSetClassCaps and applies them.

Because our vessel's configuration is going to change over the course of the simulation (most notably at stage seperation) we need to write a function (or rather a set of functions) that will track and update our vessel's attributes. To facilitate this I have added an additional enumerator to our class interface describing our lander's major phases of flight.

Code:
enum	vesselstate	{LAUNCH, DESCENT, ASCENT, WRECKED}	VesselStatus;
The "LAUNCH" phase is essentially a "safe mode" meant to be used when the lander is powered down and attached to it's booster/SLA. "DESCENT" will be our normal/default flight configuration. And the ASCENT will be our configuration after the Descent Stage has been jettisoned.

I also added a fourth element, "WRECKED", as a placeholder should I decide to add a damage/destruction model to the vessel somewhere down the line.

While in essance the Lunar Lander is a simple 2 stage rocket (that has the slight quirk of staging while landed) the basic principals behind the functions that we are about to discuss can just as easily be applied to something more complex, such as a launch vehicle.

For instance, if I were to be coding a Saturn 5 my enumerator might look something like this...

Code:
enum	boosterstate	{ONPAD, STAGE1BURN, STAGE2BURN, STAGE3BURN, INORBIT}	BoosterStatus;


---------- Post added at 15:54 ---------- Previous post was at 14:23 ----------

PART 9: Custom Vessel States
First things first. Let's add the VesselStatus enumerator to our class interface and initialize it in the constructor.

Code:
private:
	// Vessel status variables
	enum	vesselstate	{LAUNCH, DESCENT, ASCENT, WRECKED}	VesselStatus;	
	enum	doorstate	{CLOSED, OPEN, CLOSING, OPENING}	HatchStatus, GearStatus;
Code:
LM::LM (OBJHANDLE hVessel, int flightmodel)
: VESSEL3 (hVessel, flightmodel)
{
	// Config file difficulty modifiers (1.0 = 100% of default value)
	thrust_mod		= 1.0;
	dv_mod			= 1.0;
	consumables_mod = 1.0;

	// Initial vessel state
	VesselStatus	= DESCENT;
Intializing it in this manner makes "DESCENT" the default starting state, so that, in the absence of other instructions, orbiter will spawn our lander with the decent stage attached and ready for "normal" flight.

Let's also add a VesselStatus line to our clbkLoadState and clbkSaveState functions.

Code:
	while (oapiReadScenario_nextline (scn, cbuf)) 
	{
		// Load Vessel State
		if (!_strnicmp( cbuf, "STATE", 5))  // find line labeled "STATE" in scn file
		{
			sscanf(cbuf+5, "%i", &VesselStatus);	// Read value stored there to VesselStatus
		}

		// Load animation states
        else if (!_strnicmp (cbuf, "HATCH", 5)) // find line labeled "HATCH" in scn file
		{
Code:
	// Write default vessel parameters to scenario file
	VESSEL3::clbkSaveState (scn);

	// Write custom parameters to scenario file
	sprintf (cbuf, "%i", VesselStatus);						// Vessel status
	oapiWriteScenario_string (scn, "STATE", cbuf);

	sprintf (cbuf, "%i %0.4f", HatchStatus, hatch_proc);	// EVA Hatch status and animation state
	oapiWriteScenario_string (scn, "HATCH", cbuf);
With this little bit of house-keeping out of the way, lets go back to our class interface and declare a new custom function.

The first parameter that we are going to be tracking/updating is our vessel's empty mass so let's call our function "CalcEmptyMass".

Code:
	// Custom vessel functions
	void	DefineAnimations (void);			// Define mesh animations
	void	DefineLighting (void);				// Define local light sources
	void	DefineParticleStreams (void);		// Define exhaust and particle streams
	void	DefineSounds (void);				// Define sound effects
	double	CalcEmptyMass (void);				// Calculate current empty mass of vessel
Note that unlike our previous functions we will be wanting "CalcEmptyMass" to return a value. As such we must assign it a data-type. Orbiter uses "double" for mass calculation therefore that is the data-type that we will use.

Now that the function has been declared let's go to the source file and define it.

Code:
// --------------------------------------------------------------
// Calculate current empty mass of vessel
// --------------------------------------------------------------
double LM::CalcEmptyMass (void)
{
	double output;

	if (VesselStatus == ASCENT) output = LM_ASC_EMPTYMASS;	// If vessel is in ascent configuration output equals ascent stage empty mass. 
	else output = LM_ASC_EMPTYMASS + LM_DES_EMPTYMASS;		// If not, output equals the empty masses of the ascent and descent stages combined. (Default empty mass)

	// Add mass of crew	**not implimented**
	// Add mass of cargo	**not implimented**

	return output;	// Return total
}
The function really is quite simple. If the lander is in ascent configuration our output value should be equal to the ascent stage's empty mass. If not it should be equal to the ascent stage's empty mass plus that of the descent stage.

NOTE: Orbiter counts anything that isn't in a fuel tank as "Empty Mass". Our vessel currently has no crew or cargo management, but if/when they are added they will need to accounted for in this calculation as well, hense the two place holders.

Now that we have a function where do we call it? Ideally we want orbiter to check/update our vessel's physical state at the beginning of each time step (before any forces are applied) and it just so happens that there is a call back function for doing exactly that. It is called "clbkPreStep".

Declare it in your class interface...

Code:
	void	clbkPostCreation ();											// Finalise vessel creation
	void	clbkPreStep (double simt, double simdt, double mjd);			// Pre-Step processes
	void	clbkPostStep (double simt, double simdt, double mjd);			// Post-Step processes
And overload it...

Code:
} // End "LM::clbkPostCreation"

// --------------------------------------------------------------
// Pre-Step processes
// --------------------------------------------------------------
void LM::clbkPreStep (double simt, double simdt, double mjd)
{
	// Update Vessel State
	SetEmptyMass (CalcEmptyMass ());

	// Debuggery
	sprintf (oapiDebugString(), "Vessel Status %0.0f, Empty Mass %0.3f", (float)VesselStatus, GetMass());
} // End "LM::clbkPreStep"

// --------------------------------------------------------------
// Post-Step processes
// --------------------------------------------------------------
void LM::clbkPostStep (double simt, double simdt, double mjd)
{
The meaning of "SetEmptyMass (CalcEmptyMass ());" should be obvious. We are telling orbiter to set our vessel's empty mass to the value returned by our "CalcEmptyMass ()" function.

Compile and test...

Everything should function normally but if you go into the scenario file and change your vessel's status to "ASCENT" (STATE 2) you should note a change in your vessel's mass.

Congratulations, we are on our way.

---------- Post added at 17:12 ---------- Previous post was at 15:54 ----------

PART 9: Custom Vessel States (Continued)

Now to control the rest of or vessel's parameters I am going to introduce a new type of statement called a "switch". Switches are broken into a set set of "cases" that function similar to a set of nested "if" functions.

If not "case 1" then "case 2", if not "case 2" then "case 3", and so on down the line. Specific outputs or functions can then be assigned to a given case.

The following is an example of a simple switch statement
Code:
switch (x)
{
 case x < 0:
  **x is a negative number**
  break;

 case x = 0:
  **x is equal to 0**
  break;

 case x > 0:
  **x is a positive number**
  break;
}
NOTE: cases must be unambiguous (no overlap).

What we are going to do is create a switch that takes "VesselStatus" as it's input and then uses the individual states as it's cases. Go to clbkPreStep and add the following.

Code:
	// Update Vessel State
	SetEmptyMass (CalcEmptyMass ());

	switch (VesselStatus)
	{
	case LAUNCH:
		
		break;

	case DESCENT:

		break;

	case ASCENT:

		break;
	} // End "switch (VesselStatus)"
So far so good. Now let's add some simple instructions...

Code:
	switch (VesselStatus)
	{
	case LAUNCH:
		GearStatus = CLOSED;
		gear_proc = 0;
		SetAnimation( anim_Gear, 0);
		break;

	case DESCENT:
		SetDefaultPropellantResource (ph_descentFuel);	// Set descent stage fuel tank as default propellant resource
		break;

	case ASCENT:
		SetDefaultPropellantResource (ph_ascentFuel);	// Set ascent stage fuel tank as default propellant resource
		break;
	} // End "switch (VesselStatus)"
I think you should be able to see where this is all going.

If we are in launch config we want to make sure that the gear are retracted (and thus not sticking out the sides of the fairing ).

If we are in descent config lets make sure that we're using the correct fuel tank

If we are in ascent config do likewise.

We can add as many instructions to each case as we want, have fun with it.

NOTE: If your switch function gets too crowded or unwieldly you may want to break the instructions for each case off into thier own seperate sub-functions so that your switch looks something like this...

Code:
	switch (VesselStatus)
	{
	case LAUNCH:
		ApplyLaunchConfig ();
		break;
	case DESCENT:
		ApplyDescentConfig ();
		break;
	case ASCENT:
		ApplyAscentConfig ();
		break;
	}
Now obviously those functions will need to be properly declared and defined but at this point this should pose no obstical to you.

Last edited by Hlynkacg; 01-05-2013 at 12:05 AM.
Hlynkacg is offline   Reply With Quote
Old 01-06-2013, 03:35 AM   #19
Hlynkacg
Aspiring rocket scientist
 
Hlynkacg's Avatar


Default

It occurs to me that if someone is following along step by step the lander isn't really flyable at this point in the tutorial due to the fact that I have not defined any thruster groups.

This is because I have plans for a custom thrust control system that will be implimented after the cockpit (or rather the required displays and switches) have been added.

For the sake of testing, or if you simply decide that you want to forgo advanced thruster modeling all together, add the following to either "clbkPostCreation" or to the bottom of "clbkSetClassCaps"

Code:
	// TEMPORARY THRUST CONTROL CONTROL GROUPS
	THRUSTER_HANDLE	th_group[4];

	// RCS control groups (Rotation)
	th_group[0]	= th_rcsA[4];
	th_group[1] = th_rcsA[6];
	th_group[2] = th_rcsB[0];
	th_group[3] = th_rcsB[2];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHUP);

	th_group[0] = th_rcsA[0];
	th_group[1] = th_rcsA[2];
	th_group[2] = th_rcsB[4];
	th_group[3] = th_rcsB[6];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_PITCHDOWN);

	th_group[0] = th_rcsA[0];
	th_group[1] = th_rcsA[6];
	th_group[2] = th_rcsB[2];
	th_group[3] = th_rcsB[4];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKLEFT); 

	th_group[0] = th_rcsA[2];
	th_group[1] = th_rcsA[4];
	th_group[2] = th_rcsB[0];
	th_group[3] = th_rcsB[6];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_BANKRIGHT);

	th_group[0] = th_rcsA[5];
	th_group[1] = th_rcsB[1];
	//th_group[0] = th_rcsA[3];
	//th_group[1] = th_rcsB[7];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWRIGHT);

	th_group[0] = th_rcsA[7];
	th_group[1] = th_rcsB[3];
	//th_group[0] = th_rcsA[1];
	//th_group[1] = th_rcsB[5];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_YAWLEFT);

	// RCS control groups (Translation)
	th_group[0] = th_rcsA[2];
	th_group[1] = th_rcsA[6];
	th_group[2] = th_rcsB[0];
	th_group[3] = th_rcsB[4];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_UP);

	th_group[0] = th_rcsA[0];
	th_group[1] = th_rcsA[4];
	th_group[2] = th_rcsB[2];
	th_group[3] = th_rcsB[6];
	CreateThrusterGroup (th_group, 4, THGROUP_ATT_DOWN);

	th_group[0] = th_rcsB[1];
	th_group[1] = th_rcsB[3];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_RIGHT);

	th_group[0] = th_rcsA[5];
	th_group[1] = th_rcsA[7];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_LEFT);

	th_group[0] = th_rcsA[3];
	th_group[1] = th_rcsB[5];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_FORWARD);

	th_group[0] = th_rcsA[1];
	th_group[1] = th_rcsB[7];
	CreateThrusterGroup (th_group, 2, THGROUP_ATT_BACK);
	// END TEMPORARY THRUSTER GROUPS
Hlynkacg is offline   Reply With Quote
Thanked by:
Old 01-07-2013, 09:28 PM   #20
Hlynkacg
Aspiring rocket scientist
 
Hlynkacg's Avatar


Default

So now that we've got the basics of vessel states down it's time to nodel the actual staging.

But before we do, lets take a look at what exactly I've put in my vessel states...

Code:
// --------------------------------------------------------------
// Pre-Step processes
// --------------------------------------------------------------
void LM::clbkPreStep (double simt, double simdt, double mjd)
{
	// Update Vessel State
	SetEmptyMass (CalcEmptyMass ());

	switch (VesselStatus)
	{
	// Update Vessel State
	SetEmptyMass (CalcEmptyMass ());

	switch (VesselStatus)
	{
	case LAUNCH: // If vessel is in Launch config...
		// Fold and stow landing gear
		GearStatus = CLOSED;
		gear_proc = 0;
		SetAnimation (anim_Gear, 0);

		// Disable engines
		SetThrusterLevel (th_descent, 0);	
		SetThrusterLevel (th_ascent, 0);
		break;

	case DESCENT: // If vessel is in Descent (normal flight) config...
		SetDefaultPropellantResource (ph_descentFuel);			// Set descent stage fuel tank as default propellant resource
		CreateThrusterGroup (&th_descent, 1, THGROUP_HOVER);	// Assign descent stage engine to THGROUP_HOVER
		SetThrusterLevel (th_ascent, 0);						// Disable ascent stage engine
		break;

	case ASCENT: // If vessel is in Asecent config...
		SetDefaultPropellantResource (ph_ascentFuel);			// Set ascent stage fuel tank as default propellant resource
		CreateThrusterGroup (&th_ascent, 1, THGROUP_HOVER);		// Assign ascent stage engine to THGROUP_HOVER	
		break;
	} // End "switch (VesselStatus)"

	// Debuggery
	//sprintf (oapiDebugString(), "Vessel Status %0.0f, Empty Mass %0.3f", (float)VesselStatus, GetMass());

} // End "LM::clbkPreStep"
NOTE: Orbiter assumes that a vessel's main axis of thrust will be along the z+ "horizontal/fwd" axis but ours is the y+ "vertical" axis. As such I have assigned the main (ascent and descent stage) engines to "THGROUP_HOVER" rather than "THGROUP_MAIN" to prevent awkward conflicts with certain MFDs and within the Orbiter core itself.

Now that we're all on the same page...

PART 10: Staging
Staging is a complex and involved process. As such it's something that should really be broken off into it's own function. Go to your class interface and declare a new custom function if you haven't done so already.

Code:
...
	double	CalcEmptyMass (void);				// Calculate current empty mass of vessel
	void	StageSeparation (void);				// Jettison descent stage

	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
...
and define it as a stub in our source file...

Code:
} // End "LM::CalcEmptyMass"

// --------------------------------------------------------------
// Jettison descent stage
// --------------------------------------------------------------
void LM::StageSeparation (void)
{

} // End "LM::StageSeparation"
Finally, we'll need a way to call the function (actually jettison the stage) so go to "clbkConsumeBufferedKey" and add the following lines...

Code:
	// Jettison descent stage when [Alt] + [J] is pressed.
	if (key == OAPI_KEY_J && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && KEYMOD_ALT(kstate))
	{ 
		StageSeparation();	// run stage seperation routine...
		return 1;			// ...and return '1' to indicate that the input has been processed
	}
Our stage seperation function will now be activated by pressing alt + J. Now the the ground-work is out of the way let's look at the stage seperation function itself.

I learned most of my orbiter-coding-fu by studying/tearing apart the stock Atlantis' code function by function, line by line, and it just so happens that there is a function in Atlantis.cpp that does almost exactly what we want our stage seperation function to do "Atlantis::SeparateTank". We wont be copying it directly but we will be using it as a reference.

Essentially our stage seperation function needs to do three things...
  • Spawn a new "descent stage" vessel
  • Match the new vessel's state to our parent vessel's
  • Apply the required physical changes to our parent vessel

Lets start by declaring some variables, I've also included a "Sanity check" to prevent the jettison routine from running if there is no descent stage to jettison...

Code:
// --------------------------------------------------------------
// Jettison descent stage
// --------------------------------------------------------------
void LM::StageSeparation (void)
{
	if (VesselStatus == DESCENT) // Sanity check, is there a descent stage to jettison?
	{
		VESSELSTATUS vs;
		char name[256];
		VECTOR3 sofs = LM_DES_OFFSET;	// Seperation offset			
		VECTOR3	sdir = { 0,-1, 0};		// Seperation direction
		double	svel = 0.3;				// Separation velocity
"VESSELSTATUS" is a data structure used by orbiter to keep track of a vessel's state (velocity vectors, orientation, etc...) within the simulation.

"name" is a 256 space character string that we will use to generate a vessel name for our descent stage.

The nature of the last three variables should be readily apperant.

With our variables declared lets take a page from "Atlantis::SeparateTank" and start by spawning the new descent stage vessel. To do this we must first determine the new vessel's position, velocity and orientation within the simulation. IE the contents of it "VESSELSTATUS" data structure.

Because it is part of our parent vessel (the LM) that is being jettisoned we will simply copy our parent vessel's VESSELSTATUS and apply some transformations.

Code:
		VECTOR3	sdir = { 0,-1, 0};		// Seperation direction
		double	svel = 0.3;				// Separation velocity

		// Get vessel status structure
		VECTOR3 rofs;
		GetStatus (vs);
		Local2Rel (sofs, vs.rpos);	
		GlobalRot (sdir, rofs);
		vs.rvel += rofs*svel;
"rofs" is a placeholder variable that we will be used in calculating our translations.

"GetStatus (vs)" copies our vessel's current state to "vs" the VESSELSTATUS structure that we declared at the begining of the function.

"Local2Rel (sofs, vs.rpos);" Is an orbiter core function. It takes our seperation offset variable and writes it to the "relative position" variable inside "vs" after performing "...a transformation from local vessel coordinates to the ecliptic frame centered at the vessel's reference body."

"GlobalRot (sdir, rofs);" converts our seperation direction from our vessel's local reference frame to Orbiter's global reference frame, and saves it to rofs.

Finally "vs.rvel += rofs*svel;" takes the velocity component of "vs" and adds our seperation velocity to it along the vector rofs.

The total result is a vessel state that matches our parent vessel's but is offset by "sofs" and moving away at a rate of "svel" meters per second in "sdir" direction.

Now that we have a VESSELSTATE let's create a vessel.

Code:
		vs.rvel += rofs*svel;

		// Create descent stage as seperate vessel
		strcpy (name, GetName()); 
		strcat (name, "-DescentStage");
		oapiCreateVessel (name, "UMMU_Apollo/LM_descentstage", vs);	// create descent stage vessel
step one is to get a name for our new vessel.

"strcpy (name, GetName());" copies our parent vessel's name to the "name" character string that we declared at the beginning of the function.

"strcat (name, "-DescentStage");" tacks "-DescentStage" onto the end of whatever characters were saved to our "name" string. For instance, if our LM were named "Eagle", "name" would read "Eagle-DescentStage".

Finally "oapiCreateVessel (name, "UMMU_Apollo/LM_descentstage", vs);" creates a new vessel in orbiter using the name, config file, and VESSELSTATE passed to it. In this case I am using "LM_descentstage.cfg" saved in the "UMMU_Apollo" sub-directory of "Orbiter/Config/Vessels".

Lets compile and test...


It works, but there is a problem. Our parent vessel still has a descent stage.

Lets fix that...

Code:
		oapiCreateVessel (name, "UMMU_Apollo/LM_descentstage", vs);	// create descent stage vessel
		
		// Remove descent stage from vessel instance
		DelThruster (th_descent);						// Delete descent stage engine
		DelPropellantResource (ph_descentH2O);			// Delete descent stage water tank
		DelPropellantResource (ph_descentO2);			// Delete descent stage O2 tank
		DelPropellantResource (ph_descentFuel);			// Delete descent stage propellant tank
		DelMesh (mesh_Descent);							// Delete descent stage mesh
		VesselStatus = ASCENT;							// Set vessel status to ascent

These lines should be self explanetory. they delete the descent stage thrusters, propellant resources and mesh from our vessel instance and then set our VesselStatus to "ASCENT" so that the rest of our vessel's functions will recognize that the descent stage has been jettisoned.

NOTE: When deleting propellant tanks or thrusters from a vessel they should always be removed in the opposite order that they were created. Failure to do so can cause some akward errors durring playbacks or when closing and opening scenarios because Orbiter records propellant levels and throttle settings by index number rather than handle. If you delete "tank 2" before you delete "tank 4", tank 2's status will become tank 3's and so on.

Compile and test again...


Much better

Now this last option is entirely optional but I'm going to do it because I'm a completionist and knowing how to do it may come in handy later. Lets set the propellant levels in our spawned descent stage to those in our parent vessel's descent tanks.

For this to work we need to do it after we've spawned the descent stage vessel but before we delete the descent tanks from our parent vessel.

Code:
		oapiCreateVessel (name, "UMMU_Apollo/LM_descentstage", vs);	// create descent stage vessel

		// Match descent stage's propellant levels to LM's descent tanks  
		OBJHANDLE oh_descent = oapiGetObjectByName (name);		// get handle of descent stage vessel 
		VESSEL *ds = oapiGetVesselInterface (oh_descent);		// get interface for descent stage
		ds->SetPropellantMass (ds->GetPropellantHandleByIndex (0), GetPropellantMass (ph_descentFuel));
		ds->SetPropellantMass (ds->GetPropellantHandleByIndex (1), GetPropellantMass (ph_descentO2));
		ds->SetPropellantMass (ds->GetPropellantHandleByIndex (2), GetPropellantMass (ph_descentH2O));
		
		// Remove descent stage from vessel instance
		DelThruster (th_descent);						// Delete descent stage engine
This is actually our first interaction with external class interfaces/instances.

"OBJHANDLE oh_descent = oapiGetObjectByName (name);" gets a handle for the descent stage vessel that we just created.

"VESSEL *ds = oapiGetVesselInterface (oh_descent);" This function creates a new class interface (it does not create a new vessel) called "ds" that points to an existing vessel (our descent stage). Using this interface we can now call functions in that vessel's code as well as our own.

NOTE: much care should be taken in which functions you call. All sort of errors/bugs can arise from calling a function that is not declared or has a different definition from the vessel you're calling it from. To play it safe, restrict these calls to default Orbiter API functions such as the above.

"ds->" indicates that we are calling the function that follows in "ds"'s code rather than our own vessel's. In this case we are getting handles for 3 of ds's propellant tanks and setting their proppellant masses to the masses in our parent vessel's descent tanks.

NOTE: this assumes that the descent stage vessel has 3 tanks. this can cause issues if does not. To head-off any such problems I will simply post the source code of my descent stage here. At this point, if you've been following along, you should know what to do with it.

Code:
// ==============================================================
//                ORBITER MODULE: LM Descent
//                  Part of the ORBITER SDK
//               Copyright (C) 2012 Greg Hlynka
//                    All rights reserved
//
// LM_DescentStage.cpp
// Control module for LM Descent Stage vessel class
//
// Notes:
// ==============================================================

#include "LM.h"

// ==============================================================
// Some vessel Constants
// ==============================================================

const double	LM_DES_SIZE			= 4.3;					// Decent Stage's mean radius [m]
const VECTOR3	LM_DES_CS			= { 7.38, 7.38, 12.8};	// Decent Stage x,y,z cross sections [m^2]
const VECTOR3	LM_DES_PMI			= { 1.28, 1.28, 2.06};	// Decent Stage principal moments of inertia (mass-normalised) [m^2]
const VECTOR3	LM_DES_RD			= {	0.01, 0.01, 0.01};	// Decent Stage rotation drag coefficients
const VECTOR3	LM_DES_TDP[3]		= {{ 0.0,-2.3, 4.4}, {-3.8,-2.3,-2.2}, { 3.8,-2.3,-2.2}}; // Decent Stage touchdown points [m]

// ==============================================================
// Descent stage class Interface
// ==============================================================

class LM_Descent: public VESSEL3 {
public:
	LM_Descent (OBJHANDLE hVessel, int flightmodel);
	~LM_Descent ();

	// Custom vessel functions
	void	DefineAnimations (void);			// Define mesh animations
	void	DefineParticleStreams (void);		// Define exhaust and particle streams
	void	DefineSounds (void);				// Define sound effects

	// Overloaded callback functions
	void	clbkSetClassCaps (FILEHANDLE cfg);								// Set the capabilities of the vessel class
	void	clbkLoadStateEx (FILEHANDLE scn, void *status);					// Read status from scenario file
	void	clbkSaveState (FILEHANDLE scn);									// Write status to scenario file
	void	clbkPostStep (double simt, double simdt, double mjd);			// Manage Animations and Post-Step processes
	int		clbkConsumeBufferedKey (DWORD key, bool down, char *kstate);	// Process keyboard inputs

	// Thruster and Propellant handles
	PROPELLANT_HANDLE	ph_descentFuel;						// Functional Propellant tanks
	PROPELLANT_HANDLE	ph_descentO2, ph_descentH2O;		// Represenative propellant tanks (consumables)
	
	THRUSTER_HANDLE		th_descent;							// Functional thrusters
	THRUSTER_HANDLE		th_dust, th_vent;					// Represenative thrusters (particle streams)

private:
	// Vessel status variables
	enum	doorstate	{CLOSED, OPEN, CLOSING, OPENING}	GearStatus;

	// Animations
	UINT						anim_Gear;
	double						gear_proc;
	ANIMATIONCOMPONENT_HANDLE	ach_GearLeg[4], ach_GearStrut[4], ach_GearLock[4];
	MGROUP_TRANSFORM			*mgt_Leg[4], *mgt_Strut[4], *mgt_Downlock[4];
	
	// Meshes
	MESHHANDLE	mh_descent;
};

LM_Descent::LM_Descent (OBJHANDLE hVessel, int flightmodel)
: VESSEL3 (hVessel, flightmodel)
{
	DefineAnimations();

	// Set initial values
	gear_proc = 1.0;
	GearStatus = OPEN;	

	// Load Mesh
	mh_descent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_DescentStage");
}

LM_Descent::~LM_Descent ()
{
}

// ==============================================================
// Custom vessel functions
// ==============================================================

// --------------------------------------------------------------
// Define Animations
// --------------------------------------------------------------
void LM_Descent::DefineAnimations(void)
{
	// Declare mesh groups to be animated
	static UINT meshgroup_Legs[4][3] = {
		{DS_GRP_LandingFoot_FWD,	DS_GRP_ShockStrut_FWD,	DS_GRP_PrimaryStrut_FWD}, 
		{DS_GRP_LandingFoot_AFT,	DS_GRP_ShockStrut_AFT,	DS_GRP_PrimaryStrut_AFT},
		{DS_GRP_LandingFoot_PORT,	DS_GRP_ShockStrut_PORT,	DS_GRP_PrimaryStrut_PORT}, 
		{DS_GRP_LandingFoot_STBD,	DS_GRP_ShockStrut_STBD,	DS_GRP_PrimaryStrut_STBD}
	};

	static UINT meshgroup_Struts[4] = { DS_GRP_SecondaryStruts_FWD, DS_GRP_SecondaryStruts_AFT, DS_GRP_SecondaryStruts_PORT, DS_GRP_SecondaryStruts_STBD};

	static UINT meshgroup_Locks[4] = { DS_GRP_Downlock_FWD, DS_GRP_Downlock_AFT, DS_GRP_Downlock_PORT, DS_GRP_Downlock_STBD};

	static UINT meshgroup_Ladder = DS_GRP_Ladder;

	// Create landing gear animation
	anim_Gear = CreateAnimation (1.0);

	for (int i = 0; i < 4; i++)
	{
		// Animation components
		mgt_Leg[i]		= new MGROUP_ROTATE ( 0, &meshgroup_Legs[i][0], 3, LM_LegPivot[i], LM_LegAxis[i], (float) 45*RAD);		// Animate landing legs
		mgt_Strut[i]	= new MGROUP_ROTATE ( 0, &meshgroup_Struts[i], 1, LM_StrutPivot[i], LM_LegAxis[i], (float)-63*RAD);		// Animate Support Struts attatched to legs
		mgt_Downlock[i]	= new MGROUP_ROTATE ( 0, &meshgroup_Locks[i], 1, LM_DownlockPivot[i], LM_LegAxis[i], (float) 150*RAD);	// Animate Locking mechanism joining support struts to body

		// Add individual components to 'anim_Gear' 
		ach_GearLeg[i]		= AddAnimationComponent (anim_Gear, 0, 1, mgt_Leg[i]);
		ach_GearStrut[i]	= AddAnimationComponent (anim_Gear, 0, 1, mgt_Strut[i], ach_GearLeg[i]);
		ach_GearLock[i]		= AddAnimationComponent (anim_Gear, 0, 1, mgt_Downlock[i], ach_GearStrut[i]);
	}

	static MGROUP_ROTATE mgt_Ladder ( 0, &meshgroup_Ladder, 1, LM_LegPivot[0], LM_LegAxis[0], (float) 45*RAD); // Apply front leg's animation state to ladder.
	AddAnimationComponent (anim_Gear, 0, 1, &mgt_Ladder);

} // end "DefineAnimations(void)"

// --------------------------------------------------------------
// Define exhaust and particle streams
// --------------------------------------------------------------
void LM_Descent::DefineParticleStreams (void)
{
	// Load exhaust textures
	SURFHANDLE tex_main = oapiRegisterExhaustTexture ("Exhaust2");
	AddExhaust (th_descent, 4.0, 0.8, 1.4, tex_main);

} // End "LM_Descent::DefineParticleStreams"

// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM_Descent::DefineSounds (void)
{

} // End "LM_Descent::DefineSounds"

// ==============================================================
// Overloaded callback functions
// ==============================================================

// --------------------------------------------------------------
// Set capabilities of vessel
// --------------------------------------------------------------
void LM_Descent::clbkSetClassCaps (FILEHANDLE cfg)
{
	// physical vessel parameters
	SetSize (LM_DES_SIZE);
	SetCrossSections (LM_DES_CS);
	SetPMI (LM_DES_PMI);
	SetRotDrag (LM_DES_RD);
	SetEmptyMass (LM_DES_EMPTYMASS);
	SetTouchdownPoints (LM_DES_TDP[0], LM_DES_TDP[1], LM_DES_TDP[2]);

	// propellant resources
	ph_descentFuel	= CreatePropellantResource (LM_DES_FUELMASS);		// Descent stage propellant tank
	ph_descentO2	= CreatePropellantResource (LM_DES_O2MASS);			// Descent stage O2 tank
	ph_descentH2O	= CreatePropellantResource (LM_DES_WATERMASS);		// Descent stage water tank

	// main engine
	th_descent	= CreateThruster (LM_DES_ENGINEPOS - LM_DES_OFFSET, _V( 0, 1, 0), LM_DES_MAXTHRUST, ph_descentFuel, LM_DES_ISP);	

	// associate a mesh for the visual
	AddMesh (mh_descent);	
	SetMeshVisibilityMode (0, MESHVIS_ALWAYS);	

	// camera parameters
	SetCameraOffset (_V( 0.0, 2.4, 0.0));
}

// --------------------------------------------------------------
// Read status from scenario file
// --------------------------------------------------------------
void LM_Descent::clbkLoadStateEx (FILEHANDLE scn, void *status)
{
	int i = 0;
	char *cbuf; 

	while (oapiReadScenario_nextline (scn, cbuf)) 
	{
		// Load animation states
		if (!_strnicmp (cbuf, "GEAR", 4)) // find line labeled "GEAR" in scn file
		{
			sscanf (cbuf+4, "%i %lf", &GearStatus, &gear_proc);		// Read values stored there to GearStatus and gear_proc
			SetAnimation (anim_Gear, gear_proc);					// Apply process value to animation.
		}

		// Load default parameters
		else ParseScenarioLineEx (cbuf, status);	

	} // End "while (oapiReadScenario_nextline (scn, cbuf))"

} // End "clbkLoadStateEx(FILEHANDLE scn, void *status)"

// --------------------------------------------------------------
// Write status to scenario file
// --------------------------------------------------------------
void LM_Descent::clbkSaveState (FILEHANDLE scn)
{
	int i = 0;
	char cbuf[256];
	
	// Write default vessel parameters to file
	VESSEL3::clbkSaveState (scn);

	// Write custom parameters to file
	sprintf (cbuf, "%i %0.4f", GearStatus, gear_proc);		// Landing Gear status and animation state
	oapiWriteScenario_string (scn, "GEAR", cbuf);

} // End "clbkSaveState(FILEHANDLE scn)"

// --------------------------------------------------------------
// Post-Step processes and Animations
// --------------------------------------------------------------
void LM_Descent::clbkPostStep (double simt, double simdt, double mjd)
{
	// Landing Gear animation control logic
	if (gear_proc < 0)			// If process value is less than 0...
	{
		GearStatus = CLOSED;	// ...set status to "CLOSED"
		gear_proc = 0;			// and process value to 0
	}

	else if (gear_proc > 1)		// If process value is greater than 1...
	{
		GearStatus = OPEN;		// ...set status to "OPEN"
		gear_proc = 1;			// and process value to 1
	}

	if (GearStatus > CLOSED)	
	{
		double	delta = simdt / 0.3;	// delta = simdt divided by duration of animation in seconds

		if (GearStatus == OPENING)		// if Status equals "OPENING"...
		{
			gear_proc += delta;			// ...add delta to process value
		}
		
		if (GearStatus == CLOSING)		// if Status equals "CLOSING"...
		{
			gear_proc -= delta;			// ...subtract it.
		}

		SetAnimation( anim_Gear, gear_proc);	// apply process value to animation
	}
	
	//// Debuggery
	//sprintf(oapiDebugString(), "Landing Gear Status %0.0f, gear_proc %0.3f", (float)GearStatus, gear_proc);
}

// --------------------------------------------------------------
// Process keyboard inputs
// --------------------------------------------------------------
int  LM_Descent::clbkConsumeBufferedKey (DWORD key, bool down, char *kstate)
{
	// Deploy Landing Gear when [G] is pressed.
	if(key == OAPI_KEY_G  && down && !KEYMOD_SHIFT(kstate) && !KEYMOD_CONTROL (kstate) && !KEYMOD_ALT(kstate))
	{
		if (GearStatus != OPEN) GearStatus = OPENING;
		else GearStatus = CLOSING;
		return 1;
	}

	return 0; // if no keys are pressed the function returns '0' and nothing happens
}

// ==============================================================
// API callback interface
// ==============================================================

// --------------------------------------------------------------
// Vessel initialisation
// --------------------------------------------------------------
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
	return new LM_Descent (hvessel, flightmodel);
}

// --------------------------------------------------------------
// Vessel cleanup
// --------------------------------------------------------------
DLLCLBK void ovcExit (VESSEL *vessel)
{
	if (vessel) delete (LM_Descent*)vessel;
}
Compile everything one more time and test it...

Use the scenario editor to mess with the propellant levels and make sure that they transfer properly.

So far so good, yes?

we're almost done, but there are still two things left to do...

the first is minor house keeping, if our vessel starts the scenario in ascent configuration it will still appear with the descent stage mesh, engine, and tanks. They only get deleted when the stage seperation routine runs and if we're already in ascent config that wont happen.

as such we need to go into "clbkPostCreation" and apply the changes that would normally happen durring stage seperation...

Code:
void LM::clbkPostCreation ()
{
	DefineAnimations ();
	DefineLighting ();
	DefineParticleStreams ();
	DefineSounds ();

	if (VesselStatus == ASCENT) // If vessel is spawned in ASCENT config we need to apply the changes that would normally happen durring stage seperation...
	{
		DelThruster (th_descent);						// Delete descent stage engine
		DelPropellantResource (ph_descentH2O);			// Delete descent stage water tank
		DelPropellantResource (ph_descentO2);			// Delete descent stage O2 tank
		DelPropellantResource (ph_descentFuel);			// Delete descent stage propellant tank
		DelMesh (mesh_Descent);							// Delete descent stage mesh
	}
The second thing we need to do is shift our vessel's center of gravity and physical parameters.

Ordinarily we would do this as part of the stage seperation function but in this case the LM's "quirk" will bite us in the ass.

You see, changing a vessels physical parameters especially it's CoG and touchdown points while a vessel is on the ground can cause all sorts of weirdness (up to and including getting catapulted into NaN-space). Where does the LM do it's staging? On the ground of course.

As such we need a work-around, fortunatly for you I've already thought of one.

What we will do is add a set of instructions to the "ASCENT" case of our switch in "clbkPreStep" that will check to see if A) our vessel is landed and B) if our parameters have already been changed. If both come back "false" our new parameters will be applied.

To make this work we need to add a new variable to our class interface...

Code:
private:
	// Vessel status variables
	enum	vesselstate	{LAUNCH, DESCENT, ASCENT, WRECKED}	VesselStatus;	
	enum	doorstate	{CLOSED, OPEN, CLOSING, OPENING}	HatchStatus, GearStatus;
	double	thrust_mod, dv_mod, consumables_mod;
	bool	CGshifted;
"bool" stands for "boolean" which is a variable or function that returns one of two possible values "true" or "false".

Let's initialise "CGshifted" in the class constructor...
Code:
	// Initial vessel state
	VesselStatus	= DESCENT;
	CGshifted		= false;
And then add the required lines to our switch in clbkPreStep...
Code:
	case ASCENT: // If vessel is in Ascent config...
		SetDefaultPropellantResource (ph_ascentFuel);			// Set ascent stage fuel tank as default propellant resource
		CreateThrusterGroup (&th_ascent, 1, THGROUP_HOVER);		// Assign ascent stage engine to THGROUP_HOVER	
		
		if ((CGshifted == false) && (GroundContact() == false)) // If center of gravity has not already been shifted and LM is not touching the ground... 
		{
			SetSize (LM_ASC_SIZE);			// Update size
			SetPMI (LM_ASC_PMI);			// Update moments of intertia
			SetCrossSections (LM_ASC_CS);	// Update cross sections
			SetRotDrag (LM_ASC_RD);			// Update rotational drag
			SetTouchdownPoints (LM_ASC_TDP[0], LM_ASC_TDP[1], LM_ASC_TDP[2]); // Update touchdown points
			ShiftCG (LM_ASC_OFFSET);		// Shift center of gravity
			CGshifted = true;				// Mark "CGshifted" as "true"
		}
		break;
	} // End "switch (VesselStatus)"
Once again, compile and test...


Staging successful!

This concludes Part 10 and our class on custom vessel states and staging.

What topic should I cover next?

VCs? Lights? Particle streams? UMMU Support? Sound effects?

Last edited by Hlynkacg; 01-07-2013 at 09:43 PM.
Hlynkacg is offline   Reply With Quote
Old 01-07-2013, 10:50 PM   #21
DaveS
Addon Developer
 
DaveS's Avatar


Default

Thanks for the latest installment! I'm a bit lost on the Descent Stage source part. Should I create a new project for it and it to the main solution? I keep getting unresolved linker errors when I compile the DS source despite having set up the DS project exactly like the main project.
DaveS is online now   Reply With Quote
Old 01-07-2013, 10:57 PM   #22
Hlynkacg
Aspiring rocket scientist
 
Hlynkacg's Avatar


Default

Quote:
Originally Posted by DaveS View Post
 Thanks for the latest installment! I'm a bit lost on the Descent Stage source part. Should I create a new project for it and it to the main solution? I keep getting unresolved linker errors when I compile the DS source despite having set up the DS project exactly like the main project.
That's what I did but you can also create it as a completely seperate solution.

If you're gettin linker errors the only thing I can think of is to check the DescentStage's project properties and make sure that it's reading your orbiter sdk directory and the directory containing your parent vessel (LM).

If it can find LM.h that might be your problem.

Other than that you should simply set the DescentStage's project up the same way you did the LM proper.

ETA:
Any requests for what to cover next?
Hlynkacg is offline   Reply With Quote
Old 01-07-2013, 11:17 PM   #23
DaveS
Addon Developer
 
DaveS's Avatar


Default

Quote:
Originally Posted by Hlynkacg View Post
 That's what I did but you can also create it as a completely seperate solution.

If you're gettin linker errors the only thing I can think of is to check the DescentStage's project properties and make sure that it's reading your orbiter sdk directory and the directory containing your parent vessel (LM).
No joy. Still keep getting the unresolved linker errors:

Code:
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "void __cdecl dummy(void)" (?dummy@@YAXXZ) referenced in function "void __cdecl calldummy(void)" (?calldummy@@YAXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void * __cdecl oapiLoadMeshGlobal(char const *)" (__imp_?oapiLoadMeshGlobal@@YAQAXPBD@Z) referenced in function "public: __thiscall LM_Descent::LM_Descent(void *,int)" (??0LM_Descent@@QAE@PAXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall VESSEL3::VESSEL3(void *,int)" (__imp_??0VESSEL3@@QAE@PAXH@Z) referenced in function "public: __thiscall LM_Descent::LM_Descent(void *,int)" (??0LM_Descent@@QAE@PAXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkSetStateEx(void const *)" (?clbkSetStateEx@VESSEL2@@UAEXPBX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkPostCreation(void)" (?clbkPostCreation@VESSEL2@@UAEXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkFocusChanged(bool,void *,void *)" (?clbkFocusChanged@VESSEL2@@UAEX_NPAX1@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkPreStep(double,double,double)" (?clbkPreStep@VESSEL2@@UAEXNNN@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkPlaybackEvent(double,double,char const *,char const *)" (?clbkPlaybackEvent@VESSEL2@@UAE_NNNPBD0@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkVisualCreated(void *,int)" (?clbkVisualCreated@VESSEL2@@UAEXPAXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkVisualDestroyed(void *,int)" (?clbkVisualDestroyed@VESSEL2@@UAEXPAXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkDrawHUD(int,struct HUDPAINTSPEC const *,struct HDC__ *)" (?clbkDrawHUD@VESSEL2@@UAEXHPBUHUDPAINTSPEC@@PAUHDC__@@@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkRCSMode(int)" (?clbkRCSMode@VESSEL2@@UAEXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkADCtrlMode(unsigned long)" (?clbkADCtrlMode@VESSEL2@@UAEXK@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkHUDMode(int)" (?clbkHUDMode@VESSEL2@@UAEXH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkMFDMode(int,int)" (?clbkMFDMode@VESSEL2@@UAEXHH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkNavMode(int,bool)" (?clbkNavMode@VESSEL2@@UAEXH_N@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkDockEvent(int,void *)" (?clbkDockEvent@VESSEL2@@UAEXHPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL2::clbkAnimate(double)" (?clbkAnimate@VESSEL2@@UAEXN@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall VESSEL2::clbkConsumeDirectKey(char *)" (?clbkConsumeDirectKey@VESSEL2@@UAEHPAD@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkLoadGenericCockpit(void)" (?clbkLoadGenericCockpit@VESSEL2@@UAE_NXZ)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkLoadPanel(int)" (?clbkLoadPanel@VESSEL2@@UAE_NH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkPanelMouseEvent(int,int,int,int)" (?clbkPanelMouseEvent@VESSEL2@@UAE_NHHHH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkPanelRedrawEvent(int,int,void *)" (?clbkPanelRedrawEvent@VESSEL2@@UAE_NHHPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkLoadVC(int)" (?clbkLoadVC@VESSEL2@@UAE_NH@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkVCMouseEvent(int,int,union VECTOR3 &)" (?clbkVCMouseEvent@VESSEL2@@UAE_NHHAATVECTOR3@@@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL2::clbkVCRedrawEvent(int,int,void *)" (?clbkVCRedrawEvent@VESSEL2@@UAE_NHHPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL3::clbkPanelMouseEvent(int,int,int,int,void *)" (?clbkPanelMouseEvent@VESSEL3@@UAE_NHHHHPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL3::clbkPanelRedrawEvent(int,int,void *,void *)" (?clbkPanelRedrawEvent@VESSEL3@@UAE_NHHPAX0@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall VESSEL3::clbkGeneric(int,int,void *)" (?clbkGeneric@VESSEL3@@UAEHHHPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL3::clbkLoadPanel2D(int,void *,unsigned long,unsigned long)" (?clbkLoadPanel2D@VESSEL3@@UAE_NHPAXKK@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall VESSEL3::clbkDrawHUD(int,struct HUDPAINTSPEC const *,class oapi::Sketchpad *)" (?clbkDrawHUD@VESSEL3@@UAE_NHPBUHUDPAINTSPEC@@PAVSketchpad@oapi@@@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL3::clbkRenderHUD(int,struct HUDPAINTSPEC const *,void *)" (?clbkRenderHUD@VESSEL3@@UAEXHPBUHUDPAINTSPEC@@PAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall VESSEL3::clbkGetRadiationForce(union VECTOR3 const &,union VECTOR3 &,union VECTOR3 &)" (?clbkGetRadiationForce@VESSEL3@@UAEXABTVECTOR3@@AAT2@1@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void * __thiscall VESSEL::AddAnimationComponent(unsigned int,double,double,class MGROUP_TRANSFORM *,void *)const " (__imp_?AddAnimationComponent@VESSEL@@QBEPAXINNPAVMGROUP_TRANSFORM@@PAX@Z) referenced in function "public: void __thiscall LM_Descent::DefineAnimations(void)" (?DefineAnimations@LM_Descent@@QAEXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: unsigned int __thiscall VESSEL::CreateAnimation(double)const " (__imp_?CreateAnimation@VESSEL@@QBEIN@Z) referenced in function "public: void __thiscall LM_Descent::DefineAnimations(void)" (?DefineAnimations@LM_Descent@@QAEXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: unsigned int __thiscall VESSEL::AddExhaust(void *,double,double,double,void *)const " (__imp_?AddExhaust@VESSEL@@QBEIPAXNNN0@Z) referenced in function "public: void __thiscall LM_Descent::DefineParticleStreams(void)" (?DefineParticleStreams@LM_Descent@@QAEXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void * __cdecl oapiRegisterExhaustTexture(char *)" (__imp_?oapiRegisterExhaustTexture@@YAPAXPAD@Z) referenced in function "public: void __thiscall LM_Descent::DefineParticleStreams(void)" (?DefineParticleStreams@LM_Descent@@QAEXXZ)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetCameraOffset(union VECTOR3 const &)const " (__imp_?SetCameraOffset@VESSEL@@QBEXABTVECTOR3@@@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetMeshVisibilityMode(unsigned int,unsigned short)const " (__imp_?SetMeshVisibilityMode@VESSEL@@QBEXIG@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: unsigned int __thiscall VESSEL::AddMesh(void *,union VECTOR3 const *)const " (__imp_?AddMesh@VESSEL@@QBEIPAXPBTVECTOR3@@@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void * __thiscall VESSEL::CreateThruster(union VECTOR3 const &,union VECTOR3 const &,double,void *,double,double,double)const " (__imp_?CreateThruster@VESSEL@@QBEPAXABTVECTOR3@@0NPAXNNN@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void * __thiscall VESSEL::CreatePropellantResource(double,double,double)const " (__imp_?CreatePropellantResource@VESSEL@@QBEPAXNNN@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetTouchdownPoints(union VECTOR3 const &,union VECTOR3 const &,union VECTOR3 const &)const " (__imp_?SetTouchdownPoints@VESSEL@@QBEXABTVECTOR3@@00@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetEmptyMass(double)const " (__imp_?SetEmptyMass@VESSEL@@QBEXN@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetRotDrag(union VECTOR3 const &)const " (__imp_?SetRotDrag@VESSEL@@QBEXABTVECTOR3@@@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetPMI(union VECTOR3 const &)const " (__imp_?SetPMI@VESSEL@@QBEXABTVECTOR3@@@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetCrossSections(union VECTOR3 const &)const " (__imp_?SetCrossSections@VESSEL@@QBEXABTVECTOR3@@@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::SetSize(double)const " (__imp_?SetSize@VESSEL@@QBEXN@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSetClassCaps(void *)" (?clbkSetClassCaps@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall VESSEL::ParseScenarioLineEx(char *,void *)const " (__imp_?ParseScenarioLineEx@VESSEL@@QBEXPADPAX@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkLoadStateEx(void *,void *)" (?clbkLoadStateEx@LM_Descent@@UAEXPAX0@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: bool __thiscall VESSEL::SetAnimation(unsigned int,double)const " (__imp_?SetAnimation@VESSEL@@QBE_NIN@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkLoadStateEx(void *,void *)" (?clbkLoadStateEx@LM_Descent@@UAEXPAX0@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) bool __cdecl oapiReadScenario_nextline(void *,char * &)" (__imp_?oapiReadScenario_nextline@@YA_NPAXAAPAD@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkLoadStateEx(void *,void *)" (?clbkLoadStateEx@LM_Descent@@UAEXPAX0@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl oapiWriteScenario_string(void *,char *,char *)" (__imp_?oapiWriteScenario_string@@YAXPAXPAD1@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSaveState(void *)" (?clbkSaveState@LM_Descent@@UAEXPAX@Z)
1>Grumman_LM_DescentStage.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: virtual void __thiscall VESSEL2::clbkSaveState(void *)" (__imp_?clbkSaveState@VESSEL2@@UAEXPAX@Z) referenced in function "public: virtual void __thiscall LM_Descent::clbkSaveState(void *)" (?clbkSaveState@LM_Descent@@UAEXPAX@Z)
Anything you need?
DaveS is online now   Reply With Quote
Old 01-07-2013, 11:21 PM   #24
Urwumpe
Certain Super User
 
Urwumpe's Avatar

Default

Did you configure the directories and libraries for the current configuration? eg for Release and not for debug?
Urwumpe is offline   Reply With Quote
Thanked by:
Old 01-07-2013, 11:32 PM   #25
DaveS
Addon Developer
 
DaveS's Avatar


Default

Quote:
Originally Posted by Urwumpe View Post
 Did you configure the directories and libraries for the current configuration? eg for Release and not for debug?
Yes. I set it up both for Release and Debug. No joy on either. Here's the settings:



DaveS is online now   Reply With Quote
Old 01-07-2013, 11:39 PM   #26
Urwumpe
Certain Super User
 
Urwumpe's Avatar

Default

Include directories are not library directories, there is a different option for them. And you still need to add the names of the libraries that you plan to include.
Urwumpe is offline   Reply With Quote
Thanked by:
Old 01-07-2013, 11:46 PM   #27
orb
O-F Administrator
Ninja
 
orb's Avatar

Default

Quote:
Originally Posted by DaveS View Post
 
Setting additional library directories isn't enough. You also need to add Orbiter.lib and Orbitersdk.lib to additional linker dependencies (only if you don't use Orbiter property sheets, of course ).
orb is offline   Reply With Quote
Thanked by:
Old 01-07-2013, 11:52 PM   #28
DaveS
Addon Developer
 
DaveS's Avatar


Default

Thanks all. Orb: How do you add property sheets? I have tried but I only get an error message.
DaveS is online now   Reply With Quote
Old 01-07-2013, 11:57 PM   #29
orb
O-F Administrator
Ninja
 
orb's Avatar

Default

Quote:
Originally Posted by DaveS View Post
 Orb: How do you add property sheets?
<PostID: 378725>, or search for "Property Manager" phrase in forums or blogs.

Quote:
Originally Posted by DaveS View Post
 I have tried but I only get an error message.
What error message? When you add the property sheets or during build process?
orb is offline   Reply With Quote
Old 01-08-2013, 12:02 AM   #30
DaveS
Addon Developer
 
DaveS's Avatar


Default

Quote:
Originally Posted by orb View Post
 <PostID: 378725>, or search for "Property Manager" phrase in forums or blogs.


What error message? When you add the property sheets or during build process?
When adding them. They have the extension *.vsprops while VC++ 2010 Express only recognizes *.props.
DaveS is online now   Reply With Quote
Reply

  Orbiter-Forum > Orbiter Space Flight Simulator > Tutorials & Challenges


Thread Tools

Posting Rules
BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
Forum Jump


All times are GMT. The time now is 12:26 AM.

Quick Links Need Help?


About Us | Rules & Guidelines | TOS Policy | Privacy Policy

Orbiter-Forum is hosted at Orbithangar.com
Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright 2007 - 2012, Orbiter-Forum.com. All rights reserved.