Tutorial Coding a Lunar Lander from the ground up (when you don't know how to code)

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
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.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
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?
 

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
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?
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,588
Reaction score
2,312
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Did you configure the directories and libraries for the current configuration? eg for Release and not for debug?
 

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
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:

DS_C%2B%2B.jpg


DS_Linker.jpg
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,588
Reaction score
2,312
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
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.
 

orb

O-F Administrator,
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
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 :p).
 

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
Thanks all. Orb: How do you add property sheets? I have tried but I only get an error message.
 

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
<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.
PropSheet_Error.jpg
 

orb

O-F Administrator,
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
When adding them. They have the extension *.vsprops while VC++ 2010 Express only recognizes *.props.

Point 2 in the linked post covers that (i.e. converting a stock vessel and plug-in sample to VS2010 version, along with property sheets [vsprops -> props]).
 

DaveS

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Feb 4, 2008
Messages
9,429
Reaction score
680
Points
203
ETA:
Any requests for what to cover next?
How about a shortie on how deal with payloads? IE going from LM to an upper stage.

---------- Post added at 03:51 AM ---------- Previous post was at 01:27 AM ----------

How about a shortie on how deal with payloads? IE going from LM to an upper stage.
After thinking a bit more on this, I believe the best approach would be to use attachments for the payloads.

So this is my part requests:
  • Payloads through attachments
  • Thrust Vector Control
  • Parent/Child hierarchical animations

This along with the other parts should cover most things needed to create some nice stuff through the Orbiter API.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
After thinking a bit more on this, I believe the best approach would be to use attachments for the payloads.

So this is my part requests:
  • Payloads through attachments
  • Thrust Vector Control
  • Parent/Child hierarchical animations

This along with the other parts should cover most things needed to create some nice stuff through the Orbiter API.

To be honest, I'm a little shakey on payloads/attachment points. I simply do not have the working knowledge to wrte a worthwhile tutorial on the subject.

We already touched upon Parent/Child hierarchical animations with the hatch and it's handle so what specifically do you have in mind?

Thrust vectoring I can do ;)
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
This will be quick and simple, I just want to get it out of the way.

PART 11: OrbiterSound Support

Step 1 is obviously to install Dan Steph's OrbiterSound.

NOTE: my code uses v 4.0

Dan already included an excellent tutorial in his own documentation so I will only touch upon the basics. Once Orbitersound has been installed go to the sound/OrbiterSound_SDK folder in your Orbiter root directory and find the following files "OrbiterSoundSDK40.h" and "OrbiterSoundSDK40.lib". Copy and paste these files to your "OrbiterSDK/Include" and "Lib" folders respectivly so that Visual Studio will be able to see them.

Alternately you can go into your project properties and add the sound/OrbiterSound_SDK directory to your include directories.

Once youve done this you will be able to call OrbiterSound's Functions (load and play sound effects/music) from within your own code. So let's get to coding.

Open your vessels header file and add the following line to your class interface.

Code:
	void	clbkPostStep (double simt, double simdt, double mjd);					// Post-Step processes

[COLOR="red"]	// ID number for OrbiterSound
	int		OrbiterSoundID;[/COLOR]

	// Thruster and Propellant handles
	PROPELLANT_HANDLE	ph_rcsA, ph_rcsB, ph_ascentFuel, ph_descentFuel;			// Functional Propellant tanks

Orbiter sound assigns a unique ID number to every vessel in the scenario. This number is used by Orbiter Sound to keep track of which sounds belong to which vessel.

Now it's time to start adding sounds. By default orbitersound simply assigns a number to each sound in turn as it is loaded but this can become complex and hard to read. As such I will be assigning my own values using the #define macro. #define associates an identifier with a token string or variable so that rather than trying to remeber that our "Hatch opening" sound is sond effect # 1161 we can simply call it "Hatch_open" and the compiler will fill in the appropriate value for us.

To avoid conflicts from overlapping definitions #defines should be kept in thier own header files. As such lets create a new header file. I called mine "LM_Sounds.h"

here are it's contents...
Code:
// ==============================================================
//		ORBITER MODULE: 'SPIDER' LUNAR EXCURSION MODULE
//			A custom Vessel For Orbiter 2010/2011
//  
// LM_Sounds.h
// Definitions for OrbiterSound 4.0
// ==============================================================

#pragma once

#define SILENCE				00 // "silence.wav" The sound of...

#define SOUND_BTN			01 // Button press "button.wav"
#define SOUND_ROTARY		02 // Dial click "rotary.wav"
#define SOUND_SWITCH		03 // Switch sound "switch_throw.wav"
#define SOUND_SWITCHGUARD	04 // Alternate Switch sound "switch_guard.wav"

#define SOUND_HATCHOPEN		05 // EVA Hatch opening "hatch_open.wav"
#define SOUND_HATCHCLOSE	06 // EVA Hatch closing "hatch_close.wav"
#define SOUND_STAGESEP		07 // Stage Seperation "stage_sep.wav"
#define SOUND_EXPLSVDEVICE	10 // Explsoive device firing "cad.wav"

#define SOUND_CAUTION		11 // Basic alert tone "alarm_caution.wav"
#define SOUND_WARNING		12 // OH :censored:! alarm tone "alarm_warning.wav"
Yes it is a direct copy/pase from my [ame="http://www.orbithangar.com/searchid.php?ID=5846"]Spider Lunar Lander *Beta v2.3*[/ame] addon's code. don't worry about it.

What we've done is assign each name "SOUND_HATCHOPEN" to a number "05" and vice versa. I know it doesn't seem like much now but doing this will make things monumentally easier down the line. Especially as we get

into more complex operations involving sub-classes and the like.

Now that we've got our definitions set lets put them to work.

Go back to our original (from here on refered to as "our vessel's") header file and include "OrbiterSoundSDK40.h". Also include our new Sound header file.

Code:
// Orbiter SDK files
#include "orbitersdk.h"
[COLOR="red"]#include "OrbiterSoundSDK40.h"[/COLOR]

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

[COLOR="red"]// Other resource files and sub-classes
#include "LM_Sounds.h"[/COLOR]

Now we're ready to start coding in earnest.

If you recall, back in Part 8 I added an empty function source code called called "DefineSounds", lets fill it.

Code:
// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{
	[COLOR="red"]// OrbiterSound 4.0
	OrbiterSoundID = ConnectToOrbiterSoundDLL (GetHandle());			// Init sound[/COLOR]

The first step is to get our vessel's ID number from orbitersound. We need this ID to modify our own vessel's sounds without effecting any other vessels in the simulation.

The next step is to take all those names/numbers that we declared in our LM_sounds header file and tie them to actual audio files.

Code:
// --------------------------------------------------------------
// Define sound effects
// --------------------------------------------------------------
void LM::DefineSounds (void)
{
	// OrbiterSound 4.0
	OrbiterSoundID = ConnectToOrbiterSoundDLL (GetHandle());			// Init sound
[COLOR="red"]	SoundOptionOnOff (OrbiterSoundID, PLAYCOUNTDOWNWHENTAKEOFF, FALSE);	// Disable the stock countdown... it's annoying :)

	RequestLoadVesselWave (OrbiterSoundID, SILENCE, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\silence.wav", DEFAULT);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_BTN, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\switch_button.wav", INTERNAL_ONLY);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_ROTARY, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\switch_rotary.wav", INTERNAL_ONLY);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_SWITCH, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\switch_click.wav", INTERNAL_ONLY);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_SWITCHGUARD, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\switch_guard.wav", INTERNAL_ONLY);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_HATCHOPEN, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\hatch_open.wav", BOTHVIEW_FADED_MEDIUM);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_HATCHCLOSE, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\hatch_close.wav", BOTHVIEW_FADED_MEDIUM);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_STAGESEP, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\stage_sep.wav", BOTHVIEW_FADED_MEDIUM);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_EXPLSVDEVICE, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\cad.wav", BOTHVIEW_FADED_MEDIUM);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_CAUTION, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\alarm_caution.wav", RADIO_SOUND);
	RequestLoadVesselWave (OrbiterSoundID, SOUND_WARNING, "Sound\\_CustomVesselsSounds\\UMMU_Apollo\\alarm_warning.wav", RADIO_SOUND);[/COLOR]
} // End "LM::DefineSounds"

NOTE: Exact syntax and use of these functions is explained in the OrbiterSound 4.0 documentation. Your file paths may vary. Make sure you enter the correct file path/name FOR YOU rather than just copy/pasting. Any crashes/errors that result are your problem. :compbash2:

Ok now that we have our sound files loaded and we've given them names it's time to actually play one.

Lets go back to our old friend the Hatch...

Code:
void LM::clbkPostStep (double simt, double simdt, double mjd)
{
	// 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
			[COLOR="red"]if (hatch_proc < 0.2) PlayVesselWave (OrbiterSoundID, SOUND_HATCHOPEN); // Play opening sound effect[/COLOR]
		}
		
		if (HatchStatus == CLOSING)				// if Status equals "CLOSING"...
		{
			hatch_proc -= delta;				// ...subtract it.
			[COLOR="red"]if (hatch_proc < 0.3) PlayVesselWave (OrbiterSoundID, SOUND_HATCHCLOSE); // Play closing/locking sound effect[/COLOR]
		}

		SetAnimation (anim_Hatch, hatch_proc);	// Apply process value to animation.
	} // End "if (HatchStatus > CLOSED)"

As you may recall from back in Part 7, 0.2 and 0.3 are the points in the open/closing animations where the hatch handle begins to turn as such this is the moment that I want my sound of the hatch latching and unlatching sound to play.

As such I set up a pair of simple "if" statements. while the hatch is closing if (hatch_proc < 0.2) Play sound effect, then likewise for opening.

Compile and test...

there's nothing to take a screenshot shot of but you should now hear the hatch open and close when you press the k key.

This concludes PART 11.

Further information/tutorials on using the various features of OrbiterSound can be found in the OrbiterSound documentation and SDK samples.

In PART 12 I will start to discuss virtual cockpits, Followed by sub-system modelling. (making all those switches and buttons do something)

For those that requested thrust vectoring. It is on the to-do list but I see no point in coding an advanced thruster management system if there is no way to control that system.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
So let's add a virtual cockpit.

Virtual cockpits can dramatically increase the immersion factor of a simulation and a custom cockpit and/or 2d panel is an absolute ust for any vessel that with advanced subsystem modeling. The historical LM had over 160 switches and displays spread across 14 panels and we've only got so many keyboard commands to work with. :lol:

PART 12: Adding a virtual cockpit.

First thing first, we need to declare a mesh for our VC. Add a new mesh handle and index to your class interface...

Code:
	// Meshes
	MESHHANDLE	mh_ascent,[COLOR="red"] mh_cockpit,[/COLOR] mh_descent;	// Mesh handles
	UINT		mesh_Descent;			// Descent stage mesh index
	UINT		mesh_Ascent;			// Ascent stage mesh index
	[COLOR="Red"]UINT		mesh_Cockpit;			// Virtual cockpit mesh index[/COLOR]

... and load it in your constructor.

Code:
	// Load meshes
	mh_descent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_DescentStage");
	mh_ascent		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_AscentStage");
[COLOR="red"]	mh_cockpit		= oapiLoadMeshGlobal("UMMU_Apollo/LEM_VC");[/COLOR]

Remember to make sure you've got the correct filepath.

Once you have the mesh loaded we need to add it to our vessel, let's go to clbkSetClassCaps and do so.

Code:
	// 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
	[COLOR="red"]mesh_Cockpit	= AddMesh (mh_cockpit, &LM_ASC_OFFSET);	// Virtual cockpit mesh[/COLOR]
NOTE: the VC and Ascent stage meshes were designed with matching coordinate frames so we use "LM_ASC_OFFSET" for both.

Compile and test...
picture.php


Outwardly there is no change, but if we zoom way in...

picture.php


We will see that the cockpit mesh is there, it's just covered. This is because orbiter ordinarily only renders a vessel's meshes while in an exterior view. What we need to do is tell orbiter that this is a VC mesh. In clbkSetClassCaps add the following lines.

Code:
	// 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
	mesh_Cockpit	= AddMesh (mh_cockpit, &LM_ASC_OFFSET);	// Virtual cockpit mesh

[COLOR="red"]	SetMeshVisibilityMode (mesh_Descent, MESHVIS_ALWAYS);	// Set desent stage mesh to be always visible, By default a vessel's own meshes are only visible when in an external view. I want the the descent stage's legs to be visible from the cockpit. 
	SetMeshVisibilityMode (mesh_Cockpit, MESHVIS_VC);		// Set cockpit mesh to be visible in virtual cockpit view [/COLOR]

The cockpit mesh will now only be rendered in cockpit views. Also, because I want the legs of the descent stage to be visible through the cockpit windows I've set the descent stage mesh so that it will always rendered regardless of view mode.

If you compile and test again you will see that the cockpit mesh has been vanished.

Now we need to enable the virtual cockpit mode in orbiter. To do this we need to overload yet more callback functions. Back in our class interface declare the following...

Code:
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


[COLOR="red"]	bool	clbkLoadVC (int id);													// Load virtual cockpit mode
	bool	clbkVCMouseEvent (int id, int event, VECTOR3 &p);						// Respond to virtual cockpit mouse events
	bool	clbkVCRedrawEvent (int id, int event, SURFHANDLE surf);					// Respond to virtual cockpit area redraw requests[/COLOR]

the purposes of these 3 functions should be apparent from thier names, if not read the comment along side ;)

Now add them to our source code as stubs

Code:
// --------------------------------------------------------------
// Load virtual cockpit mode
// --------------------------------------------------------------
bool LM::clbkLoadVC (int id)
{
	return true;
} // End "LM::clbkLoadVC"

// --------------------------------------------------------------
// Respond to virtual cockpit mouse events
// --------------------------------------------------------------
bool LM::clbkVCMouseEvent (int id, int event, VECTOR3 &p)
{
	return false;
} // End "LM::clbkLoadVC"

// --------------------------------------------------------------
// Respond to virtual cockpit area redraw requests
// --------------------------------------------------------------
bool LM::clbkVCRedrawEvent (int id, int event, SURFHANDLE surf)
{
	return false;
} // End "LM::clbkVCRedrawEvent"

NOTE: clbkLoadVC returns "true" because we want the VC mode to be enabled, clbkVCMouseEvent and clbkVCRedrawEvent return false because we have not given them anything to do yet.

Compile and test...

picture.php


We're making progress :thumbup: but the current camera position is a little awkward. lets define a new one or rather new ones.

You see, orbiter allows you define multiple VC positions and then cycle between them. In the DG you can cycle between the Pilot's position and the various passengers, in the stock Atlantis you can cycle between the pilot, co-pilot, and cargo bay views.

These views are differentiated by the "id" variable passed to clbkLoadVC by Orbiter's core.

For the LM I have 3 positions in mind as well, The Comander's view (Left window), LMP's view (Right window), and docking view (Looking up through rendevous window).

to declare our three views we will need to add a "switch (id)" statement to clbkLoadVC.

Code:
bool LM::clbkLoadVC (int id)
{

[COLOR="red"]	// Register Camera (view) properties
	switch (id)
	{
	case 0: // Commander's position
		break;

	case 1: // Pilot's position
		break;

	case 2: // Commander's position (looking up through COAS Reticle)
		break;
	} // end "switch (id)"[/COLOR]
 
	return true;
} // End "LM::clbkLoadVC"

Then for each case we need to declare 4 things...

The position of the of the camera, it's default direction, it's rotation range, and it's neighbours. The first three values should be self explanitory, the last one is used to select a view using the arrow keys. Pressing ctrl + the Left, Right, Up, or Down key in that view will select the listed adjacent view.

So lets get on with it...

Code:
	// Register Camera (view) properties
	switch (id)
	{
	case 0: // Commander's position
[COLOR="red"]		SetCameraOffset (_V(-0.57, 0.68, 1.12));				// Set camera position (x,y,z)
		SetCameraDefaultDirection (_V( 0, 0, 1));					// Set camera direction (x,y,z)
		SetCameraRotationRange (RAD*120, RAD*120, RAD*60, RAD*60);	// Set camera range of motion (Left, Right, Up, Down)
		oapiVCSetNeighbours (-1, 1, 2,-1);							// Set adjacent cameras (Left, Right, Top, Bottom)
		break;[/COLOR]

	case 1: // Pilot's position
[COLOR="red"]		SetCameraOffset (_V( 0.57, 0.68, 1.12));				// Set camera position (x,y,z)
		SetCameraDefaultDirection (_V( 0, 0, 1));					// Set camera direction (x,y,z)
		SetCameraRotationRange (RAD*120, RAD*120, RAD*60, RAD*60);	// Set camera range of motion (Left, Right, Up, Down)
		oapiVCSetNeighbours ( 0,-1,-1,-1);							// Set adjacent cameras (Left, Right, Top, Bottom)[/COLOR]
		break;

	case 2: // Commander's position (looking up through COAS Reticle)
[COLOR="red"]		SetCameraOffset (_V(-0.59, 0.68, 1.12));				// Set camera position (x,y,z)
		SetCameraDefaultDirection (_V( 0, 1, 0));					// Set camera direction (x,y,z)
		SetCameraRotationRange (RAD*15, RAD*15, RAD*15, RAD*15);	// Set camera range of motion (Left, Right, Up, Down)
		oapiVCSetNeighbours (-1, 1,-1, 0);							// Set adjacent cameras (Left, Right, Top, Bottom)[/COLOR]
		break;
	} // end "switch (id)"

Compile and test...

picture.php


:huh: Seems we forgot to account for the offset position of our mesh.

Now thanks to "shiftCG" our camera will be in the correct position if we jettison the Descent stage but seeing as lying on the floor for half the flight isn't really desireable lets add a variable to compensate for this. Go to the top of "clbkLoadVC" and add the following...

Code:
bool LM::clbkLoadVC (int id)
{
[COLOR="red"]	// Check CG offset 
	VECTOR3 ofs;
	if (CGshifted == false) ofs = LM_ASC_OFFSET;
	else ofs = _V( 0, 0, 0);[/COLOR]

	// Register Camera (view) properties
	switch (id)
	{

then add "ofs" to each of your camera positions

Code:
	case 0: // Commander's position
		SetCameraOffset (_V(-0.57, 0.68, 1.12)[COLOR="red"] + ofs[/COLOR]);				// Set camera position (x,y,z)
		SetCameraDefaultDirection (_V( 0, 0, 1));					// Set camera direction (x,y,z)
		SetCameraRotationRange (RAD*120, RAD*120, RAD*60, RAD*60);	// Set camera range of motion (Left, Right, Up, Down)
		oapiVCSetNeighbours (-1, 1, 2,-1);							// Set adjacent cameras (Left, Right, Top, Bottom)
		break;

Let's try that again...

picture.php


Much better :thumbup:

Check out the other views as well

picture.php

^ Pilot's view

picture.php


^ Docking view

This concludes part 12.

Next we will be talking about how to add a sub-class to your vessel, and registering VC active areas.
 
Last edited:

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
hey i have followed this tutorial and i can jettison the descent stage, but for some reason, when i close orbiter and open the (current State) the descent stage is still attached, but the descent stage that i detached is there to. it is obviously a problem with the save and load state function, but i cant see anywhere in the tutorial that it explains the staging in the save vessel state function... please help :cheers:



never mind, ive got it now, i just missed a step in clbkPreStep :)

---------- Post added at 08:23 AM ---------- Previous post was at 04:58 AM ----------

i have followed all the steps in the tutorial, but for some reason, th rcs engines dont work??? what am i doing wrong?

also, none of the autopilots work, nor does the lander undock when i press Ctrl+D.... does anyone else get this??
 
Last edited:

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Don't know about the docking issue, chances are that it's an issue with clbkconsumebufferedkey, make sure it returns a value even when a key is not being pressed (return 0;).

As for RCS thruster groups, make sure that they have been defined.

I assigned temporary groups back in clbkpostcreation but the final config will have to wait till the VC is done which means after I get some time off from school.
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Don't know about the docking issue, chances are that it's an issue with clbkconsumebufferedkey, make sure it returns a value even when a key is not being pressed (return 0;).

As for RCS thruster groups, make sure that they have been defined.

I assigned temporary groups back in clbkpostcreation but the final config will have to wait till the VC is done which means after I get some time off from school.

yeah, stupid mistake, you were right, i just forgot to return at the end of ConsumeBufferedKey

thanks :cheers:
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I've been putting of writing this part because it's complicated and it involves a bunch of programming that does not directly apply to Orbiter. That said I really need to post something before my "in-progress point" goes too much further, so without further ado...

PART 13: Classes and Objects
In C++ a class is an expanded concept of a data structure that can hold both data and functions. An object is specific instance of a class. In terms of variables, a class would be the type, and an object would be the variable. Classes and the individual objects/instances within that class exist independently of each other but can interact via a class interface.

Every vessel that exists within an Orbiter simulation state is an instance of the Orbiter API "VESSEL" class. These instances are then assigned a sub-class that details the vessel's specific properties. If you look in our header file, at the top of our class declaration you will see these lines...

Code:
// ==============================================================
// Lunar Module class interface
// ==============================================================

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

...They declare our new "LM" class as a new sub-class/object of orbiter's VESSEL3 class. This is what tells Orbiter to treat our dll as a spcaecraft rather than a planet or MFD. It's also what allows our lunar lander to access the Orbiter API's default functions as well as have things like thrusters, propellant tanks, and animations, which are themselves extensions of the "VESSEL" parent class.

For 90% of all orbiter vessel addons this is all you really need to know.

Unfortunatly, I am not writing a 90% addon. Looking at our virtual cockpit you will see that we have a lot of switches and displays. 182 of them to be precise, and each one will need it's own sub-function if we want them to be interactive. Obviously, our vessel's class declaration would rapidly become large and unwieldy if we were to add all of these functions and their associated variables to it directly. As such I have decided to to add two member classes to our vessel. One to handle user inputs and cockpit displays, and another to handle environmental and subsystem modeling.

NOTE:
I want to make it absolutely clear that this is not required. For something less complex, like the stock Delta Glider or ShuttleA, this is overkill. If you can save yourself the trouble by all means do so.


That said, knowing how to do this may come in handy so lets get to it.

the first step is to declare a "friend class" in our vessel's class interface.

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

[COLOR="Red"]	// Member classes
	friend class	LM_COCKPIT;
	LM_COCKPIT		*vc;					// Virtual cockpit interface[/COLOR]

	// Custom vessel functions
	void	DefineAnimations (void);												// Define mesh animations

Declaring a class as a "friend" of another allows instances of those two classes to share data with each other.

Next we need to add a Header and a Source file for our new class, I called mine "LM_cockpit" .h and .cpp.

In my LM_Cockpit.h I declare our new class along with some of the basic functions I know I will be needing.

Code:
// ==============================================================
// Class declaration
// ==============================================================
class LM_COCKPIT 
{
friend class LM;

public:
	LM_COCKPIT (LM *vessel);
	~LM_COCKPIT ();

	inline const LM *GetVessel() const { return v; } // Return a vessel interface for the LM to which this cockpit belongs 

	// Public Functions
	void	InitVC (UINT mesh);										// Load VC animations, bitmaps and initial VC state
	void	RegisterActiveAreas (VECTOR3 ofs);						// Register VC active areas
	bool	MouseEvent (int id, int event, VECTOR3 &p);				// Respond to user inputs
	bool	RedrawEvent (int id, int event, SURFHANDLE surf);		// Respond to redraw requests
	bool	ParseScenarioLine (char *line);							// Read status from scenario file
	void	SaveState (FILEHANDLE scn);								// Write status to scenario file
	void	PreStep (double simt, double simdt, double mjd);		// Pre-Step processes
	void	PostStep (double simt, double simdt, double mjd);		// Post-Step processes

private:
	LM	*v;							// vessel interface

};

Next I add the constructor and destructor of my cockpit class along with stubs for all my functions to LM_Cockpit.cpp.

Code:
// ==============================================================
//                 Orbiter Module: LM_Cockpit
//    part of the Apollo Applications Project for Orbiter (AAPO)
//               copyright (c) 2013 Greg Hlynka
//                    all rights reserved
//
// LM_Cockpit.cpp
// control module for AAPO Lunar Module's virtual cockpit and displays
//
// notes: Writing tutorials is hard
// ==============================================================

#include "orbitersdk.h"
#include "LM.h"
#include "LM_Cockpit.h"

// ==============================================================
// LM Cockpit class interface
// ==============================================================

// --------------------------------------------------------------
// Constructor
// --------------------------------------------------------------
LM_COCKPIT::LM_COCKPIT (LM *vessel)
{
	v = vessel;
	int i = 0;
}

// --------------------------------------------------------------
// Destructor
// --------------------------------------------------------------
LM_COCKPIT::~LM_COCKPIT ()
{
}

// ==============================================================
// Public functions
// ==============================================================
*Function Stubs Go Here*

// ==============================================================
// Private functions
// ==============================================================
*More Stubs*

At this point you should probably stop and make sure that everything compiles.

I also want to draw attention the "LM *vessel" initializer in our class constructor, this is how we'll need this to attach the individual cockpit instance to its specific Lunar module.

Assuming everything works, it's now time to add an instance of our cockpit class to our LM. Because we want a LM Cockpit for every LM in the scenario we will add the call to create a new cockpit to our LM's constructor. This way orbiter will create a cockpit instance and corresponding interface for it each time a LM instance is created.

Code:
// --------------------------------------------------------------
// Constructor
// --------------------------------------------------------------
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;

[COLOR="red"]	// Load sub classes
	vc = new LM_COCKPIT (this);	// Cockpit class
[/COLOR]...

"this" in the cockpit class call is refers to the specific LM instance being constructed and gets passed to the "LM *vessel" initializer I pointed out earlier. The LM and it's cockpit are now linked, and Cockpit class functions can be accessed using the "vc" interface.

Now that we have provisions for creating cockpits we need a provission for destroying one. Add the following line to our LM's class destructor.

Code:
// --------------------------------------------------------------
// Destructor
// --------------------------------------------------------------
LM::~LM ()
{
	delete	vc;
}

Otherwise if our LM were to be deleted from the simulation the cockpit class would continue to float around taking up memory and processing power. Remember kids, only you can prevent memory leaks!

Now that the ground work has been laid we can start doing things with our shiny new cockpit class. To access the cockpit class' functions and variables from within the LM simply use the vc interface declared above and demonstrated below...

Code:
// --------------------------------------------------------------
// Post-Step processes
// --------------------------------------------------------------
void LM::clbkPostStep (double simt, double simdt, double mjd)
{
[COLOR="red"]	vc->PostStep (simt, simdt, mjd);
[/COLOR]

Compile and make sure you didn't break anything.

In part 14 we'll learn how to make our new cockpit class actually do sh*t.

---------- Post added 03-26-13 at 11:26 ---------- Previous post was 03-25-13 at 13:20 ----------

PART 14: Making the Cockpit Interactive

So last we left off we had a cockpit and a shiny cockpit control class that didn't actually do anything. Now it's time to do stuff.

VC displays and user inputs are handled by "Active areas" that are registered when the VC is loaded. To generate these active area s I have created a function called "RegisterActiveAreas" in our cockpit control class. (See part 13) this function is a void and takes a single VECTOR3 variable called "ofs" as an argument. If you look at the top of our "clbkLoadVC" back in the vessel class itself I think you can figure out what it's for.

Anyway, in order to function each active area needs to be assigned an index value and a location within the cockpit. The index value is a base 8 intiger and the location a VECTOR3. We could declare these Individually but for the sake of my sanity and because there are a whole lot of active areas in our cockpit I will be putting as much as I can into the Cockpit header file.

Observe...
Code:
// ==============================================================
// Active Area Definitions
// ==============================================================

// Multi Function Display indexes
#define CmdMFD					00		// Primary (Panel 4) MFD

// Main Panel Displays and Gauges 
#define AID_LCD_DATETIME		0001	// Main panel digital clock
#define AID_LCD_FUEL			0002	// Main engine digital propellant readout
#define AID_LCD_RCS				0003	// RCS thruster digital propellant readout
#define AID_LCD_DELTAV			0004	// dV digital display
#define AID_GAUGE_ALTRANGE		0005	// Altitude/Range indicator
#define AID_GAUGE_RANGERATE		0006	// Range Rate indicator
#define AID_GAUGE_VACC			0007	// Vertical acceleration (T/W) indicator

#define AID_CAUTIONADVISORY		0010	// Caution/Warning/Advisory (CWA) Display
#define AID_P1_MASTERCAUTION	0011	// Commander's master Caution Indicator
#define AID_P2_MASTERCAUTION	0012	// Pilot's master Caution Indicator
#define AID_P1_XPOINTER			0013	// Commander's ground-speed/slip indicator
#define AID_P2_XPOINTER			0014	// Pilot's ground-speed/slip indicator
#define AID_P1_ADIBALL			0015	// Commander's Attitude/Direction indicator
#define AID_P2_ADIBALL			0016	// Pilot's Attitude/Direction indicator

// EVA Hatch
#define AID_EVAHANDLE_OPEN		0020
#define AID_EVAHANDLE_CLOSE		0021
#define AID_EVA_EGRESS			0022

// VC Active Areas (Panel 1)
#define AID_PANEL_1				((id >= 0100) && (id <= 0124))
#define AID_SWITCH_P1_00		0100
#define AID_SWITCH_P1_01		0101
#define AID_SWITCH_P1_02		0102
#define AID_SWITCH_P1_03		0103	// Engine arm
#define AID_SWITCH_P1_04		0104	
#define AID_SWITCH_P1_05		0105	// Throttle Control Mode
#define AID_SWITCH_P1_06		0106	
#define AID_SWITCH_P1_07		0107	// CDR's ACA prop

#define AID_SWITCH_P1_08		0110	// Manual control select
#define AID_SWITCH_P1_09		0111
#define AID_SWITCH_P1_10		0112
#define AID_SWITCH_P1_11		0113	// Ascent He REG 1
#define AID_SWITCH_P1_12		0114	// Descent He REG 1
#define AID_SWITCH_P1_13		0115	// Ascent He REG 2
#define AID_SWITCH_P1_14		0116	// Descent He REG 2
#define AID_SWITCH_P1_15		0117

#define AID_SWITCH_P1_16		0120
#define AID_SWITCH_P1_17		0121	// Guidance control
#define AID_SWITCH_P1_18		0122	// Altimeter mode select
#define AID_SWITCH_P1_19		0123
#define AID_DIAL_P1_00			0124

#define AID_BTN_P1_ABORT		0007	// Docking latch release button
#define AID_BTN_P1_ABORTSTAGE	0010	// Abort Stage button

// VC Active Areas (Panel 2)
#define AID_PANEL_2				((id >= 0200) && (id <= 0226))
#define AID_SWITCH_P2_00		0201	// RCS "A" propellant shut off valve
#define AID_SWITCH_P2_01		0202	// RCS "A" quad-1
#define AID_SWITCH_P2_02		0203	// RCS "A" quad-2
#define AID_SWITCH_P2_03		0204	// RCS A/B cross-feed valve
#define AID_SWITCH_P2_04		0205	// RCS "A" oxidizer shut off valve
#define AID_SWITCH_P2_05		0206	// RCS "A" quad-4
#define AID_SWITCH_P2_06		0207	// RCS "A" quad-3
#define AID_SWITCH_P2_07		0210	// RCS "A" ascent tank cross-feed valve
#define AID_SWITCH_P2_08		0211	// RCS "B" propellant shut off valve
#define AID_SWITCH_P2_09		0212	// RCS "B" quad-1
#define AID_SWITCH_P2_10		0213	// RCS "B" quad-2
#define AID_SWITCH_P2_11		0214	// RCS "B" oxidizer shut off valve
#define AID_SWITCH_P2_12		0215	// RCS "B" quad-4
#define AID_SWITCH_P2_13		0216	// RCS "B" quad-3
#define AID_SWITCH_P2_14		0217	// RCS "B" ascent tank cross-feed valve
#define AID_SWITCH_P2_15		0220	// LMP's ACA prop
#define AID_SWITCH_P2_16		0221	// ADI Rate Scale
#define AID_SWITCH_P2_17		0222

#define AID_DIAL_P2_00			0223
#define AID_DIAL_P2_01			0224
#define AID_DIAL_P2_02			0225
#define AID_DIAL_P2_03			0226

// VC Active Areas (Panel 3)
#define AID_PANEL_3				((id >= 0300) && (id <= 0337))
#define AID_SWITCH_P3_01		0301	// Engine gimbal
#define AID_SWITCH_P3_02		0302	// Command overide
#define AID_SWITCH_P3_03		0303
#define AID_SWITCH_P3_04		0304
#define AID_SWITCH_P3_05		0305
#define AID_SWITCH_P3_06		0306	// Dead band select
#define AID_SWITCH_P3_07		0307	// Pitch control mode
#define AID_SWITCH_P3_09		0310
#define AID_SWITCH_P3_10		0311	// Roll control mode
#define AID_SWITCH_P3_12		0312	// Event timer start
#define AID_SWITCH_P3_13		0313	// Yaw control mode
#define AID_SWITCH_P3_14		0314
#define AID_SWITCH_P3_15		0315	// Event timer mode
#define AID_SWITCH_P3_16		0316	
#define AID_SWITCH_P3_17		0317	// Quad 1 heater
#define AID_SWITCH_P3_18		0320	// Quad 2 heater
#define AID_SWITCH_P3_19		0321	// Event timer slew (min)
#define AID_SWITCH_P3_20		0322	// Quad 4 heater
#define AID_SWITCH_P3_21		0323	// Quad 3 heater
#define AID_SWITCH_P3_22		0324	// Event timer slew (sec)
#define AID_SWITCH_P3_23		0325
#define AID_SWITCH_P3_24		0326
#define AID_SWITCH_P3_25		0327

#define AID_DIAL_P3_00			0332
#define AID_DIAL_P3_01			0333
#define AID_DIAL_P3_02			0334	// Attitude control mode
#define AID_DIAL_P3_03			0335
#define AID_DIAL_P3_04			0336
#define AID_DIAL_P3_05			0337

// VC Active Areas (Panel 4)
#define AID_PANEL_4				((id >= 0400) && (id <= 0404))
#define AID_SWITCH_P4_00		0401	// CDR's RCS vernier
#define AID_SWITCH_P4_01		0402	// CDR's RCS mode select
#define AID_SWITCH_P4_02		0403	// LMP's RCS vernier
#define AID_SWITCH_P4_03		0404	// LMP's RCS mode select

#define AID_MFD_P4_LBUTTONS		0411	// MFD soft-keys (Left side)
#define AID_MFD_P4_RBUTTONS		0412	// MFD soft-keys (Right side)
#define AID_MFD_P4_PWR			0413	// MFD 'Power' button
#define AID_MFD_P4_SEL			0414	// MFD 'Select' button
#define AID_MFD_P4_MNU			0415	// MFD 'Menu' button

// VC Active Areas (Panel 5)
#define AID_PANEL_5				((id >= 0500) && (id <= 0516))
#define AID_SWITCH_P5_00		0501	// Mission timer mode
#define AID_SWITCH_P5_01		0502	
#define AID_SWITCH_P5_02		0503	// Mission timer slew (hrs)
#define AID_SWITCH_P5_03		0504	
#define AID_SWITCH_P5_04		0505	// Mission timer slew (min)
#define AID_SWITCH_P5_05		0506
#define AID_SWITCH_P5_06		0507	// Mission timer slew (sec)
#define AID_SWITCH_P5_07		0510

#define AID_BTN_P5_XTRANS		0511	// Ullage burn
#define AID_BTN_P5_START		0512	// Start engine
#define AID_BTN_P5_STOP			0513	// Kill thrust

#define AID_DIAL_P5_00			0514	// Floodlights
#define AID_DIAL_P5_01			0515	// Panel lights
#define AID_DIAL_P5_02			0516	// Displays

// VC Active Areas (Panel 6)
#define AID_PANEL_6				((id >= 0600) && (id <= 0626))
#define AID_SWITCH_P6_00		0600	// Gyro 1
#define AID_SWITCH_P6_01		0601	// Gyro 1
#define AID_SWITCH_P6_02		0602	// Gyro 2
#define AID_SWITCH_P6_03		0603	// Gyro 2
#define AID_SWITCH_P6_04		0604	// Gyro 1
#define AID_SWITCH_P6_05		0605	// Gyro 1
#define AID_SWITCH_P6_06		0606	// Gyro 2
#define AID_SWITCH_P6_07		0607	// Gyro 2

#define AID_SWITCH_P6_08		0610	// Gyro 1
#define AID_SWITCH_P6_09		0611	// Gyro 1
#define AID_SWITCH_P6_10		0612	// Gyro 2
#define AID_SWITCH_P6_11		0613	// Gyro 2

#define AID_DIAL_P6_00			0623	// Gyro 1, Ref Frame select
#define AID_DIAL_P6_01			0624	// Gyro 2, Ref Frame select
#define AID_DIAL_P6_02			0625	// Gyro 1, Target mode select
#define AID_DIAL_P6_03			0626	// Gyro 2, Target mode select

// Projection mode
	// Primary Axis 
	// Set/Clear offset
	// Nav Channel select

// VC Active Areas (Panel 8)
#define AID_PANEL_8				((id >= 1000) && (id <= 1023))
#define AID_SWITCH_P8_00		1001	
#define AID_SWITCH_P8_01		1002	
#define AID_SWITCH_P8_02		1003	
#define AID_SWITCH_P8_03		1004	 
#define AID_SWITCH_P8_04		1005
#define AID_SWITCH_P8_05		1006
#define AID_SWITCH_P8_06		1007
#define AID_SWITCH_P8_07		1008
#define AID_SWITCH_P8_08		1009
#define AID_SWITCH_P8_09		1010
#define AID_SWITCH_P8_10		1011
#define AID_SWITCH_P8_11		1012
#define AID_SWITCH_P8_12		1013
#define AID_SWITCH_P8_13		1014
#define AID_SWITCH_P8_14		1015
#define AID_SWITCH_P8_15		1016
#define AID_SWITCH_P8_16		1017
#define AID_SWITCH_P8_17		1018
#define AID_SWITCH_P8_18		1019
#define AID_SWITCH_P8_19		1020
#define AID_SWITCH_P8_20		1021
#define AID_SWITCH_P8_21		1022
#define AID_SWITCH_P8_22		1023

// VC Active Areas (Panel 12)
#define AID_PANEL_12			((id >= 1200) && (id <= 1226))
#define AID_SWITCH_P12_00		1200	
#define AID_SWITCH_P12_01		1201	
#define AID_SWITCH_P12_02		1202	
#define AID_SWITCH_P12_03		1203	 
#define AID_SWITCH_P12_04		1204
#define AID_SWITCH_P12_05		1205
#define AID_SWITCH_P12_06		1206
#define AID_SWITCH_P12_07		1207
#define AID_SWITCH_P12_08		1208
#define AID_SWITCH_P12_09		1209
#define AID_SWITCH_P12_10		1210
#define AID_SWITCH_P12_11		1211
#define AID_SWITCH_P12_12		1212
#define AID_SWITCH_P12_13		1213
#define AID_SWITCH_P12_14		1214
#define AID_SWITCH_P12_15		1215
#define AID_SWITCH_P12_16		1216
#define AID_SWITCH_P12_17		1217
#define AID_SWITCH_P12_18		1218
#define AID_SWITCH_P12_19		1219
#define AID_SWITCH_P12_20		1220
#define AID_SWITCH_P12_21		1221
#define AID_SWITCH_P12_22		1222

#define AID_DIAL_P12_00			1223
#define AID_DIAL_P12_01			1224
#define AID_DIAL_P12_02			1225
#define AID_DIAL_P12_03			1226

// VC Active Areas (Panel 14)
#define AID_PANEL_14			((id >= 1400) && (id <= 1416))
#define AID_SWITCH_P14_00		1400	
#define AID_SWITCH_P14_01		1401	
#define AID_SWITCH_P14_02		1402	
#define AID_SWITCH_P14_03		1403	
#define AID_SWITCH_P14_04		1404	
#define AID_SWITCH_P14_05		1405	
#define AID_SWITCH_P14_06		1406	
#define AID_SWITCH_P14_07		1407	
#define AID_SWITCH_P14_08		1408	
#define AID_SWITCH_P14_09		1409	
#define AID_SWITCH_P14_10		1410	
#define AID_SWITCH_P14_11		1411	
#define AID_SWITCH_P14_12		1412	
#define AID_SWITCH_P14_13		1413	
#define AID_SWITCH_P14_14		1414	
#define AID_SWITCH_P14_15		1415

#define AID_DIAL_P14_00			1416

// ==============================================================
// VC Constants
// ==============================================================

const double P1_TILT	= 8*RAD;
const double P2_TILT	= 8*RAD;
const double P3_TILT	= 35*RAD;
const double P4_TILT	= 45*RAD;
const double P6_TILT	= 10*RAD;
const double P12_TILT	= 20*RAD;
const double P14_TILT	= 25*RAD;

// Number of switches on each panel
const int	 P1_NSWITCH		= 20;
const int	 P2_NSWITCH		= 18;
const int	 P3_NSWITCH		= 23;
const int	 P4_NSWITCH		= 4;
const int	 P5_NSWITCH		= 8;
const int	 P6_NSWITCH		= 12;
const int	 P8_NSWITCH		= 22;
const int	 P12_NSWITCH	= 22;
const int	 P14_NSWITCH	= 16;

// Number of dials/thumbwheels
const int	 P1_NDIAL		= 1;
const int	 P2_NDIAL		= 4;
const int	 P3_NDIAL		= 6;
const int	 P4_NDIAL		= 0;
const int	 P5_NDIAL		= 1;
const int	 P6_NDIAL		= 4;
const int	 P8_NDIAL		= 0;
const int	 P12_NDIAL		= 4;
const int	 P14_NDIAL		= 1;

// Dial rotation axises
const VECTOR3	P1_DIAL_AXIS	= { 0.00, sin(P1_TILT),-cos(P1_TILT)};
const VECTOR3	P2_DIAL_AXIS	= { 0.00, sin(P2_TILT),-cos(P2_TILT)};
const VECTOR3	P3_DIAL_AXIS	= { 0.00, cos(P3_TILT),-cos(P3_TILT)};	
const VECTOR3	P6_DIAL_AXIS	= { 0.00, cos(P6_TILT),-sin(P6_TILT)};	
const VECTOR3	P12_DIAL_AXIS	= {-sin(P12_TILT), cos(P12_TILT), 0.00};
const VECTOR3	P14_DIAL_AXIS	= { sin(P14_TILT), cos(P14_TILT), 0.00};

// ==============================================================
// Positions of active areas in VC mesh
// ==============================================================

const VECTOR3	ADI_BALL_POS[2]			= { {-0.29190, 0.63346, 1.76486}, { 0.30211, 0.63346, 1.76486}};
const VECTOR3	VC_HUD_POS				= {-0.59, 0.975,-1.12353};
const VECTOR3	MASTERCAUTION_POS[2]	= { {-0.41025, 0.68624, 1.70677}, { 0.42073, 0.69283, 1.70771}};
const VECTOR3	UNDOCK_BTN_POS			= {-0.09360, 0.53426, 1.68610};
const VECTOR3	ABORTSTAGE_BTN_POS		= {-0.04435, 0.53426, 1.68610};
const VECTOR3	XTRANS_BTN_POS			= {-0.61200, 0.02649, 1.46188};
const VECTOR3	START_BTN_POS			= {-0.68256, 0.03258, 1.46172};
const VECTOR3	STOP_BTN_POS			= {-0.68256, 0.04126, 1.51096};
const VECTOR3	MFD_POS					= { 0.00000, 0.10972, 1.44827};

// MFD buttons (relative to MFD)
const VECTOR3 MFD_BUTTON_POS[11] = {
	{-0.11331, 0.06884,-0.01354}, {-0.09703, 0.06884,-0.01354}, {-0.11331,-0.07099,-0.01354}, {-0.09703,-0.07099,-0.01354}, // Quadrilateral describing left side soft-keys
	{ 0.09703, 0.06884,-0.01354}, { 0.11331, 0.06884,-0.01354}, { 0.09703,-0.07099,-0.01354}, { 0.11331,-0.07099,-0.01354}, // Quadrilateral describing right side soft-keys
	{-0.07291,-0.09406,-0.01354}, { 0.05361,-0.09406,-0.01354}, { 0.07337,-0.09406,-0.01354} };								// Position of Power, Select, and Menu buttons 

// Panel 1 Toggle-switchs
const VECTOR3 P1_TOGGLE_POS[P1_NSWITCH] = { 
	{-0.41112, 0.63679, 1.69711}, {-0.41112, 0.58858, 1.69117}, {-0.35903, 0.51596, 1.68097}, {-0.33970, 0.41121, 1.66625}, 
	{-0.31091, 0.51596, 1.68097}, {-0.31091, 0.45853, 1.67290}, {-0.29446, 0.41121, 1.66625}, {-0.26431, 0.51596, 1.68097}, 
	{-0.26431, 0.45853, 1.67290}, {-0.26039, 0.41121, 1.66625}, {-0.23584, 0.74792, 1.71273}, {-0.22377, 0.48943, 1.67724}, 
	{-0.22377, 0.41121, 1.66625}, {-0.17579, 0.48943, 1.67724}, {-0.17579, 0.41121, 1.66625}, {-0.14838, 0.47650, 1.67542}, 
	{-0.13043, 0.41994, 1.66747}, {-0.04043, 0.69083, 1.70554}, {-0.04043, 0.64232, 1.69873}, {-0.04043, 0.59233, 1.69170}};

// Panel 1 Dial
const VECTOR3 P1_DIAL_POS[P1_NDIAL] = { {-0.06803, 0.45938, 1.67554}};

// Panel 2 Toggle-switchs
const VECTOR3 P2_TOGGLE_POS[P2_NSWITCH] = { 
	{ 0.05041, 0.76698, 1.71625}, { 0.05041, 0.67718, 1.70362}, { 0.05041, 0.59878, 1.69261}, { 0.05098, 0.51056, 1.68021}, { 0.09446, 0.76698, 1.71625}, 
	{ 0.09446, 0.67718, 1.70362}, { 0.09446, 0.59878, 1.69261}, { 0.12440, 0.51056, 1.68021}, { 0.13811, 0.76698, 1.71625}, { 0.13811, 0.67718, 1.70362}, 
	{ 0.13811, 0.59878, 1.69261}, { 0.18217, 0.76698, 1.71625}, { 0.18217, 0.67718, 1.70362}, { 0.18217, 0.59878, 1.69261}, { 0.17825, 0.51056, 1.68021},
	{ 0.17825, 0.45658, 1.67262}, { 0.42048, 0.64216, 1.69870}, { 0.42048, 0.58858, 1.69117}};

// Panel 2 Dials
const VECTOR3 P2_DIAL_POS[P2_NDIAL] = {
	{ 0.05703, 0.45057, 1.67430}, { 0.24710, 0.49405, 1.68041}, { 0.24710, 0.42250, 1.67036}, { 0.36027, 0.45827, 1.67538}};

// Panel 3 Toggle-switchs
const VECTOR3 P3_TOGGLE_POS[P3_NSWITCH] = {
	{-0.35652, 0.32535, 1.62434}, {-0.36395, 0.28302, 1.59471}, {-0.31776, 0.32535, 1.62434}, {-0.31776, 0.28302, 1.59471}, 
	{-0.25709, 0.26603, 1.58281}, {-0.11335, 0.32744, 1.62581}, {-0.11335, 0.28496, 1.59606}, /*{-0.11335, 0.23648, 1.56212}, */
	{-0.06036, 0.32744, 1.62581}, {-0.06036, 0.28496, 1.59606}, /*{-0.06036, 0.23648, 1.56212},*/ {-0.00681, 0.32744, 1.62581}, 
	{-0.00681, 0.28496, 1.59606}, {-0.00681, 0.23648, 1.56212}, { 0.10519, 0.32744, 1.62581}, { 0.13944, 0.32744, 1.62581}, 
	{ 0.13944, 0.28127, 1.59348}, { 0.13944, 0.23958, 1.56429}, { 0.17233, 0.32744, 1.62581}, { 0.17233, 0.28127, 1.59348}, 
	{ 0.17233, 0.23958, 1.56429}, { 0.20522, 0.32744, 1.62581}, { 0.24006, 0.32744, 1.62581}, { 0.24006, 0.29890, 1.60583}, 
	{ 0.35709, 0.23510, 1.56115}
	};
	
// Panel 3 Dials
const VECTOR3 P3_DIAL_POS[P3_NDIAL] = { 
	{-0.33425, 0.23079, 1.56118}, {-0.19012, 0.24194, 1.56899}, {-0.08162, 0.23373, 1.56324}, { 0.06008, 0.24099, 1.56833}, 
	{ 0.26664, 0.23420, 1.56357}, { 0.30563, 0.29777, 1.60808}
	};

// Panel 4 Toggle-switchs
const VECTOR3 P4_TOGGLE_POS[P4_NSWITCH] = {
	{-0.12900, 0.14776, 1.48380}, {-0.12900, 0.08487, 1.42090}, { 0.13500, 0.14776, 1.48380}, { 0.13500, 0.08487, 1.42090}
	};

// Panel 5 Toggle-switchs
const VECTOR3 P5_TOGGLE_POS[P5_NSWITCH] = {
	{-0.59670, 0.03450, 1.49293}, {-0.55200, 0.02374, 1.43190}, {-0.54173, 0.03450, 1.49293}, {-0.51697, 0.02374, 1.43190},
	{-0.49969, 0.03450, 1.49293}, {-0.48194, 0.02374, 1.43190}, {-0.45762, 0.03450, 1.49293}, {-0.44691, 0.02374, 1.43190},
	};

// Panel 6 Toggle-switchs
const VECTOR3 P6_TOGGLE_POS[P6_NSWITCH] = {
	{ 0.64043, 0.03879, 1.51246}, { 0.64043, 0.03011, 1.46319}, { 0.64043, 0.02092, 1.41107}, { 0.64043, 0.01223, 1.36180},
	{ 0.67543, 0.03879, 1.51246}, { 0.67543, 0.03011, 1.46319}, { 0.67543, 0.02092, 1.41107}, { 0.67543, 0.01223, 1.36180},
	{ 0.71042, 0.03879, 1.51246}, { 0.71042, 0.03011, 1.46319}, { 0.71042, 0.02092, 1.41107}, { 0.71042, 0.01223, 1.36180},
	};

// Panel 6 Dials
const VECTOR3 P6_DIAL_POS[P6_NDIAL] = {
	{ 0.49704, 0.03177, 1.49183}, { 0.49704, 0.01389, 1.39044}, { 0.58025, 0.03177, 1.49183}, { 0.58025, 0.01389, 1.39044}
	};

// Panel 8 Toggle-switchs
const VECTOR3 P8_TOGGLE_POS[P8_NSWITCH] = { 
	{-1.07517, 0.18985, 1.04111}, {-1.07517, 0.18985, 1.07512}, {-0.98428, 0.15676, 1.08001}, {-1.09473, 0.19697, 1.11613},
	{-0.98428, 0.15676, 1.11001}, {-1.04353, 0.17833, 1.18348}, {-0.98996, 0.15883, 1.18348}, {-1.09335, 0.19646, 1.21348},
	{-1.04353, 0.17833, 1.21348}, {-1.09335, 0.19646, 1.24348}, {-1.04353, 0.17833, 1.24348}, {-0.99655, 0.16123, 1.24348},
	{-1.09335, 0.19646, 1.27348}, {-1.04353, 0.17833, 1.27348}, {-0.99655, 0.16123, 1.27348}, {-1.10953, 0.20235, 1.35700},
	{-1.02496, 0.17157, 1.35700}, {-1.10953, 0.20235, 1.39700}, {-1.02496, 0.17157, 1.39700}, {-1.07194, 0.18867, 1.44199},
	{-1.07194, 0.18867, 1.48199}, {-0.98159, 0.15579, 1.49572}
	};

// Panel 12 Toggle-switchs
const VECTOR3 P12_TOGGLE_POS[P12_NSWITCH] = {
	{ 1.01020, 0.07249, 1.51678}, { 1.08673, 0.10035, 1.49908}, { 1.12601, 0.11465, 1.45174}, { 1.04277, 0.08435, 1.45174},
	{ 1.12601, 0.11465, 1.39949}, { 1.04277, 0.08435, 1.39949}, { 1.12734, 0.11513, 1.34949}, { 1.08975, 0.10145, 1.34949},
	{ 1.10855, 0.10829, 1.30949}, { 1.10855, 0.10829, 1.26949}, { 1.05714, 0.08958, 1.26396}, { 1.10855, 0.10829, 1.22949},
	{ 1.05714, 0.08958, 1.22396}, { 1.11352, 0.11010, 1.18210}, { 1.05714, 0.08958, 1.18927}, { 1.05714, 0.08958, 1.14927},
	{ 1.11352, 0.11010, 1.13210}, { 1.05714, 0.08958, 1.12073}, { 1.11352, 0.11010, 1.09210}, { 1.05714, 0.08958, 1.08073},
	{ 1.00075, 0.06906, 1.08073}, { 1.05714, 0.08958, 1.04375}
	};

// Panel 12 Dials
const VECTOR3 P12_DIAL_POS[P12_NDIAL] = { 
	{ 1.01313, 0.07094, 1.02660}, { 1.01313, 0.07094, 0.95302}, { 1.10485, 0.10432, 0.88523}, { 1.02386, 0.07484, 0.88523}
	};

// Panel 14 Toggle-switchs
const VECTOR3 P14_TOGGLE_POS[P14_NSWITCH] = { 
	{ 1.03621, 0.31420, 1.32009}, { 1.03621, 0.31420, 1.25009}, { 1.07247, 0.33111, 1.18009}, { 1.03621, 0.31420, 1.18009},
	{ 1.07247, 0.33111, 1.15009}, { 1.07247, 0.33111, 1.11326}, { 1.03621, 0.31420, 1.11326}, { 1.07247, 0.33111, 1.08326},
	{ 1.03621, 0.31420, 1.08326}, { 1.07247, 0.33111, 1.05326}, { 1.03621, 0.31420, 1.05326}, { 1.06281, 0.32661, 1.00857},
	{ 1.06281, 0.32661, 0.96491}, { 1.06281, 0.32661, 0.92641}, { 1.06281, 0.32661, 0.88962}, { 1.06281, 0.32661, 0.83221}
	};

// Panel 14 Dial
const VECTOR3 P14_DIAL_POS[P14_NDIAL] = {{ 1.08456, 0.33399, 1.25001}};

// ==============================================================
// Class declaration
// ==============================================================
class LM_COCKPIT 
{

That is a list of every switch, display, and button in our cockpit. each with a unique index number and 3D position assigned to it.

Yes it's a lot of information, but defining it once in the header file allows us to access it at will and will make what comes next a lot easier. Lets start with something simple, making the door open.

Open our RegisterActiveAreas function and add the following lines...

Code:
// --------------------------------------------------------------
// Register VC active areas
// --------------------------------------------------------------
void LM_COCKPIT::RegisterActiveAreas (VECTOR3 ofs)
{
	int i = 0;

[COLOR="Red"]	// EVA hatch handle
	oapiVCRegisterArea (AID_EVAHANDLE_OPEN, PANEL_REDRAW_NEVER, PANEL_MOUSE_DOWN);
	oapiVCSetAreaClickmode_Spherical( AID_EVAHANDLE_OPEN, _V(-0.3356, -0.6232, 1.5988) + ofs, 0.08);

	oapiVCRegisterArea (AID_EVAHANDLE_CLOSE, PANEL_REDRAW_NEVER, PANEL_MOUSE_DOWN);
	oapiVCSetAreaClickmode_Spherical (AID_EVAHANDLE_CLOSE, _V( 0.3300, -0.4888, 0.9368) + ofs, 0.08);[/COLOR]

What we are doing here is creating two active areas. thier postions correlate to the positions of the EVA hatch's door handle when the door is open and when the door is closed. Let's break them down.

"oapiVCRegisterArea (AID_EVAHANDLE_OPEN, PANEL_REDRAW_NEVER, PANEL_MOUSE_DOWN);
"

This line is the initial declaration of the active area. "AID_EVAHANDLE_OPEN" is the index number as defined in our header file, "PANEL_REDRAW_NEVER" indicates that there are no "redraw events" associated with the active area. Redraw events are how the VC updates things like gauges and other displays. "PANEL_MOUSE_DOWN" means that the active area is activated when someone clicks on it (mouse button is down).

"oapiVCSetAreaClickmode_Spherical (AID_EVAHANDLE_CLOSE, _V( 0.3300,-0.4888, 0.9368) + ofs, 0.08);"

This line states what kind of active area we are dealing with and it's posittion within the cockpit. In this case the active area is a spherical zone 0.08 units in diameter (8 cm) at the position indicated.

Now that we have an active area it's time to assign a function to it.

In the vessel class itself parsing of cockpit inputs is handled by "clbkVCMouseEvent" to serve this purpose you will see that I have placed a function called "MouseEvent" in our cockpit control class.

In it I have placed the following lines...

Code:
// --------------------------------------------------------------
// Respond to user inputs
// --------------------------------------------------------------
bool LM_COCKPIT::MouseEvent (int id, int event, VECTOR3 &p)
{
[COLOR="Red"]	switch (id) 
	{
	// EVA hatch handle
	case AID_EVAHANDLE_OPEN:
		if (v->HatchStatus == CLOSED) v->HatchStatus = OPENING; // If hatch is closed open hatch
		return true;

	case AID_EVAHANDLE_CLOSE:
		if (v->HatchStatus == OPEN) v->HatchStatus = CLOSING; // If hatch is open close hatch
		return true;
}[/COLOR]
return false;

When the user clicks on an active area Orbiter automatically passes the index number of the area to the mouse event parser, the "id" variable in the above code. The parser then compares that id number to it's list of possibles contained in the "switch (id)" function. If it finds a match it performs the assigned function and returns true, otherwise it returns false.

In this case the functions and their purpose should be readily apparent.

NOTE: v-> is a interface that allows us to manipulate data/call functions from our parent vessel as described in part 13.

Now that we have the some active areas registered and some lines for our parser to chew on we need to add them to our simulation. Our cockpit is not actually a vessel, planet, mfd, or somesuch and as such Orbiter will ignore it unless we specifically tell it otherwise.

To do this we need to go back to our lunar module's code and add calls for the cockpit's functions.

In ClbkLoadVC add a call to register our active areas.

Code:
// --------------------------------------------------------------
// Load virtual cockpit mode
// --------------------------------------------------------------
bool LM::clbkLoadVC (int id)
{
	// Check CG offset 
	VECTOR3 ofs;
	if (CoG_shifted) ofs = _V( 0, 0, 0);
	else ofs = LM_ASC_OFFSET; 

	// Register MFD
	static VCMFDSPEC Cmd_MFD = {mesh_Cockpit, VC_GRP_MFD_Cmd};		// Surface on which to display MFD. (Mesh, Mesh Group)
	oapiVCRegisterMFD (0, &Cmd_MFD);								// Register MFD in orbiter's interface [registry # and VCMFDSPEC]. (registry numbers start at 0 and count up)

[COLOR="red"]	// Register active areas
	vc->RegisterActiveAreas (ofs);
[/COLOR]

Then to handle our mouse events overload "clbkVCMouseEvent" and add the following line.

Code:
// --------------------------------------------------------------
// Respond to virtual cockpit mouse events
// --------------------------------------------------------------
bool LM::clbkVCMouseEvent (int id, int event, VECTOR3 &p)
{
[COLOR="red"]	return vc->MouseEvent (id, event, p); // Pass user inputs to cockpit control class[/COLOR]
} // End "LM::clbkLoadVC"

The active areas should now be registered in orbiter and user inputs will be passed to the cockpit control class to be parsed.

Compile and test your handy work. clicking on the hatch handle in the VC should now cause the hatch to open or close.

picture.php


NEXT UP: Animated Switches.
 
Last edited:

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Don't know about the docking issue, chances are that it's an issue with clbkconsumebufferedkey, make sure it returns a value even when a key is not being pressed (return 0;).

As for RCS thruster groups, make sure that they have been defined.

I assigned temporary groups back in clbkpostcreation but the final config will have to wait till the VC is done which means after I get some time off from school.

thanks for the reply. i posted that question quite a while ago, i worked it out now. also thanks for this tutorial, thanks to you my knowledge of C++ code has incresed by ten fold!! :thumbup: you have been a huge help!
 
Top