Coding a Lunar Lander from the ground up: The virtual cockpit

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, 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... Much better yes? :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.
 
Top