Browse Source

support GraphicsOutput::set_inverted()

David Rose 21 years ago
parent
commit
48e09cda31

+ 76 - 57
panda/src/display/config_display.cxx

@@ -35,71 +35,90 @@ ConfigureFn(config_display) {
   init_libdisplay();
 }
 
-// This is normally true; set it false to disable view-frustum culling
-// (primarily useful for debugging).
-const bool view_frustum_cull = config_display.GetBool("view-frustum-cull", true);
+ConfigVariableBool view_frustum_cull
+("view-frustum-cull", true,
+ "This is normally true; set it false to disable view-frustum culling "
+ "(primarily useful for debugging).");
 
-// Set this true to show the number of unused states in the pstats
-// graph for TransformState and RenderState counts.  This adds a bit
-// of per-frame overhead to count these things up.
-const bool pstats_unused_states = config_display.GetBool("pstats-unused-states", false);
+ConfigVariableBool pstats_unused_states
+("pstats-unused-states", false,
+"Set this true to show the number of unused states in the pstats "
+"graph for TransformState and RenderState counts.  This adds a bit "
+"of per-frame overhead to count these things up.");
 
-// This is the default threading model to use for new windows.  Use
-// empty string for single-threaded, or something like "cull/draw" for
-// a 3-stage pipeline.  See GraphicsEngine::set_threading_model().
 
 // Warning!  The code that uses this is currently experimental and
 // incomplete, and will almost certainly crash!  Do not set
 // threading-model to anything other than its default of a
 // single-threaded model unless you are developing Panda's threading
 // system!
-const string threading_model = config_display.GetString("threading-model", "");
-
-// This indicates the initial setting of the auto-flip flag.  Set it
-// true (the default) to cause render_frame() to flip all the windows
-// before it returns (in single-threaded mode only), or false to wait
-// until an explicit call to flip_frame() or the next render_frame().
-const bool auto_flip = config_display.GetBool("auto-flip", true);
-
-// Set this true to yield the timeslice at the end of the frame to be
-// more polite to other applications that are trying to run.
-const bool yield_timeslice = config_display.GetBool("yield-timeslice", false);
-
-const string screenshot_filename = config_display.GetString("screenshot-filename", "%~p-%a-%b-%d-%H-%M-%S-%Y-%~f.%~e");
-const string screenshot_extension = config_display.GetString("screenshot-extension", "jpg");
-
-// Set this true to cause offscreen GraphicsBuffers to be created as
-// GraphicsWindows, if possible, so that their contents may be viewed
-// interactively.  Handy during development of multipass algorithms.
-const bool show_buffers = config_display.GetBool("show-buffers", false);
-
-// Set this true to make GraphicsOutput::make_render_texture() try to
-// create a parasite buffer before it tries to create an offscreen
-// buffer.  This may be desired if you know your graphics API does not
-// support render-directly-to-texture and you want to minimize
-// framebuffer memory.
-const bool prefer_parasite_buffer = config_display.GetBool("prefer-parasite-buffer", true);
-
-// Set this true to make GraphicsOutput::make_render_texture() first
-// try to create a single-buffered offscreen buffer, before falling
-// back to a double-buffered one (or whatever kind the source window
-// has).  This is true by default to reduce waste of framebuffer
-// memory, but you may get a performance benefit by setting it to
-// false (since in that case the buffer can share a graphics context
-// with the window).
-const bool prefer_single_buffer = config_display.GetBool("prefer-single-buffer", true);
-
-
-
-// Use the variable load-display to specify the name of the default
-// graphics display library or GraphicsPipe to load.  It is the name
-// of a shared library (or * for all libraries named in aux-display),
-// optionally followed by the name of the particular GraphicsPipe
-// class to create.
-
-// Also use the variable aux-display to name each of the graphics
-// display libraries that are available on a particular platform.
-// This variable may be repeated several times.
+ConfigVariableString threading_model
+("threading-model", "",
+ "This is the default threading model to use for new windows.  Use "
+ "empty string for single-threaded, or something like \"cull/draw\" for "
+ "a 3-stage pipeline.  See GraphicsEngine::set_threading_model(). "
+ "EXPERIMENTAL and incomplete, do not use this!");
+
+ConfigVariableBool auto_flip
+("auto-flip", true,
+ "This indicates the initial setting of the auto-flip flag.  Set it "
+ "true (the default) to cause render_frame() to flip all the windows "
+ "before it returns (in single-threaded mode only), or false to wait "
+ "until an explicit call to flip_frame() or the next render_frame().");
+
+ConfigVariableBool yield_timeslice
+("yield-timeslice", false,
+ "Set this true to yield the timeslice at the end of the frame to be "
+ "more polite to other applications that are trying to run.");
+
+ConfigVariableString screenshot_filename
+("screenshot-filename", "%~p-%a-%b-%d-%H-%M-%S-%Y-%~f.%~e",
+ "This specifies the filename pattern to be used to generate "
+ "screenshots captured via save_screenshot_default().  See "
+ "DisplayRegion::save_screenshot()."
+);
+ConfigVariableString screenshot_extension
+("screenshot-extension", "jpg",
+ "This specifies the default filename extension (and therefore the "
+ "default image type) to be used for saving screenshots.");
+
+ConfigVariableBool show_buffers
+("show-buffers", false,
+ "Set this true to cause offscreen GraphicsBuffers to be created as "
+ "GraphicsWindows, if possible, so that their contents may be viewed "
+ "interactively.  Handy during development of multipass algorithms.");
+
+ConfigVariableBool prefer_parasite_buffer
+("prefer-parasite-buffer", true,
+ "Set this true to make GraphicsOutput::make_render_texture() try to "
+ "create a parasite buffer before it tries to create an offscreen "
+ "buffer.  This may be desired if you know your graphics API does not "
+ "support render-directly-to-texture and you want to minimize "
+ "framebuffer memory.");
+
+ConfigVariableBool prefer_single_buffer
+("prefer-single-buffer", true,
+ "Set this true to make GraphicsOutput::make_render_texture() first "
+ "try to create a single-buffered offscreen buffer, before falling "
+ "back to a double-buffered one (or whatever kind the source window "
+ "has).  This is true by default to reduce waste of framebuffer "
+ "memory, but you may get a performance benefit by setting it to "
+ "false (since in that case the buffer can share a graphics context "
+ "with the window).");
+
+
+ConfigVariableBool copy_texture_inverted
+("copy-texture-inverted", false,
+ "Set this true to indicate that the GSG in use will invert textures when "
+ "it performs a framebuffer-to-texture copy operation, or false to indicate "
+ "that it does the right thing.  If this is not set, the default behavior is "
+ "determined by the GSG's internal logic.");
+
+ConfigVariableBool window_inverted
+("window-inverted", false,
+ "Set this true to create all windows with the inverted flag set, so that "
+ "they will render upside-down and backwards.  Normally this is useful only "
+ "for debugging.");
 
 
 ////////////////////////////////////////////////////////////////////

+ 16 - 11
panda/src/display/config_display.h

@@ -21,29 +21,34 @@
 
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
+#include "configVariableBool.h"
+#include "configVariableString.h"
+#include "configVariableList.h"
 #include "dconfig.h"
 
-#include <string>
 #include "pvector.h"
 
 ConfigureDecl(config_display, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(display, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(gsg, EXPCL_PANDA, EXPTP_PANDA);
 
-extern const bool view_frustum_cull;
-extern const bool pstats_unused_states;
+extern ConfigVariableBool view_frustum_cull;
+extern ConfigVariableBool pstats_unused_states;
 
-extern const string threading_model;
-extern const bool auto_flip;
-extern const bool yield_timeslice;
+extern ConfigVariableString threading_model;
+extern ConfigVariableBool auto_flip;
+extern ConfigVariableBool yield_timeslice;
 
-extern const string screenshot_filename;
-extern const string screenshot_extension;
+extern ConfigVariableString screenshot_filename;
+extern ConfigVariableString screenshot_extension;
 
-extern const bool show_buffers;
+extern ConfigVariableBool show_buffers;
 
-extern const bool prefer_parasite_buffer;
-extern const bool prefer_single_buffer;
+extern ConfigVariableBool prefer_parasite_buffer;
+extern ConfigVariableBool prefer_single_buffer;
+
+extern ConfigVariableBool copy_texture_inverted;
+extern ConfigVariableBool window_inverted;
 
 extern EXPCL_PANDA void init_libdisplay();
 

+ 0 - 21
panda/src/display/displayRegion.I

@@ -50,27 +50,6 @@ get_sort() const {
   return _sort;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: DisplayRegion::do_compute_pixels
-//       Access: Private
-//  Description: The private implementation of compute_pixels, this
-//               assumes that we already have the lock.
-////////////////////////////////////////////////////////////////////
-INLINE void DisplayRegion::
-do_compute_pixels(int x_size, int y_size) {
-  if (display_cat.is_debug()) {
-    display_cat.debug()
-      << "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
-  }
-
-  _pl = int((_l * x_size) + 0.5);
-  _pr = int((_r * x_size) + 0.5);
-  _pb = int((_b * y_size) + 0.5);
-  _pt = int((_t * y_size) + 0.5);
-  _pbi = int(((1.0f - _b) * y_size) + 0.5);
-  _pti = int(((1.0f - _t) * y_size) + 0.5);
-}
-
 
 INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) {
   dr.output(out);

+ 34 - 0
panda/src/display/displayRegion.cxx

@@ -553,3 +553,37 @@ win_display_regions_changed() {
     _window->win_display_regions_changed();
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::do_compute_pixels
+//       Access: Private
+//  Description: The private implementation of compute_pixels, this
+//               assumes that we already have the lock.
+////////////////////////////////////////////////////////////////////
+void DisplayRegion::
+do_compute_pixels(int x_size, int y_size) {
+  if (display_cat.is_debug()) {
+    display_cat.debug()
+      << "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
+  }
+
+  _pl = int((_l * x_size) + 0.5);
+  _pr = int((_r * x_size) + 0.5);
+
+  const GraphicsOutput *win = get_window();
+  nassertv(win != (GraphicsOutput *)NULL);
+  if (win->get_inverted()) {
+    // The window is inverted; compute the DisplayRegion accordingly.
+    _pb = int(((1.0f - _t) * y_size) + 0.5);
+    _pt = int(((1.0f - _b) * y_size) + 0.5);
+    _pbi = int((_t * y_size) + 0.5);
+    _pti = int((_b * y_size) + 0.5);
+
+  } else {
+    // The window is normal.
+    _pb = int((_b * y_size) + 0.5);
+    _pt = int((_t * y_size) + 0.5);
+    _pbi = int(((1.0f - _b) * y_size) + 0.5);
+    _pti = int(((1.0f - _t) * y_size) + 0.5);
+  }
+}

+ 1 - 1
panda/src/display/displayRegion.h

@@ -99,7 +99,7 @@ PUBLISHED:
 
 private:
   void win_display_regions_changed();
-  INLINE void do_compute_pixels(int x_size, int y_size);
+  void do_compute_pixels(int x_size, int y_size);
   Mutex _lock;
 
   float _l;

+ 48 - 22
panda/src/display/graphicsEngine.cxx

@@ -30,6 +30,7 @@
 #include "pStatClient.h"
 #include "pStatCollector.h"
 #include "mutexHolder.h"
+#include "cullFaceAttrib.h"
 #include "string_utils.h"
 
 #if defined(WIN32)
@@ -739,7 +740,7 @@ void GraphicsEngine::
 cull_and_draw_together(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
 
-  PT(SceneSetup) scene_setup = setup_scene(dr->get_camera(), gsg);
+  PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
   if (setup_gsg(gsg, scene_setup)) {
     DisplayRegionStack old_dr = gsg->push_display_region(dr);
     gsg->prepare_display_region();
@@ -819,7 +820,7 @@ cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
     cull_result = new CullResult(gsg);
   }
 
-  PT(SceneSetup) scene_setup = setup_scene(dr->get_camera(), gsg);
+  PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
   if (scene_setup != (SceneSetup *)NULL) {
     BinCullHandler cull_handler(cull_result);
     do_cull(&cull_handler, scene_setup, gsg);
@@ -964,7 +965,14 @@ do_flip_frame() {
 //               reason.
 ////////////////////////////////////////////////////////////////////
 PT(SceneSetup) GraphicsEngine::
-setup_scene(const NodePath &camera, GraphicsStateGuardian *gsg) {
+setup_scene(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
+  GraphicsOutput *window = dr->get_window();
+  // The window pointer shouldn't be NULL, since we presumably got to
+  // this particular DisplayRegion by walking through a list on a
+  // window.
+  nassertr(window != (GraphicsOutput *)NULL, NULL);
+
+  NodePath camera = dr->get_camera();
   if (camera.is_empty()) {
     // No camera, no draw.
     return NULL;
@@ -1007,18 +1015,26 @@ setup_scene(const NodePath &camera, GraphicsStateGuardian *gsg) {
   // The render transform is the same as the world transform, except
   // it is converted into the GSG's internal coordinate system.  This
   // is the transform that the GSG will apply to all of its vertices.
-  CPT(TransformState) cs_transform = TransformState::make_identity();
-  CoordinateSystem external_cs = gsg->get_coordinate_system();
-  CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
-  if (internal_cs != CS_default && internal_cs != external_cs) {
-    cs_transform = 
-      TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
+  CPT(TransformState) cs_transform = gsg->get_cs_transform();
+
+  CPT(RenderState) initial_state = camera_node->get_initial_state();
+
+  if (window->get_inverted()) {
+    // If the window is to be inverted, we must set the inverted flag
+    // on the SceneSetup object, so that the GSG will be able to
+    // invert the projection matrix at the last minute.
+    scene_setup->set_inverted(true);
+
+    // This also means we need to globally invert the sense of polygon
+    // vertex ordering.
+    initial_state = initial_state->compose(get_invert_polygon_state());
   }
 
   scene_setup->set_scene_root(scene_root);
   scene_setup->set_camera_path(camera);
   scene_setup->set_camera_node(camera_node);
   scene_setup->set_lens(lens);
+  scene_setup->set_initial_state(initial_state);
   scene_setup->set_camera_transform(camera_transform);
   scene_setup->set_world_transform(world_transform);
   scene_setup->set_cs_transform(cs_transform);
@@ -1041,7 +1057,6 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
   trav.set_cull_handler(cull_handler);
   trav.set_depth_offset_decals(gsg->depth_offset_decals());
   trav.set_scene(scene_setup);
-  trav.set_camera_mask(scene_setup->get_camera_node()->get_camera_mask());
   
   if (view_frustum_cull) {
     // If we're to be performing view-frustum culling, determine the
@@ -1108,22 +1123,13 @@ setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup) {
     return false;
   }
 
-  const Lens *lens = scene_setup->get_lens();
-  if (lens == (const Lens *)NULL) {
-    // No lens, no draw.
-    return false;
-  }
-
-  if (!gsg->set_lens(lens)) {
-    // The lens is inappropriate somehow.
+  if (!gsg->set_scene(scene_setup)) {
+    // The scene or lens is inappropriate somehow.
     display_cat.error()
-      << gsg->get_type() << " cannot render with " << lens->get_type()
-      << "\n";
+      << gsg->get_type() << " cannot render scene with specified lens.\n";
     return false;
   }
 
-  gsg->set_scene(scene_setup);
-
   return true;
 }
 
@@ -1271,6 +1277,26 @@ terminate_threads() {
   _threads.clear();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::get_invert_polygon_state
+//       Access: Protected, Static
+//  Description: Returns a RenderState for inverting the sense of
+//               polygon vertex ordering: if the scene graph specifies
+//               a clockwise ordering, this changes it to
+//               counterclockwise, and vice-versa.
+////////////////////////////////////////////////////////////////////
+const RenderState *GraphicsEngine::
+get_invert_polygon_state() {
+  // Once someone asks for this pointer, we hold its reference count
+  // and never free it.
+  static CPT(RenderState) state = (const RenderState *)NULL;
+  if (state == (const RenderState *)NULL) {
+    state = RenderState::make(CullFaceAttrib::make_reverse());
+  }
+
+  return state;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::get_window_renderer
 //       Access: Private

+ 3 - 2
panda/src/display/graphicsEngine.h

@@ -152,8 +152,7 @@ private:
   void do_flip_frame();
   INLINE void close_gsg(GraphicsPipe *pipe, GraphicsStateGuardian *gsg);
 
-  PT(SceneSetup) setup_scene(const NodePath &camera, 
-                             GraphicsStateGuardian *gsg);
+  PT(SceneSetup) setup_scene(GraphicsStateGuardian *gsg, DisplayRegion *dr);
   void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
                GraphicsStateGuardian *gsg);
   void do_draw(CullResult *cull_result, SceneSetup *scene_setup,
@@ -167,6 +166,8 @@ private:
   void do_resort_windows();
   void terminate_threads();
 
+  static const RenderState *get_invert_polygon_state();
+
   // The WindowRenderer class records the stages of the pipeline that
   // each thread (including the main thread, a.k.a. "app") should
   // process, and the list of windows for each stage.

+ 13 - 0
panda/src/display/graphicsOutput.I

@@ -188,6 +188,19 @@ get_one_shot() const {
   return _one_shot;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_inverted
+//       Access: Published
+//  Description: Returns the current setting of the inverted flag.
+//               When this is true, the scene is rendered into the
+//               window upside-down, flipped like a mirror along the X
+//               axis.  See set_inverted().
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+get_inverted() const {
+  return _inverted;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::clear_delete_flag
 //       Access: Published

+ 48 - 5
panda/src/display/graphicsOutput.cxx

@@ -25,6 +25,7 @@
 #include "renderBuffer.h"
 #include "indirectLess.h"
 #include "pStatTimer.h"
+#include "configVariableBool.h"
 
 TypeHandle GraphicsOutput::_type_handle;
 
@@ -57,6 +58,7 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   _sort = 0;
   _active = true;
   _one_shot = false;
+  _inverted = window_inverted;
   _delete_flag = false;
 
   int mode = gsg->get_properties().get_frame_buffer_mode();
@@ -69,6 +71,7 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   // which we may use internally for full-window operations like
   // clear() and get_screenshot().
   _default_display_region = make_display_region(0.0f, 1.0f, 0.0f, 1.0f);
+  _default_display_region->set_active(false);
 
   _display_regions_stale = false;
 
@@ -131,15 +134,17 @@ GraphicsOutput::
 //     Function: GraphicsOutput::detach_texture
 //       Access: Published
 //  Description: Disassociates the texture from the GraphicsOutput.
-//               It will no longer be filled as the frame renders, and
-//               it may be used (with its current contents) as a
-//               texture in its own right.
+//               The texture will no longer be filled as the frame
+//               renders, and it may be used (with its current
+//               contents) as an ordinary texture in its own right.
 ////////////////////////////////////////////////////////////////////
 void GraphicsOutput::
 detach_texture() {
   MutexHolder holder(_lock);
   _texture = NULL;
   _copy_texture = false;
+
+  set_inverted(window_inverted);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -160,6 +165,9 @@ setup_copy_texture(const string &name) {
   _texture->set_wrapv(Texture::WM_clamp);
 
   _copy_texture = true;
+
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+  set_inverted(_gsg->get_copy_texture_inverted());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -185,6 +193,40 @@ is_active() const {
   return _active && is_valid();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::set_inverted
+//       Access: Published
+//  Description: Changes the current setting of the inverted flag.
+//               When this is true, the scene is rendered into the
+//               window upside-down and backwards, that is, inverted
+//               as if viewed through a mirror placed on the floor.
+//
+//               This is primarily intended to support DirectX (and a
+//               few buggy OpenGL graphics drivers) that perform a
+//               framebuffer-to-texture copy upside-down from the
+//               usual OpenGL (and Panda) convention.  Panda will
+//               automatically set this flag for offscreen buffers on
+//               hardware that is known to do this, to compensate when
+//               rendering offscreen into a texture.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+set_inverted(bool inverted) {
+  if (_inverted != inverted) {
+    _inverted = inverted;
+
+    if (_y_size != 0) {
+      // All of our DisplayRegions need to recompute their pixel
+      // positions now.
+      TotalDisplayRegions::iterator dri;
+      for (dri = _total_display_regions.begin(); 
+           dri != _total_display_regions.end(); 
+           ++dri) {
+        (*dri)->compute_pixels(_x_size, _y_size);
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::set_sort
 //       Access: Published
@@ -539,8 +581,9 @@ end_frame() {
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
   _gsg->end_frame();
 
-  // By default, we copy the framebuffer to the texture at the end of
-  // the frame.  GraphicsBuffer objects that are set up to render
+  // If _copy_texture is true, it means we should copy the framebuffer
+  // to the GraphicsOutput's associated texture after the frame has
+  // rendered.  GraphicsBuffer objects that are set up to render
   // directly into texture memory don't need to do this; they will set
   // _copy_texture to false.
   if (_copy_texture) {

+ 4 - 0
panda/src/display/graphicsOutput.h

@@ -88,6 +88,9 @@ PUBLISHED:
   INLINE void set_one_shot(bool one_shot);
   INLINE bool get_one_shot() const;
 
+  void set_inverted(bool inverted);
+  INLINE bool get_inverted() const;
+
   INLINE void clear_delete_flag();
   INLINE bool get_delete_flag() const;
 
@@ -170,6 +173,7 @@ private:
 protected:
   bool _active;
   bool _one_shot;
+  bool _inverted;
   bool _delete_flag;
 
 protected:

+ 23 - 31
panda/src/display/graphicsPipeSelection.cxx

@@ -33,49 +33,41 @@ GraphicsPipeSelection *GraphicsPipeSelection::_global_ptr = NULL;
 ////////////////////////////////////////////////////////////////////
 GraphicsPipeSelection::
 GraphicsPipeSelection() {
-  pset<string> got_display_modules;
-
-  // First get the name of the default module from the load-display
-  // variable.  We get this explicitly from Configrc now (instead of
-  // retrieving it in config_display), in case this constructor is
-  // running at static init time.
-  string load_display = config_display.GetString("load-display", "");
-  load_display = trim_right(load_display);
-  size_t space = load_display.rfind(' ');
-  if (space != string::npos) {
-    // If there's a space, it indicates the name of the GraphicsPipe
-    // class to prefer.
-    _default_pipe_name = load_display.substr(space + 1);
-    load_display = trim_right(load_display.substr(0, space));
-  }
+  // We declare these variables here instead of in config_display, in
+  // case this constructor is running at static init time.
+  ConfigVariableString load_display
+    ("load-display", "*",
+     "Specify the name of the default graphics display library or "
+     "GraphicsPipe to load.  It is the name of a shared library (or * for "
+     "all libraries named in aux-display), optionally followed by the "
+     "name of the particular GraphicsPipe class to create.");
+  
+  ConfigVariableList aux_display
+    ("aux-display",
+     "Names each of the graphics display libraries that are available on "
+     "a particular platform.  This variable may be repeated several "
+     "times.  These libraries will be tried one at a time if the library "
+     "specified by load_display cannot be loaded.");
 
-  // Everything else is the name of the .dll (or .so) file to load.
-  _default_display_module = load_display;
+  _default_display_module = load_display.get_word(0);
+  _default_pipe_name = load_display.get_word(1);
 
   if (_default_display_module == "*") {
     // '*' or empty string is the key for all display modules.
     _default_display_module = string();
 
   } else if (!_default_display_module.empty()) {
-    // Don't insert a particular display more than once.
-    if (got_display_modules.insert(_default_display_module).second) {
-      _display_modules.push_back(_default_display_module);
-    }
+    _display_modules.push_back(_default_display_module);
   }
 
   // Also get the set of modules named in the various aux-display
   // Configrc variables.  We'll want to know this when we call
   // load_modules() later.
-  Config::ConfigTable::Symbol disp;
-  config_display.GetAll("aux-display", disp);
-
-  Config::ConfigTable::Symbol::iterator ci;
-  for (ci = disp.begin(); ci != disp.end(); ++ci) {
-    string aux_display = trim_right((*ci).Val());
-
-    // Don't insert a particular display more than once.
-    if (got_display_modules.insert(aux_display).second) {
-      _display_modules.push_back(aux_display);
+  int num_aux = aux_display.get_num_unique_values();
+  for (int i = 0; i < num_aux; i++) {
+    string name = aux_display.get_unique_value(i);
+    if (name != _default_display_module) {
+      _display_modules.push_back(name);
     }
   }
 

+ 69 - 76
panda/src/display/graphicsStateGuardian.I

@@ -140,17 +140,51 @@ get_max_texture_stages() const {
   return _max_texture_stages;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_copy_texture_inverted
+//       Access: Published
+//  Description: Returns true if this particular GSG has the property
+//               that any framebuffer-to-texture copy results in a
+//               texture that is upside-down and backwards from
+//               Panda's usual convention; that is, it copies into a
+//               texture from the bottom up instead of from the top
+//               down.
+//
+//               If this is true, then on offscreen GraphicsBuffer
+//               created for the purposes of rendering into a texture
+//               should be created with the invert flag set true, to
+//               compensate.  Panda will do this automatically if you
+//               create an offscreen buffer using
+//               GraphicsOutput::make_texture_buffer().
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_copy_texture_inverted() const {
+  // If this is set from a Config variable, that overrides.
+  if (copy_texture_inverted.has_value()) {
+    return copy_texture_inverted;
+  }
+
+  // Otherwise, use whatever behavior the GSG figured for itself.
+  return _copy_texture_inverted;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_scene
 //       Access: Public
 //  Description: Sets the SceneSetup object that indicates the initial
 //               camera position, etc.  This must be called before
-//               traversal begins.
+//               traversal begins.  Returns true if the scene is
+//               acceptable, false if something's wrong.
 ////////////////////////////////////////////////////////////////////
-INLINE void GraphicsStateGuardian::
+INLINE bool GraphicsStateGuardian::
 set_scene(SceneSetup *scene_setup) {
   _scene_setup = scene_setup;
+  _current_lens = scene_setup->get_lens();
+  if (_current_lens == (Lens *)NULL) {
+    return false;
+  }
+  return prepare_lens();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -315,10 +349,9 @@ get_current_display_region(void) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_current_lens
 //       Access: Public
-//  Description: Returns the current lens being rendered with, as set
-//               by the last call to push_lens() (or restored by
-//               pop_lens()).  This lens will be made active (if it is
-//               not already) by a call to prepare_lens().
+//  Description: Returns the current lens being used to render,
+//               according to the scene specified via the last call to
+//               set_scene().
 ////////////////////////////////////////////////////////////////////
 INLINE const Lens *GraphicsStateGuardian::
 get_current_lens() const {
@@ -403,90 +436,50 @@ pop_frame_buffer(FrameBufferStack &node) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::push_lens
-//       Access: Public
-//  Description: Saves the current lens information and sets up a new
-//               lens for rendering.  The return value from this
-//               function must eventually be passed to a matching
-//               pop_lens() call.
-//
-//               The new lens will not actually be made active for
-//               rendering until the next call to prepare_lens().
-//               This is a state-changing optimization.
-////////////////////////////////////////////////////////////////////
-INLINE LensStack GraphicsStateGuardian::
-push_lens(const Lens *lens) {
-  LensStack old;
-  old._lens = _current_lens;
-  old._stack_level = _lens_stack_level;
-  _lens_stack_level++;
-  _current_lens = lens;
-  return old;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::pop_lens
-//       Access: Public
-//  Description: Restores the lens previously in effect, before the
-//               matching call to push_lens().
-//
-//               The newly-restored lens will not actually be made
-//               active for rendering until the next call to
-//               prepare_lens().  This is a state-changing
-//               optimization.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsStateGuardian::
-pop_lens(LensStack &node) {
-  nassertv(_lens_stack_level > 0);
-  _lens_stack_level--;
-  nassertv(node._stack_level == _lens_stack_level);
-  _current_lens = node._lens;
-  node._stack_level = -1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::set_lens
-//       Access: Public
-//  Description: Sets a new lens for rendering without bothering to
-//               push or pop.  This replaces the lens most recently
-//               pushed, if any.  There is no need to call
-//               prepare_lens() following this call.
-//
-//               The return value is true if the lens is acceptable,
-//               false if it is not.
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsStateGuardian::
-set_lens(const Lens *lens) {
-  _current_lens = lens;
-  return prepare_lens();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::set_coordinate_system
+//     Function: GraphicsStateGuardian::get_coordinate_system
 //       Access: Public
-//  Description: Changes the coordinate system in effect on this
+//  Description: Returns the coordinate system in effect on this
 //               particular gsg.  Normally, this will be the default
 //               coordinate system, but it might be set differently at
 //               runtime.
 ////////////////////////////////////////////////////////////////////
-INLINE void GraphicsStateGuardian::
-set_coordinate_system(CoordinateSystem cs) {
-  _coordinate_system = cs;
+INLINE CoordinateSystem GraphicsStateGuardian::
+get_coordinate_system() const {
+  return _coordinate_system;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::get_coordinate_system
+//     Function: GraphicsStateGuardian::get_internal_coordinate_system
 //       Access: Public
-//  Description: Returns the coordinate system in effect on this
-//               particular gsg.  Normally, this will be the default
-//               coordinate system, but it might be set differently at
-//               runtime.
+//  Description: Returns the coordinate system used internally by the
+//               GSG, if any one particular coordinate system is used.
+//               The default, CS_default, indicates that the GSG can
+//               use any coordinate system.
+//
+//               If this returns other than CS_default, the
+//               GraphicsEngine will automatically convert all
+//               transforms into the indicated coordinate system.
 ////////////////////////////////////////////////////////////////////
 INLINE CoordinateSystem GraphicsStateGuardian::
-get_coordinate_system() const {
+get_internal_coordinate_system() const {
   return _coordinate_system;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_cs_transform
+//       Access: Public
+//  Description: Returns a transform that converts from the GSG's
+//               external coordinate system (as returned by
+//               get_coordinate_system()) to its internal coordinate
+//               system (as returned by
+//               get_internal_coordinate_system()).  This is used for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *GraphicsStateGuardian::
+get_cs_transform() const {
+  return _cs_transform;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_light
 //       Access: Protected

+ 39 - 21
panda/src/display/graphicsStateGuardian.cxx

@@ -69,9 +69,14 @@ TypeHandle GraphicsStateGuardian::_type_handle;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 GraphicsStateGuardian::
-GraphicsStateGuardian(const FrameBufferProperties &properties) {
-  _properties = properties;
-  _coordinate_system = get_default_coordinate_system();
+GraphicsStateGuardian(const FrameBufferProperties &properties,
+                      CoordinateSystem internal_coordinate_system) :
+  _internal_coordinate_system(internal_coordinate_system),
+  _properties(properties)
+{
+  _coordinate_system = CS_invalid;
+  set_coordinate_system(get_default_coordinate_system());
+
   _current_display_region = (DisplayRegion*)0L;
   _current_lens = (Lens *)NULL;
   _needs_reset = true;
@@ -83,6 +88,10 @@ GraphicsStateGuardian(const FrameBufferProperties &properties) {
   // supported).  A derived GSG may set this differently if it
   // supports multitexturing.
   _max_texture_stages = 1;
+
+  // Initially, we set this to false; a GSG that knows it has this
+  // property should set it to true.
+  _copy_texture_inverted = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -287,11 +296,10 @@ clear(DrawableRegion *clearable) {
 //     Function: GraphicsStateGuardian::prepare_lens
 //       Access: Public, Virtual
 //  Description: Makes the current lens (whichever lens was most
-//               recently specified with push_lens()) active, so that
-//               it will transform future rendered geometry.  Normally
-//               this is only called from the draw process, and
-//               usually it is called immediately after a call to
-//               push_lens().
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
 //
 //               The return value is true if the lens is acceptable,
 //               false if it is not.
@@ -550,21 +558,31 @@ finish_decal() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::get_internal_coordinate_system
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return the
-//               coordinate system used internally by the GSG, if any
-//               one particular coordinate system is used.  The
-//               default, CS_default, indicates that the GSG can use
-//               any coordinate system.
+//     Function: GraphicsStateGuardian::set_coordinate_system
+//       Access: Public
+//  Description: Changes the coordinate system in effect on this
+//               particular gsg.  This is also called the "external"
+//               coordinate system, since it is the coordinate system
+//               used by the scene graph, external to to GSG.
 //
-//               If this returns other than CS_default, the
-//               GraphicsEngine will automatically convert all
-//               transforms into the indicated coordinate system.
+//               Normally, this will be the default coordinate system,
+//               but it might be set differently at runtime.
 ////////////////////////////////////////////////////////////////////
-CoordinateSystem GraphicsStateGuardian::
-get_internal_coordinate_system() const {
-  return CS_default;
+void GraphicsStateGuardian::
+set_coordinate_system(CoordinateSystem cs) {
+  _coordinate_system = cs;
+
+  // Changing the external coordinate system changes the cs_transform.
+  if (_internal_coordinate_system == CS_default ||
+      _internal_coordinate_system == _coordinate_system) {
+    _cs_transform = TransformState::make_identity();
+
+  } else {
+    _cs_transform = 
+      TransformState::make_mat
+      (LMatrix4f::convert_mat(_coordinate_system,
+                              _internal_coordinate_system));
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 11 - 8
panda/src/display/graphicsStateGuardian.h

@@ -66,7 +66,8 @@ class EXPCL_PANDA GraphicsStateGuardian : public GraphicsStateGuardianBase {
   // Interfaces all GSGs should have
   //
 public:
-  GraphicsStateGuardian(const FrameBufferProperties &properties);
+  GraphicsStateGuardian(const FrameBufferProperties &properties,
+                        CoordinateSystem internal_coordinate_system);
   virtual ~GraphicsStateGuardian();
 
 PUBLISHED:
@@ -82,9 +83,10 @@ PUBLISHED:
   INLINE const GraphicsThreadingModel &get_threading_model() const;
 
   INLINE int get_max_texture_stages() const;
+  INLINE bool get_copy_texture_inverted() const;
 
 public:
-  INLINE void set_scene(SceneSetup *scene_setup);
+  INLINE bool set_scene(SceneSetup *scene_setup);
   INLINE SceneSetup *get_scene() const;
 
   virtual PreparedGraphicsObjects *get_prepared_objects();
@@ -148,13 +150,11 @@ public:
                                             const DisplayRegion *dr);
   INLINE void pop_frame_buffer(FrameBufferStack &node);
 
-  INLINE LensStack push_lens(const Lens *lens);
-  INLINE void pop_lens(LensStack &stack);
-  INLINE bool set_lens(const Lens *lens);
-
-  INLINE void set_coordinate_system(CoordinateSystem cs);
+  void set_coordinate_system(CoordinateSystem cs);
   INLINE CoordinateSystem get_coordinate_system() const;
-  virtual CoordinateSystem get_internal_coordinate_system() const;
+  INLINE CoordinateSystem get_internal_coordinate_system() const;
+
+  INLINE const TransformState *get_cs_transform() const;
 
   virtual void issue_transform(const TransformState *transform);
   virtual void issue_color_scale(const ColorScaleAttrib *attrib);
@@ -250,6 +250,8 @@ protected:
   int _force_normals;
 
   CoordinateSystem _coordinate_system;
+  CoordinateSystem _internal_coordinate_system;
+  CPT(TransformState) _cs_transform;
 
   Colorf _scene_graph_color;
   bool _has_scene_graph_color;
@@ -277,6 +279,7 @@ protected:
 
   PT(PreparedGraphicsObjects) _prepared_objects;
   int _max_texture_stages;
+  bool _copy_texture_inverted;
 
 public:
   // Statistics

+ 12 - 24
panda/src/dxgsg7/dxGraphicsStateGuardian7.cxx

@@ -229,7 +229,7 @@ set_color_clear_value(const Colorf& value) {
 ////////////////////////////////////////////////////////////////////
 DXGraphicsStateGuardian7::
 DXGraphicsStateGuardian7(const FrameBufferProperties &properties) :
-  GraphicsStateGuardian(properties) 
+  GraphicsStateGuardian(properties, CS_yup_left) 
 {
     // allocate local buffers used during rendering
 
@@ -776,11 +776,10 @@ prepare_display_region() {
 //     Function: DXGraphicsStateGuardian7::prepare_lens
 //       Access: Public, Virtual
 //  Description: Makes the current lens (whichever lens was most
-//               recently specified with push_lens()) active, so that
-//               it will transform future rendered geometry.  Normally
-//               this is only called from the draw process, and
-//               usually it is called immediately after a call to
-//               push_lens().
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
 //
 //               The return value is true if the lens is acceptable,
 //               false if it is not.
@@ -816,6 +815,13 @@ prepare_lens() {
   LMatrix4f new_projection_mat =
     convert_mat * projection_mat * rescale_mat;
 
+  if (_scene_setup->get_inverted()) {
+    // If the scene is supposed to be inverted, then invert the
+    // projection matrix.
+    static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
+    new_projection_mat *= invert_mat;
+  }
+
   HRESULT hr = 
     hr = _pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION,
                                    (LPD3DMATRIX)new_projection_mat.get_data());
@@ -4304,24 +4310,6 @@ depth_offset_decals() {
   return dx_depth_offset_decals;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: DXGraphicsStateGuardian7::get_internal_coordinate_system
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return the
-//               coordinate system used internally by the GSG, if any
-//               one particular coordinate system is used.  The
-//               default, CS_default, indicates that the GSG can use
-//               any coordinate system.
-//
-//               If this returns other than CS_default, the
-//               GraphicsEngine will automatically convert all
-//               transforms into the indicated coordinate system.
-////////////////////////////////////////////////////////////////////
-CoordinateSystem DXGraphicsStateGuardian7::
-get_internal_coordinate_system() const {
-  return CS_yup_left;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian7::compute_distance_to
 //       Access: Public, Virtual

+ 0 - 1
panda/src/dxgsg7/dxGraphicsStateGuardian7.h

@@ -133,7 +133,6 @@ public:
 
   virtual bool depth_offset_decals();
 
-  virtual CoordinateSystem get_internal_coordinate_system() const;
   INLINE float compute_distance_to(const LPoint3f &point) const;
   virtual void set_color_clear_value(const Colorf& value);
 

+ 13 - 24
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -367,7 +367,8 @@ reset_panda_gsg(void) {
 ////////////////////////////////////////////////////////////////////
 DXGraphicsStateGuardian8::
 DXGraphicsStateGuardian8(const FrameBufferProperties &properties) :
-  GraphicsStateGuardian(properties) {
+  GraphicsStateGuardian(properties, CS_yup_left)
+{
 
     reset_panda_gsg();
 
@@ -991,11 +992,10 @@ prepare_display_region() {
 //     Function: DXGraphicsStateGuardian8::prepare_lens
 //       Access: Public, Virtual
 //  Description: Makes the current lens (whichever lens was most
-//               recently specified with push_lens()) active, so that
-//               it will transform future rendered geometry.  Normally
-//               this is only called from the draw process, and
-//               usually it is called immediately after a call to
-//               push_lens().
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
 //
 //               The return value is true if the lens is acceptable,
 //               false if it is not.
@@ -1031,6 +1031,13 @@ prepare_lens() {
   LMatrix4f new_projection_mat =
     convert_mat * projection_mat * rescale_mat;
 
+  if (_scene_setup->get_inverted()) {
+    // If the scene is supposed to be inverted, then invert the
+    // projection matrix.
+    static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
+    new_projection_mat *= invert_mat;
+  }
+
   HRESULT hr = 
     _pD3DDevice->SetTransform(D3DTS_PROJECTION,
                               (D3DMATRIX*)new_projection_mat.get_data());
@@ -4077,24 +4084,6 @@ depth_offset_decals() {
   return false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: DXGraphicsStateGuardian8::get_internal_coordinate_system
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return the
-//               coordinate system used internally by the GSG, if any
-//               one particular coordinate system is used.  The
-//               default, CS_default, indicates that the GSG can use
-//               any coordinate system.
-//
-//               If this returns other than CS_default, the
-//               GraphicsEngine will automatically convert all
-//               transforms into the indicated coordinate system.
-////////////////////////////////////////////////////////////////////
-CoordinateSystem DXGraphicsStateGuardian8::
-get_internal_coordinate_system() const {
-  return CS_yup_left;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian8::set_draw_buffer
 //       Access: Protected

+ 0 - 1
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -139,7 +139,6 @@ public:
 
   virtual bool depth_offset_decals();
 
-  virtual CoordinateSystem get_internal_coordinate_system() const;
   INLINE float compute_distance_to(const LPoint3f &point) const;
   virtual void set_color_clear_value(const Colorf& value);
 

+ 13 - 24
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -366,7 +366,8 @@ reset_panda_gsg(void) {
 ////////////////////////////////////////////////////////////////////
 DXGraphicsStateGuardian9::
 DXGraphicsStateGuardian9(const FrameBufferProperties &properties) :
-  GraphicsStateGuardian(properties) {
+  GraphicsStateGuardian(properties, CS_yup_left) 
+{
 
     reset_panda_gsg();
 
@@ -983,11 +984,10 @@ prepare_display_region() {
 //     Function: DXGraphicsStateGuardian9::prepare_lens
 //       Access: Public, Virtual
 //  Description: Makes the current lens (whichever lens was most
-//               recently specified with push_lens()) active, so that
-//               it will transform future rendered geometry.  Normally
-//               this is only called from the draw process, and
-//               usually it is called immediately after a call to
-//               push_lens().
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
 //
 //               The return value is true if the lens is acceptable,
 //               false if it is not.
@@ -1023,6 +1023,13 @@ prepare_lens() {
   LMatrix4f new_projection_mat =
     convert_mat * projection_mat * rescale_mat;
 
+  if (_scene_setup->get_inverted()) {
+    // If the scene is supposed to be inverted, then invert the
+    // projection matrix.
+    static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
+    new_projection_mat *= invert_mat;
+  }
+
   HRESULT hr = 
     _pD3DDevice->SetTransform(D3DTS_PROJECTION,
                               (D3DMATRIX*)new_projection_mat.get_data());
@@ -4079,24 +4086,6 @@ depth_offset_decals() {
   return false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: DXGraphicsStateGuardian9::get_internal_coordinate_system
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return the
-//               coordinate system used internally by the GSG, if any
-//               one particular coordinate system is used.  The
-//               default, CS_default, indicates that the GSG can use
-//               any coordinate system.
-//
-//               If this returns other than CS_default, the
-//               GraphicsEngine will automatically convert all
-//               transforms into the indicated coordinate system.
-////////////////////////////////////////////////////////////////////
-CoordinateSystem DXGraphicsStateGuardian9::
-get_internal_coordinate_system() const {
-  return CS_yup_left;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian9::set_draw_buffer
 //       Access: Protected

+ 0 - 1
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -140,7 +140,6 @@ public:
 
   virtual bool depth_offset_decals();
 
-  virtual CoordinateSystem get_internal_coordinate_system() const;
   INLINE float compute_distance_to(const LPoint3f &point) const;
   virtual void set_color_clear_value(const Colorf& value);
 

+ 12 - 24
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -272,7 +272,7 @@ fix_component_ordering(GLenum external_format, PixelBuffer *pb) {
 ////////////////////////////////////////////////////////////////////
 CLP(GraphicsStateGuardian)::
 CLP(GraphicsStateGuardian)(const FrameBufferProperties &properties) :
-  GraphicsStateGuardian(properties) 
+  GraphicsStateGuardian(properties, CS_yup_right) 
 {
   _error_count = 0;
 #ifdef HAVE_CGGL
@@ -640,11 +640,10 @@ prepare_display_region() {
 //     Function: CLP(GraphicsStateGuardian)::prepare_lens
 //       Access: Public, Virtual
 //  Description: Makes the current lens (whichever lens was most
-//               recently specified with push_lens()) active, so that
-//               it will transform future rendered geometry.  Normally
-//               this is only called from the draw process, and
-//               usually it is called immediately after a call to
-//               push_lens().
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
 //
 //               The return value is true if the lens is acceptable,
 //               false if it is not.
@@ -672,6 +671,13 @@ prepare_lens() {
     LMatrix4f::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
     projection_mat;
 
+  if (_scene_setup->get_inverted()) {
+    // If the scene is supposed to be inverted, then invert the
+    // projection matrix.
+    static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
+    new_projection_mat *= invert_mat;
+  }
+
 #ifdef GSG_VERBOSE
   GLCAT.spam()
     << "glMatrixMode(GL_PROJECTION): " << new_projection_mat << endl;
@@ -2822,24 +2828,6 @@ depth_offset_decals() {
   return CLP(depth_offset_decals);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CLP(GraphicsStateGuardian)::get_internal_coordinate_system
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return the
-//               coordinate system used internally by the GSG, if any
-//               one particular coordinate system is used.  The
-//               default, CS_default, indicates that the GSG can use
-//               any coordinate system.
-//
-//               If this returns other than CS_default, the
-//               GraphicsEngine will automatically convert all
-//               transforms into the indicated coordinate system.
-////////////////////////////////////////////////////////////////////
-CoordinateSystem CLP(GraphicsStateGuardian)::
-get_internal_coordinate_system() const {
-  return CS_yup_right;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::compute_distance_to
 //       Access: Public, Virtual

+ 0 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -134,7 +134,6 @@ public:
 
   virtual bool depth_offset_decals();
 
-  virtual CoordinateSystem get_internal_coordinate_system() const;
   virtual float compute_distance_to(const LPoint3f &point) const;
 
   void print_gfx_visual();

+ 1 - 1
panda/src/gobj/lens.cxx

@@ -556,7 +556,7 @@ set_view_mat(const LMatrix4f &view_mat) {
   _lens_mat = view_mat;
   adjust_user_flags(UF_view_vector | UF_view_hpr | UF_iod_offset,
                     UF_view_mat);
-  adjust_comp_flags(CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset,
+  adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset,
                     CF_lens_mat);
   throw_change_event();
 }

+ 3 - 1
panda/src/pgraph/cullTraverser.I

@@ -27,10 +27,12 @@
 INLINE void CullTraverser::
 set_scene(SceneSetup *scene_setup) {
   _scene_setup = scene_setup;
+  _initial_state = scene_setup->get_initial_state();
+
   const Camera *camera = scene_setup->get_camera_node();
   _tag_state_key = camera->get_tag_state_key();
   _has_tag_state_key = !_tag_state_key.empty();
-  _initial_state = camera->get_initial_state();
+  _camera_mask = camera->get_camera_mask();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 51 - 0
panda/src/pgraph/sceneSetup.I

@@ -24,6 +24,8 @@
 ////////////////////////////////////////////////////////////////////
 INLINE SceneSetup::
 SceneSetup() {
+  _inverted = false;
+  _initial_state = RenderState::make_empty();
   _camera_transform = TransformState::make_identity();
   _world_transform = TransformState::make_identity();
   _cs_transform = TransformState::make_identity();
@@ -109,6 +111,32 @@ get_lens() const {
   return _lens;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_inverted
+//       Access: Published
+//  Description: Changes the current setting of the inverted flag.
+//               When this is true, the scene is rendered into the
+//               window upside-down and backwards, that is, inverted
+//               as if viewed through a mirror placed on the floor.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_inverted(bool inverted) {
+  _inverted = inverted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_inverted
+//       Access: Published
+//  Description: Returns the current setting of the inverted flag.
+//               When this is true, the scene is rendered into the
+//               window upside-down, flipped like a mirror along the X
+//               axis.
+////////////////////////////////////////////////////////////////////
+INLINE bool SceneSetup::
+get_inverted() const {
+  return _inverted;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_cull_center
 //       Access: Public
@@ -126,6 +154,29 @@ get_cull_center() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_initial_state
+//       Access: Published
+//  Description: Sets the initial state which is applied to all nodes
+//               in the scene, as if it were set at the top of the
+//               scene graph.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_initial_state(const RenderState *state) {
+  _initial_state = state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_initial_state
+//       Access: Published
+//  Description: Returns the initial state as set by a previous call
+//               to set_initial_state().
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *SceneSetup::
+get_initial_state() const {
+  return _initial_state;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_camera_transform
 //       Access: Public

+ 8 - 0
panda/src/pgraph/sceneSetup.h

@@ -50,8 +50,14 @@ public:
   INLINE void set_lens(const Lens *lens);
   INLINE const Lens *get_lens() const;
 
+  INLINE void set_inverted(bool inverted);
+  INLINE bool get_inverted() const;
+
   INLINE const NodePath &get_cull_center() const;
 
+  INLINE void set_initial_state(const RenderState *initial_state);
+  INLINE const RenderState *get_initial_state() const;
+
   INLINE void set_camera_transform(const TransformState *camera_transform);
   INLINE const TransformState *get_camera_transform() const;
 
@@ -68,6 +74,8 @@ private:
   NodePath _camera_path;
   PT(Camera) _camera_node;
   CPT(Lens) _lens;
+  bool _inverted;
+  CPT(RenderState) _initial_state;
   CPT(TransformState) _camera_transform;
   CPT(TransformState) _world_transform;
   CPT(TransformState) _cs_transform;