1
0
Fork 0

Andy wrote:

This is just a port of an old 3D HUD patch to the new view code.
This pans the HUD with the view, by pasting it onto a quad fixed in front of the viewer.  It also fixes the awful, arbitrary scaling problems the HUD code has.  The old HUD only looks right when viewed at 1024x768 and 55 degree FOV.  This works the scale out magically so that it looks right in all views.  It also redefines the "<compression-factor>" tag in the ladder to (1) mean compression instead of expansion and (2) have non-psychopathic units (now "1" means 1 degree per degree).  Fix this in your existing HUD ladder files before reporting bugs.  It's definitely a cosmetic win -- the velocity vector points at the right thing and the horizon lines up properly.

Norman wrote:

I have created a modified version of Andy's patch that implements the 3D HUD as the 'normal' and the 2D HUD as the 'minimal' HUD.   < i > and < shift I > keys
This commit is contained in:
ehofman 2003-03-21 15:02:23 +00:00
parent a4d1b0902e
commit 4c55a5aeb4
5 changed files with 162 additions and 197 deletions

View file

@ -52,6 +52,7 @@
#include <GUI/gui.h> #include <GUI/gui.h>
#include "cockpit.hxx" #include "cockpit.hxx"
#include "hud.hxx"
// This is a structure that contains all data related to // This is a structure that contains all data related to

View file

@ -32,7 +32,6 @@
#endif #endif
#include "hud.hxx"
#include "panel.hxx" #include "panel.hxx"
// Class fg_Cockpit This class is a holder for the heads up display // Class fg_Cockpit This class is a holder for the heads up display

View file

@ -23,6 +23,9 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/misc/exception.hxx> #include <simgear/misc/exception.hxx>
#include STL_STRING
#include STL_FSTREAM
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
@ -34,15 +37,13 @@
#ifdef __BORLANDC__ #ifdef __BORLANDC__
# define exception c_exception # define exception c_exception
#endif #endif
#include <math.h> #include <math.h>
#include GLUT_H #include <GL/glut.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> // char related functions #include <stdio.h> // char related functions
#include <string.h> // strcmp() #include <string.h> // strcmp()
#include STL_STRING
#include STL_FSTREAM
#include <simgear/constants.h> #include <simgear/constants.h>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
@ -174,6 +175,9 @@ static instr_item * readLabel( const SGPropertyNode * node);
static instr_item * readTBI( const SGPropertyNode * node); static instr_item * readTBI( const SGPropertyNode * node);
//$$$ end - added, Neetha, 28 Nov 2k //$$$ end - added, Neetha, 28 Nov 2k
static void drawHUD();
static void fgUpdateHUDVirtual();
void fgHUDalphaInit( void ); void fgHUDalphaInit( void );
class locRECT { class locRECT {
@ -194,78 +198,6 @@ locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
} }
// #define DEBUG // #define DEBUG
#ifdef OLD_CODE
void drawOneLine( UINT x1, UINT y1, UINT x2, UINT y2)
{
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
}
void drawOneLine( RECT &rect)
{
glBegin(GL_LINES);
glVertex2f(rect.left, rect.top);
glVertex2f(rect.right, rect.bottom);
glEnd();
}
//
// The following code deals with painting the "instrument" on the display
//
/* textString - Bitmap font string */
void textString( int x, int y, char *msg, void *font,int digit) //suma
{
if(*msg) {
// puDrawString ( NULL, msg, x, y );
glRasterPos2f(x, y);
while (*msg) {
glutBitmapCharacter(font, *msg);
msg++;
}
}
}
/* strokeString - Stroke font string */
void strokeString(int x, int y, char *msg, void *font, float theta)
{
int xx;
int yy;
int c;
float sintheta,costheta;
if(*msg) {
glPushMatrix();
glRotatef(theta * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0);
sintheta = sin(theta);
costheta = cos(theta);
xx = (int)(x * costheta + y * sintheta);
yy = (int)(y * costheta - x * sintheta);
glTranslatef( xx, yy, 0);
glScalef(.1, .1, 0.0);
while( (c=*msg++) ) {
glutStrokeCharacter(font, c);
}
glPopMatrix();
}
}
int getStringWidth ( char *str )
{
if ( HUDtext && str ) {
float r, l ;
guiFntHandle->getBBox ( str, HUD_TextSize, 0, &l, &r, NULL, NULL ) ;
return FloatToInt( r - l );
}
return 0 ;
}
#endif // OLD_CODE
//========================= End of Class Implementations=================== //========================= End of Class Implementations===================
// fgHUDInit // fgHUDInit
// //
@ -310,11 +242,18 @@ readLadder(const SGPropertyNode * node)
zenith = node->getIntValue("zenith"); //suma zenith = node->getIntValue("zenith"); //suma
nadir = node->getIntValue("nadir"); //suma nadir = node->getIntValue("nadir"); //suma
hat = node->getIntValue("hat"); hat = node->getIntValue("hat");
// The factor assumes a base of 55 degrees per 640 pixels.
// Invert to convert the "compression" factor to a
// pixels-per-degree number.
if( HUD_style == 1)
{
// if(factor == 0)
factor = 1;
factor = (640./55.) / factor;
}
SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name); SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
p = (instr_item *) new HudLadder( name, x, y, p = (instr_item *) new HudLadder( name, x, y,
width, height, factor, width, height, factor,
get_roll, get_pitch, get_roll, get_pitch,
@ -745,7 +684,6 @@ int readHud( istream &input )
<< " from " << " from "
<< path.str()); << path.str());
SGPropertyNode root2; SGPropertyNode root2;
try { try {
readProperties(path.str(), &root2); readProperties(path.str(), &root2);
@ -763,7 +701,6 @@ int readHud( istream &input )
int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ ) int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
{ {
HUD_style = 1; HUD_style = 1;
SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" ); SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
@ -785,6 +722,23 @@ int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
fgHUDalphaInit(); fgHUDalphaInit();
fgHUDReshape(); fgHUDReshape();
if ( HUDtext ) {
// this chunk of code is not necessarily thread safe if the
// compiler optimizer reorders these statements. Note that
// "delete ptr" does not set "ptr = NULL". We have to do that
// ourselves.
fntRenderer *tmp = HUDtext;
HUDtext = NULL;
delete tmp;
}
// HUD_TextSize = fgGetInt("/sim/startup/xsize") / 60;
HUD_TextSize = 10;
HUDtext = new fntRenderer();
HUDtext -> setFont ( guiFntHandle ) ;
HUDtext -> setPointSize ( HUD_TextSize ) ;
HUD_TextList.setFont( HUDtext );
return 0; // For now. Later we may use this for an error code. return 0; // For now. Later we may use this for an error code.
} }
@ -1006,6 +960,7 @@ void fgHUDalphaInit( void ) {
void fgHUDReshape(void) { void fgHUDReshape(void) {
#if 0
if ( HUDtext ) { if ( HUDtext ) {
// this chunk of code is not necessarily thread safe if the // this chunk of code is not necessarily thread safe if the
// compiler optimizer reorders these statements. Note that // compiler optimizer reorders these statements. Note that
@ -1022,6 +977,7 @@ void fgHUDReshape(void) {
HUDtext -> setFont ( guiFntHandle ) ; HUDtext -> setFont ( guiFntHandle ) ;
HUDtext -> setPointSize ( HUD_TextSize ) ; HUDtext -> setPointSize ( HUD_TextSize ) ;
HUD_TextList.setFont( HUDtext ); HUD_TextList.setFont( HUDtext );
#endif
} }
@ -1040,6 +996,12 @@ static void set_hud_color(float r, float g, float b) {
// //
void fgUpdateHUD( void ) { void fgUpdateHUD( void ) {
if( HUD_style == 1)
{
fgUpdateHUDVirtual();
return;
}
static const float normal_aspect = float(640) / float(480); static const float normal_aspect = float(640) / float(480);
// note: aspect_ratio is Y/X // note: aspect_ratio is Y/X
float current_aspect = 1.0f/globals->get_current_view()->get_aspect_ratio(); float current_aspect = 1.0f/globals->get_current_view()->get_aspect_ratio();
@ -1054,17 +1016,79 @@ void fgUpdateHUD( void ) {
} }
} }
void fgUpdateHUDVirtual()
{
FGViewer* view = globals->get_current_view();
// Standard fgfs projection, with essentially meaningless clip
// planes (we'll map the whole HUD plane to z=-1)
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(), 0.1, 10);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Standard fgfs view direction computation
float lookat[3];
lookat[0] = -sin(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
lookat[1] = tan(SG_DEGREES_TO_RADIANS * view->getPitchOffset_deg());
lookat[2] = -cos(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
if(fabs(lookat[1]) > 9999) lookat[1] = 9999; // FPU sanity
gluLookAt(0, 0, 0, lookat[0], lookat[1], lookat[2], 0, 1, 0);
// Map the -1:1 square to a 55.0x41.25 degree wide patch at z=1.
// This is the default fgfs field of view, which the HUD files are
// written to assume.
float dx = 0.52056705; // tan(55/2)
float dy = dx * 0.75; // assumes 4:3 aspect ratio
float m[16];
m[0] = dx; m[4] = 0; m[ 8] = 0; m[12] = 0;
m[1] = 0; m[5] = dy; m[ 9] = 0; m[13] = 0;
m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0;
m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
glMultMatrixf(m);
// Convert the 640x480 "HUD standard" coordinate space to a square
// about the origin in the range [-1:1] at depth of -1
glScalef(1./320, 1./240, 1);
glTranslatef(-320, -240, -1);
// Do the deed
drawHUD();
// Clean up our mess
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void fgUpdateHUD( GLfloat x_start, GLfloat y_start, void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
GLfloat x_end, GLfloat y_end ) GLfloat x_end, GLfloat y_end )
{ {
int brightness; glMatrixMode(GL_PROJECTION);
// int day_night_sw = current_aircraft.controls->day_night_switch; glPushMatrix();
int day_night_sw = global_day_night_switch; glLoadIdentity();
int hud_displays = HUD_deque.size(); gluOrtho2D(x_start, x_end, y_start, y_end);
instr_item *pHUDInstr;
// float line_width;
if( !hud_displays ) { // Trust everyone, but ALWAYS cut the cards! glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
drawHUD();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void drawHUD()
{
if( !HUD_deque.size() ) { // Trust everyone, but ALWAYS cut the cards!
return; return;
} }
@ -1072,19 +1096,6 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
HUD_LineList.erase(); HUD_LineList.erase();
// HUD_StippleLineList.erase(); // HUD_StippleLineList.erase();
pHUDInstr = HUD_deque[0];
brightness = pHUDInstr->get_brightness();
// brightness = HUD_deque.at(0)->get_brightness();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(x_start, x_end, y_start, y_end);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
@ -1096,13 +1107,13 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
// glEnable(GL_BLEND); // glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE); glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE);
glLineWidth(1.5); glLineWidth(2.0);
} else { } else {
glLineWidth(1.0); glLineWidth(1.0);
} }
if( day_night_sw == HUD_DAY) { if( global_day_night_switch == HUD_DAY) {
switch (brightness) switch (HUD_deque[0]->get_brightness())
{ {
case HUD_BRT_LIGHT: case HUD_BRT_LIGHT:
set_hud_color (0.1f, 0.9f, 0.1f); set_hud_color (0.1f, 0.9f, 0.1f);
@ -1124,8 +1135,8 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
set_hud_color (0.1f, 0.9f, 0.1f); set_hud_color (0.1f, 0.9f, 0.1f);
} }
} else { } else {
if( day_night_sw == HUD_NIGHT) { if( global_day_night_switch == HUD_NIGHT) {
switch (brightness) switch (HUD_deque[0]->get_brightness())
{ {
case HUD_BRT_LIGHT: case HUD_BRT_LIGHT:
set_hud_color (0.9f, 0.1f, 0.1f); set_hud_color (0.9f, 0.1f, 0.1f);
@ -1151,22 +1162,9 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
} }
} }
deque < instr_item * > :: iterator current = HUD_deque.begin(); for_each(HUD_deque.begin(), HUD_deque.end(), HUDdraw());
deque < instr_item * > :: iterator last = HUD_deque.end();
for ( ; current != last; ++current ) { HUD_TextList.add( fgText(40, 10, get_formated_gmt_time(), 0) );
pHUDInstr = *current;
if( pHUDInstr->enabled()) {
// fgPrintf( SG_COCKPIT, SG_DEBUG, "HUD Code %d Status %d\n",
// hud->code, hud->status );
pHUDInstr->draw();
}
}
char *gmt_str = get_formated_gmt_time();
HUD_TextList.add( fgText(40, 10, gmt_str, 0) );
#ifdef FG_NETWORK_OLK #ifdef FG_NETWORK_OLK
if ( net_hud_display ) { if ( net_hud_display ) {
@ -1175,46 +1173,32 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
#endif #endif
// temporary
// extern bool fgAPAltitudeEnabled( void );
// extern bool fgAPHeadingEnabled( void );
// extern bool fgAPWayPointEnabled( void );
// extern char *fgAPget_TargetDistanceStr( void );
// extern char *fgAPget_TargetHeadingStr( void );
// extern char *fgAPget_TargetAltitudeStr( void );
// extern char *fgAPget_TargetLatLonStr( void );
int apY = 480 - 80; int apY = 480 - 80;
// char scratch[128]; // char scratch[128];
// HUD_TextList.add( fgText( "AUTOPILOT", 20, apY) ); // HUD_TextList.add( fgText( "AUTOPILOT", 20, apY) );
// apY -= 15; // apY -= 15;
if( globals->get_autopilot()->get_HeadingEnabled() ) { FGAutopilot *AP = globals->get_autopilot();
HUD_TextList.add( fgText( 40, apY,
globals->get_autopilot()->get_TargetHeadingStr()) ); if( AP->get_HeadingEnabled() ) {
HUD_TextList.add( fgText( 40, apY, AP->get_TargetHeadingStr()) );
apY -= 15; apY -= 15;
} }
if( globals->get_autopilot()->get_AltitudeEnabled() ) { if( AP->get_AltitudeEnabled() ) {
HUD_TextList.add( fgText( 40, apY, HUD_TextList.add( fgText( 40, apY, AP->get_TargetAltitudeStr()) );
globals->get_autopilot()->get_TargetAltitudeStr()) );
apY -= 15; apY -= 15;
} }
if( globals->get_autopilot()->get_HeadingMode() == if( AP->get_HeadingMode() == FGAutopilot::FG_HEADING_WAYPOINT )
FGAutopilot::FG_HEADING_WAYPOINT )
{ {
char *wpstr; if ( strlen( AP->get_TargetWP1Str() ) ) {
wpstr = globals->get_autopilot()->get_TargetWP1Str(); HUD_TextList.add( fgText( 40, apY, AP->get_TargetWP1Str() ) );
if ( strlen( wpstr ) ) {
HUD_TextList.add( fgText( 40, apY, wpstr ) );
apY -= 15; apY -= 15;
} }
wpstr = globals->get_autopilot()->get_TargetWP2Str(); if ( strlen( AP->get_TargetWP2Str() ) ) {
if ( strlen( wpstr ) ) { HUD_TextList.add( fgText( 40, apY, AP->get_TargetWP2Str() ) );
HUD_TextList.add( fgText( 40, apY, wpstr ) );
apY -= 15; apY -= 15;
} }
wpstr = globals->get_autopilot()->get_TargetWP3Str(); if ( strlen( AP->get_TargetWP3Str() ) ) {
if ( strlen( wpstr ) ) { HUD_TextList.add( fgText( 40, apY, AP->get_TargetWP3Str() ) );
HUD_TextList.add( fgText( 40, apY, wpstr ) );
apY -= 15; apY -= 15;
} }
} }
@ -1236,9 +1220,5 @@ void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
} }

View file

@ -38,8 +38,7 @@
# include <windows.h> # include <windows.h>
#endif #endif
#include GLUT_H #include <GL/glut.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -257,6 +256,14 @@ public:
} }
}; };
class DrawLineSeg2D {
public:
void operator() (fgLineSeg2D elem) const {
elem.draw();
}
};
#define USE_HUD_TextList #define USE_HUD_TextList
extern float HUD_TextSize; extern float HUD_TextSize;
extern fntRenderer *HUDtext; extern fntRenderer *HUDtext;
@ -369,27 +376,10 @@ public:
void add( fgLineSeg2D seg ) { List.push_back(seg); } void add( fgLineSeg2D seg ) { List.push_back(seg); }
void erase( void ) { List.erase( List.begin(), List.end() ); } void erase( void ) { List.erase( List.begin(), List.end() ); }
void draw( void ) { void draw( void ) {
vector < fgLineSeg2D > :: iterator curSeg;
vector < fgLineSeg2D > :: iterator lastSeg;
curSeg = List.begin();
lastSeg = List.end();
glBegin(GL_LINES); glBegin(GL_LINES);
for ( ; curSeg != lastSeg; curSeg++ ) { for_each( List.begin(), List.end(), DrawLineSeg2D());
curSeg->draw();
}
glEnd(); glEnd();
} }
/* void draw( void ) {
vector < fgLineSeg2D > :: iterator curSeg;
vector < fgLineSeg2D > :: iterator lastSeg;
curSeg = List.begin();
lastSeg = List.end();
glBegin(GL_LINES);
for ( ; curSeg != lastSeg; curSeg++ ) {
curSeg->draw();
}
glEnd();
} */
}; };
class fgTextList { class fgTextList {
@ -404,11 +394,11 @@ public:
void erase( void ) { List.erase( List.begin(), List.end() ); } void erase( void ) { List.erase( List.begin(), List.end() ); }
void draw( void ) { void draw( void ) {
vector < fgText > :: iterator curString; if( Font == 0 )
vector < fgText > :: iterator lastString; return;
if( Font == 0 ) return; vector < fgText > :: iterator curString = List.begin();
curString = List.begin(); vector < fgText > :: iterator lastString = List.end();
lastString = List.end();
glPushAttrib( GL_COLOR_BUFFER_BIT ); glPushAttrib( GL_COLOR_BUFFER_BIT );
glEnable ( GL_ALPHA_TEST ) ; glEnable ( GL_ALPHA_TEST ) ;
glEnable ( GL_BLEND ) ; glEnable ( GL_BLEND ) ;
@ -478,7 +468,6 @@ public:
bool working = true, bool working = true,
int digit = 0); //suma int digit = 0); //suma
instr_item( const instr_item & image ); instr_item( const instr_item & image );
instr_item & operator = ( const instr_item & rhs ); instr_item & operator = ( const instr_item & rhs );
@ -553,6 +542,15 @@ public:
}; };
typedef instr_item *HIptr; typedef instr_item *HIptr;
class HUDdraw {
public:
void operator() (HIptr elem) const {
if( elem->enabled())
elem->draw();
}
};
//typedef deque < instr_item * > hud_deque_type; //typedef deque < instr_item * > hud_deque_type;
//typedef hud_deque_type::iterator hud_deque_iterator; //typedef hud_deque_type::iterator hud_deque_iterator;
//typedef hud_deque_type::const_iterator hud_deque_const_iterator; //typedef hud_deque_type::const_iterator hud_deque_const_iterator;
@ -794,7 +792,8 @@ public:
bool working, bool working,
float radius, //suma float radius, //suma
int divisions, //suma int divisions, //suma
int zoom); //suma int zoom //suma
);
~hud_card(); ~hud_card();
@ -923,6 +922,7 @@ private:
int hat; //suma int hat; //suma
// The Ladder has it's own temporary display lists
fgTextList TextList; fgTextList TextList;
fgLineList LineList; fgLineList LineList;
fgLineList StippleLineList; fgLineList StippleLineList;
@ -955,7 +955,8 @@ public:
bool working, bool working,
int zenith, //suma int zenith, //suma
int nadir, //suma int nadir, //suma
int hat); //suma int hat
); //suma
~HudLadder(); ~HudLadder();
@ -994,23 +995,6 @@ extern void fgUpdateHUD( void );
extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start, extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
GLfloat x_end, GLfloat y_end ); GLfloat x_end, GLfloat y_end );
extern void drawOneLine ( UINT x1, UINT y1, UINT x2, UINT y2);
extern void drawOneLine ( RECT &rect);
extern void textString ( int x,
int y,
char *msg,
void *font = GLUT_BITMAP_9_BY_15,int digit=0); //suma
extern void strokeString( int x,
int y,
char *msg,
void *font = GLUT_STROKE_ROMAN,
float theta = 0);
//extern void strokeString(float xx,
// float yy,
// char *msg,
// void *font = GLUT_STROKE_ROMAN)
/* /*
bool AddHUDInstrument( instr_item *pBlackBox ); bool AddHUDInstrument( instr_item *pBlackBox );
void DrawHUD ( void ); void DrawHUD ( void );

View file

@ -95,6 +95,7 @@ SG_USING_STD(endl);
#include <Autopilot/newauto.hxx> #include <Autopilot/newauto.hxx>
#include <Cockpit/hud.hxx>
#include <Cockpit/cockpit.hxx> #include <Cockpit/cockpit.hxx>
#include <Cockpit/radiostack.hxx> #include <Cockpit/radiostack.hxx>