1
0
Fork 0

Virtual cockpit patches from Andy Ross:

What the attached patch does is map your panel definition onto a (non
z-buffered) quad in front of your face.  You can twist the view around
and see it move in the appropriate ways.

Apply the patch (let me know if folks need help with that step), and
then set the /sim/virtual-cockpit property to true.  You can do this
on the command line with --prop:/sim/virtual-cockpit=1, or via the
property picker.  Bind it to a key for fast toggling if you like.

The default bindings don't allow for "panning" the view, so you'll
have to modify yours.  These are the mappings to my joystick's hat
switch, for those who need hints:

   <axis n="6">
    <desc>View Direction</desc>
    <low>
     <repeatable>true</repeatable>
     <binding>
      <command>property-adjust</command>
      <property>/sim/view/goal-offset-deg</property>
      <step type="double">1.0</step>
     </binding>
    </low>
    <high>
     <repeatable>true</repeatable>
     <binding>
      <command>property-adjust</command>
      <property>/sim/view/goal-offset-deg</property>
      <step type="double">-1.0</step>
     </binding>
    </high>
   </axis>

   <axis n="7">
    <desc>View Elevation</desc>
    <low>
     <repeatable>true</repeatable>
     <binding>
      <command>property-adjust</command>
      <property>/sim/view/goal-tilt-deg</property>
      <step type="double">1.0</step>
     </binding>
    </low>
    <high>
     <repeatable>true</repeatable>
     <binding>
      <command>property-adjust</command>
      <property>/sim/view/goal-tilt-deg</property>
      <step type="double">-1.0</step>
     </binding>
    </high>
   </axis>

While the current implementation is happy with just plastering the
panel's notion of "screen" into the 3D world, this is actually more
general.  Each panel can, in principle, have it's own coordinate
system, and you could build a cockpit out of a bunch of them.  The
mapping is specified by providing a 3D coordinate for three corners of
the quad the panel should be mapped to; this should be pretty simple
to work with.

All that's needed for a perfectly general solution is a convention on
where to store the information (a cockpit xml file, or put it in the
aircraft -set file, or...), and some work on the panel's coordinate
system conventions (some of which don't coexist very nicely with a
generalized 3D environment).  Combine that with a plib model for the
non-panel interior of the cockpit, and we're golden.

I'm actually really pleased with this.  It worked better and more
quickly than I could have imagined, and impact on the surrounding code
is quite light -- a few property tests only.  But some stuff is still
missing:

+ No equivalent work was done to the HUD, so it still displays
   incorrect headings when the view changes.  The use of pixel
   coordinates deep in the HUD code is going to give me fits doing the
   port, I sure.  It's not nearly so well put together as the panel
   (where I just changed the setup code -- none of the rendering code
   changed at all).

+ I forgot that the panel was clickable. :)  Input events still have
   the screen coordinates, which essentially kills the interactivity
   when in virtual cockpit mode.  This won't be hard to fix; it's only
   broken because I forgot the feature existed.

And one note about the implementation choice: to get away from the
inevitable near clip plane issue, the virtual cockpit renderer simply
disables the z buffer.  This means that cockpits built using these
panels need to be z-sorted, which isn't too hard since they are static
geometry.  It also means that no two "virtual panels" can ever be
allowed to interpenetrate.  No biggie.
This commit is contained in:
david 2002-03-03 00:06:24 +00:00
parent 0296c04e32
commit 7e93fca8ee
3 changed files with 166 additions and 25 deletions

View file

@ -57,7 +57,6 @@
# undef NONE
#endif
////////////////////////////////////////////////////////////////////////
// Local functions.
@ -84,10 +83,11 @@ get_aspect_adjust (int xsize, int ysize)
bool
fgPanelVisible ()
{
return ((current_panel != 0) &&
(current_panel->getVisibility()) &&
(globals->get_viewmgr()->get_current() == 0) &&
(globals->get_current_view()->get_view_offset() == 0.0));
return (fgGetBool("/sim/virtual-cockpit") ||
((current_panel != 0) &&
(current_panel->getVisibility()) &&
(globals->get_viewmgr()->get_current() == 0) &&
(globals->get_current_view()->get_view_offset() == 0.0)));
}
@ -330,19 +330,23 @@ FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
y_offset += y_adjust;
}
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
// gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
if(fgGetBool("/sim/virtual-cockpit")) {
setupVirtualCockpit();
} else {
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
// gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(x_offset, y_offset, 0);
glTranslated(x_offset, y_offset, 0);
}
// Draw the background
// Draw the background
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
@ -395,20 +399,152 @@ FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
for ( ; current != end; current++) {
FGPanelInstrument * instr = *current;
glLoadIdentity();
glPushMatrix();
glTranslated(x_offset, y_offset, 0);
glTranslated(instr->getXPos(), instr->getYPos(), 0);
instr->draw();
glPopMatrix();
}
if(fgGetBool("/sim/virtual-cockpit")) {
cleanupVirtualCockpit();
} else {
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
ssgForceBasicState();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
// Yanked from the YASim codebase. Should probably be replaced with
// the 4x4 routine from plib, which is more appropriate here.
static void invert33Matrix(float* m)
{
// Compute the inverse as the adjoint matrix times 1/(det M).
// A, B ... I are the cofactors of a b c
// d e f
// g h i
float a=m[0], b=m[1], c=m[2];
float d=m[3], e=m[4], f=m[5];
float g=m[6], h=m[7], i=m[8];
float A = (e*i - h*f);
float B = -(d*i - g*f);
float C = (d*h - g*e);
float D = -(b*i - h*c);
float E = (a*i - g*c);
float F = -(a*h - g*b);
float G = (b*f - e*c);
float H = -(a*f - d*c);
float I = (a*e - d*b);
float id = 1/(a*A + b*B + c*C);
m[0] = id*A; m[1] = id*D; m[2] = id*G;
m[3] = id*B; m[4] = id*E; m[5] = id*H;
m[6] = id*C; m[7] = id*F; m[8] = id*I;
}
void
FGPanel::setupVirtualCockpit()
{
int i;
FGViewer* view = globals->get_current_view();
// Corners for the panel quad. These numbers put a "standard"
// panel at 1m from the eye, with a horizontal size of 60 degrees,
// and with its center 5 degrees down. This will work well for
// most typical screen-space panel definitions. In principle,
// these should be settable per-panel, so that you can have lots
// of panel objects plastered about the cockpit in realistic
// positions and orientations.
float DY = .0875; // tan(5 degrees)
float a[] = { -0.5773503, -0.4330172 - DY, -1 }; // bottom left
float b[] = { 0.5773503, -0.4330172 - DY, -1 }; // bottom right
float c[] = { -0.5773503, 0.4330172 - DY, -1 }; // top left
// A standard projection, in meters, with especially close clip
// planes.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(),
0.01, 100);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// Generate a "look at" matrix using OpenGL (!) coordinate
// conventions.
float lookat[3];
float pitch = view->get_view_tilt();
float rot = view->get_view_offset();
lookat[0] = -sin(rot);
lookat[1] = sin(pitch) / cos(pitch);
lookat[2] = -cos(rot);
if(fabs(lookat[1]) > 9999) lookat[1] = 9999; // FPU sanity
gluLookAt(0, 0, 0, lookat[0], lookat[1], lookat[2], 0, 1, 0);
// Translate the origin to the location of the panel quad
glTranslatef(a[0], a[1], a[2]);
// Generate a matrix to translate unit square coordinates from the
// panel to real world coordinates. Use a basis for the panel
// quad and invert. Note: this matrix is relatively expensive to
// compute, and is invariant. Consider precomputing and storing
// it. Also, consider using the plib vector math routines, so the
// reuse junkies don't yell at me. (Fine, I hard-coded a cross
// product. Just shoot me and be done with it.)
float u[3], v[3], w[3], m[9];
for(i=0; i<3; i++) u[i] = b[i] - a[i]; // U = B - A
for(i=0; i<3; i++) v[i] = c[i] - a[i]; // V = C - A
w[0] = u[1]*v[2] - v[1]*u[2]; // W = U x V
w[1] = u[2]*v[0] - v[2]*u[0];
w[2] = u[0]*v[1] - v[0]*u[1];
for(int i=0; i<3; i++) { // |Ux Uy Uz|-1
m[i] = u[i]; // m =|Vx Vy Vz|
m[i+3] = v[i]; // |Wx Wy Wz|
m[i+6] = w[i];
}
invert33Matrix(m);
float glm[16]; // Expand to a 4x4 OpenGL matrix.
glm[0] = m[0]; glm[4] = m[1]; glm[8] = m[2]; glm[12] = 0;
glm[1] = m[3]; glm[5] = m[4]; glm[9] = m[5]; glm[13] = 0;
glm[2] = m[6]; glm[6] = m[7]; glm[10] = m[8]; glm[14] = 0;
glm[3] = 0; glm[7] = 0; glm[11] = 0; glm[15] = 1;
glMultMatrixf(glm);
// Finally, a scaling factor to convert the 1024x768 range the
// panel uses to a unit square mapped to the panel quad.
glScalef(1./1024, 1./768, 1);
// Scale to the appropriate vertical size. I'm not quite clear on
// this yet; an identical scaling is not appropriate for
// _width, for example. This should probably go away when panel
// coordinates get sanified for virtual cockpits.
glScalef(1, _height/768.0, 1);
// Now, turn off the Z buffer. The panel code doesn't need
// it, and we're using different clip planes anyway (meaning we
// can't share it without glDepthRange() hackery or much
// framebuffer bandwidth wasteage)
glDisable(GL_DEPTH_TEST);
}
void
FGPanel::cleanupVirtualCockpit()
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
/**
* Set the panel's visibility.
@ -694,7 +830,8 @@ FGLayeredInstrument::draw ()
if (test()) {
for (int i = 0; i < (int)_layers.size(); i++) {
glPushMatrix();
glTranslatef(0.0, 0.0, (i / 100.0) + 0.1);
if(!fgGetBool("/sim/virtual-cockpit"))
glTranslatef(0.0, 0.0, (i / 100.0) + 0.1);
_layers[i]->draw();
glPopMatrix();
}

View file

@ -181,6 +181,9 @@ public:
virtual bool doMouseAction (int button, int updown, int x, int y);
private:
void setupVirtualCockpit();
void cleanupVirtualCockpit();
mutable bool _visibility;
mutable bool _mouseDown;
mutable int _mouseButton, _mouseX, _mouseY;

View file

@ -1258,7 +1258,8 @@ static void fgIdleFunction ( void ) {
void fgReshape( int width, int height ) {
int view_h;
if ( fgPanelVisible() && idle_state == 1000 ) {
if ( (!fgGetBool("/sim/virtual-cockpit"))
&& fgPanelVisible() && idle_state == 1000 ) {
view_h = (int)(height * (current_panel->getViewHeight() -
current_panel->getYOffset()) / 768.0);
} else {