API Question Triggering clbkLoadVC within the simulation

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Like the title says...

I have two issues that would be solved by triggering load VC.

The first is that I want my camera position to be persistent, If i'm looking through the COAS reticle and I switch to external view I want to be looking through the COAS when I switch back.

The second is that when my center of gravity shifts the camera and meshes move but the active areas do not.

Reloading the VC would fix this but I'm worried about the knock-on effects of calling a default clbk from within the simulation.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Reloading the VC would fix this but I'm worried about the knock-on effects of calling a default clbk from within the simulation.
Orbiter already calls clbkLoadVC from within the simulation, you should be able to do so on your own.

From the API reference:
Orbiter calls VESSEL2::clbkLoadVC() with the appropriate id whenever the user switches to a new [VC camera]
position.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Ok, I started calling "clbkLoadVC ()" as part of my "UpdateLanderCoG ()" function and encountered an odd bug.

picture.php


The background image used by my displays is getting "contaminated" by prior display states.

On a related note is there a quick and simple way to load a texture once and then have it shared by multiple instances of the same class? IE loading "font.dds" once and then using for all the LCD displays that are active in the simulation.

In theory a static class member would do the trick but in practice I find that the surface in question is still getting initialized multiple times.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
The background image used by my displays is getting "contaminated" by prior display states.
I'm no expert on displays and textures, but could you "clear" the surfaces somehow? You'd have to post some of the code inside LoadVC()

On a related note is there a quick and simple way to load a texture once and then have it shared by multiple instances of the same class? IE loading "font.dds" once and then using for all the LCD displays that are active in the simulation.

In theory a static class member would do the trick but in practice I find that the surface in question is still getting initialized multiple times.
Yes, a static member should do it. Do you currently use a static member?
 

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'm no expert on displays and textures, but could you "clear" the surfaces somehow? You'd have to post some of the code inside LoadVC()

Nor I, this is a new one on me but it is specifically associated with the VC call in UpdateLanderCoG (). Commenting it out causes thing to return to normal.

Yes, a static member should do it. Do you currently use a static member?

Yes, i have "static SURFHANDLE lcd_font" declared in my LCD class and then in the constructor "if (lcd_font == NULL) lcd_font = oapiLoadTexture (*file path*)"

Problem is that while observing the step through in debug mode, while the contents of lcd_font are persisting the it still gets loaded (and subsequently overwritten) by each instance of class LCD in the simulation, thus defeating the whole purpose of making it static in the first place.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Yes, i have "static SURFHANDLE lcd_font" declared in my LCD class and then in the constructor "if (lcd_font == NULL) lcd_font = oapiLoadTexture (*file path*)"

Problem is that while observing the step through in debug mode, while the contents of lcd_font are persisting the it still gets loaded (and subsequently overwritten) by each instance of class LCD in the simulation, thus defeating the whole purpose of making it static in the first place.
Hmm, in C++ you aren't allowed to initialize a static member of a class in the constructor's initializer list, but you should be able to give it a value inside the constructor like normal.

Does that mean that lcd_font has a non-zero value, yet lcd_font==NULL returns true as well? What does a printf of lcd_font reveal in the constructor?
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Does that mean that lcd_font has a non-zero value, yet lcd_font==NULL returns true as well? What does a printf of lcd_font reveal in the constructor?

Gibberish.

But I can't set it to Null because otherwise it would get set to null and subsequently reloaded each time an instance of LCD is added to the simulation which would basically be what I have now only worse.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Gibberish
Sorry :lol:

Hlynkacg said:
contents of lcd_font are persisting
If the above is true, then the oapiLoadTexture call should never occur, as (lcd_font == NULL) would be false.

Are both of those occurring? Specifically, before the if statement, is lcd_call not a null pointer? Secondly, does the code inside the if statement execute?

---------- Post added at 05:38 PM ---------- Previous post was at 05:35 PM ----------

Is this what the code is like?
Code:
class LCD
{
  public:
  LCD();
  private:
  static SURFHANDLE lcd_font;
}
//later
LCD::LCD()
{
  if (lcd_font == NULL)
  {
    lcd_font = oapiLoadTexture(filename);
  }
}
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Sorry :lol:

No I mean Printf returns gibberish.

Are both of those occurring? Specifically, before the if statement, is lcd_call not a null pointer? Secondly, does the code inside the if statement execute?

the code executes, the problem is that the texture either gets loaded once for every instance in the simulation or it doesn't get loaded at all.

I want it to load once, period, stop.

What is the best way to do this?
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
No I mean Printf returns gibberish.
Did you specify a pointer type in printf? Assuming SURFHANDLE is a pointer (probably), the correct way to do a printf for it is:
Code:
printf("address of lcd_font = %p\n", (void*)lcd_font);

the code executes, the problem is that the texture either gets loaded once for every instance in the simulation or it doesn't get loaded at all.
That should be what a static member variable does. I don't understand why it would do either of the behaviors above :shrug:

Can you post relevant code snippets of the class definition and of the constructor?
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Class declaration for the LCD instrument class...

Code:
// ==============================================================
// Cockpit class interface
// ==============================================================
class LCD : public Instrument
{
	public:
		LCD (VESSEL3 *vessel, SURFHANDLE source = NULL);
		~LCD ();

		// Public functions
		bool	RegisterArea (int aid, int x, int y, int strlen, SURFHANDLE surf);
		bool	RedrawEvent (SURFHANDLE surf);

		bool	SetDisplay (char *cbuf);
		void	SetGridParameters (int h = 0, int w = 0, int col = 0, int rows = 0);

	private:
		// Private functions
		void	PrintLCD (SURFHANDLE surf, char* cbuf, int row, int _length);

		// Private variables
		int		length;
		int		cHeight, cWidth;			// Character dimmensions 
		int		nRows, nChar;				// Dimmenstions of character grid
		char	buffer[64], displayed[64];	// Characer buffer and currently displayed character string
		
		// Surface handles
	[COLOR="Red"]	static SURFHANDLE	surf_lcdfont;	// Surface handle for character grid[/COLOR]
};

Constructor

Code:
// --------------------------------------------------------------
// Constructor
// --------------------------------------------------------------
LCD::LCD (VESSEL3 *vessel, SURFHANDLE source) : Instrument (vessel, source) 
{
	// Initialize variables 
	RefreshRate = 2;
	length = 0;
	SetGridParameters ();		// Set default grid parameters
	sprintf (displayed, "\0");	// Blank display string is blank 

	// Load character grid
	if (surf_lcdfont == NULL) surf_lcdfont = oapiLoadTexture ("AAPO/LCD_blitfont.dds");	// load font.dds
}

Initially the code was not executing so I added a "surf_lcdfont = NULL" to the top of the cpp file but then it initializes all the time defeating the purpose of making it static in the first place.
 
Last edited:

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Initially the code was not executing so I added a "surf_lcdfont = NULL" to the top of the cpp file but then it initializes all the time defeating the purpose of making it static in the first place.
That's (probably) your problem.

You never initialize the static SURFHANDLE, that's why it caused the undefined behavior beforehand. Basically, sometimes the SURFHANDLE would start NULL, other times it would start with some random memory value. Then, when you added the surf_lcdfont assignment to the constructor, it initializes all the time.

The correct way to initialize static member variables is as follows:
Code:
[COLOR="Red"]//Init static member variable to null
SURFHANDLE LCD::surf_lcdfont = 0;[/COLOR]
// --------------------------------------------------------------
// Constructor
// --------------------------------------------------------------
LCD::LCD (VESSEL3 *vessel, SURFHANDLE source) : Instrument (vessel, source) 
{
	// Initialize variables 
	RefreshRate = 2;
	length = 0;
	SetGridParameters ();		// Set default grid parameters
	sprintf (displayed, "\0");	// Blank display string is blank 

	// Load character grid
	if (surf_lcdfont == NULL) surf_lcdfont = oapiLoadTexture ("AAPO/LCD_blitfont.dds");	// load font.dds
}
It's kind of weird, but you have to initialize them inside the .cpp file, outside of any function. You'd expect them to default to some default value (null in this case), but they don't.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
That did it, thank you.

I'm also not sure if I ever thanked you for all the help you provied on the VC switches but thank you for that too.

While I've got you here I was wondering if I could get your help with a similar issue.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Sure, what?

Similar to what we did with automating the parsing of switches, I want to automate my displays.

I've already created a basic "Instrument" class from which all of the different displays are derived, but I'm not sure how to proceed.

The problem is that unlike the switches which are all essentially identical. The displays come in many forms, and as a result the constructors for each are unique, so it isnt as simple as creating a single "CreateDisplayFunction" and adding that instance to a map.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
The problem is that unlike the switches which are all essentially identical. The displays come in many forms, and as a result the constructors for each are unique, so it isnt as simple as creating a single "CreateDisplayFunction" and adding that instance to a map.
Yep, we can do the same thing (I did something similar in SimpitController, where I had a generic IO, but I created a map that covered all of them).

You basically create a vector of pointers to Instruments. I'm busy at the moment, but I'll make a more detailed answer later when I'm free.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Do what you need to do and thank you in advance :tiphat:
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Hlynkacg said:
I've already created a basic "Instrument" class from which all of the different displays are derived, but I'm not sure how to proceed.

The problem is that unlike the switches which are all essentially identical. The displays come in many forms, and as a result the constructors for each are unique, so it isnt as simple as creating a single "CreateDisplayFunction" and adding that instance to a map.

Ok. This whole setup relies on the fact that you can have a collection of base class pointers that call derived class methods when invoked.

Some theory:
Normally, if you try to set a base class variable equal to a derived class, the extra derived class methods get "sliced" and removed.
Code:
class Animal
{
  public:
  Animal() {}
  virtual void Speak() {printf("I'm an animal!");}
}

class Dog::public Animal
{
  public:
  Dog() : Animal() {}
  void Speak() {printf("Bark");}
} 
//somewhere later
Animal myAnimal = Dog();
//if myAnimal.Speak() is called, it will print "i'm an animal", as the extra
//dog methods/implementations have been sliced off

We avoid that if we use pointers
Code:
Animal * myAnimal = new Dog();
//now, myAnimal->Speak() will print "Bark", as the computer figures
//out that that pointer actually points to a Dog

Now, enough of the animal classes.

First, you have to define the base class, with any and all of the public methods that derived Instruments should call. (This base class is similar to an interface in other languages, like C#)
Code:
class Instrument
{
  public:
  Insturment();
  virtual bool RegisterArea () [COLOR="Red"]= 0[/COLOR];
  virtual bool RedrawEvent () [COLOR="Red"]= 0[/COLOR];
}

Note the function signatures. The virtual, like normal, allows the function to be overridden in derived classes. The "= 0" makes a function a "pure virtual" function; basically, it has no definition in that class. This forces any derived classes that are actually constructed to have a definition for that function. Effectively, the "= 0" is just error prevention, as the computer will throw an error if you try to create a derived class that does not implement one of the pure virtual functions.

Now, to hold all your instruments, just create a vector that holds pointers to Instrument. This will probably be in the vessel definition.
Code:
std::vector<Instrument *> VesselInstruments;

Now, you need to create a function that creates each of the instruments. Unlike some of my stuff, you will determine the instruments pre-compile-time, so just create a function inside whatever class holds the instruments- again, this is probably in the vessel class.

Inside the createInstruments function, just push back pointers to the derived types into the vector.
Code:
//assuming there are derived classes
//LCDInstrument, FooInstrument, BarInstrument
bool createInstruments()
{
	//create the LCD's
	VesselInstruments.push_back(new LCDInstrument(10, "Fuel Quantity")) //obviously replace with whatever the constructor actually is
	VesselInstruments.push_back(new LCDInstrument(15, "Fuel Flow"));
	
	//to demonstrate additional setup before adding the instrument to the main vector
	FooInstrument * foo1 = new FooInstrument();
	foo1->setUpGrid();
	VesselInstruments.push_back(foo1);
	
	//create bar instrument
	VesselInstruments.push_back(new BarInstrument());
}

Now, just like in the VC switches, you just call the base class methods. Note that it is impossible to call any derived class public method that does not exist in the base class.
Code:
void registerInstrumentAreas()
{
  for (int i = 0; i < VesselInstruments.size(); i++)
  {
    VesselInstruments[i]->RegisterArea();
  }
}

void redrawInstruments()
{
  for (int i = 0; i < VesselInstruments.size(); i++)
  {
    VesselInstruments[i]->RedrawEvent();
  }
}
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Thank you for the detailed reply.

is there a particular reason we're using a std::vector in this cases as opposed to a map as we did with the switches?
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
is there a particular reason we're using a std::vector in this cases as opposed to a map as we did with the switches?
Yes. Before, as Orbiter referred to each switch area by an ID number, it was convenient to make it a map, so each switch could be easily found. In this case, at least from what you've said, it seems like you don't have to specifically refer to any instrument; each instrument just does it's own thing, so we use a vector.

Now of course if you want to be able to access a specific instrument by some system, you can make it a map.
 
Top