Browse Source

automatic stereo cameras for stereo windows

David Rose 20 years ago
parent
commit
264fc31115

+ 43 - 16
direct/src/showbase/ShowBase.py

@@ -746,26 +746,19 @@ class ShowBase(DirectObject.DirectObject):
         return aspectRatio
         return aspectRatio
 
 
     def makeCamera(self, win, sort = 0, scene = None,
     def makeCamera(self, win, sort = 0, scene = None,
-                   displayRegion = (0, 1, 0, 1), stereoChannel = None,
-                   aspectRatio = None,
+                   displayRegion = (0, 1, 0, 1), stereo = None,
+                   aspectRatio = None, clearDepth = 0, clearColor = None,
                    lens = None, camName = 'cam'):
                    lens = None, camName = 'cam'):
         """
         """
         Makes a new 3-d camera associated with the indicated window,
         Makes a new 3-d camera associated with the indicated window,
         and creates a display region in the indicated subrectangle.
         and creates a display region in the indicated subrectangle.
-        """
-        dr = win.makeDisplayRegion(*displayRegion)
-        dr.setSort(sort)
-
-        if stereoChannel != None:
-            dr.setStereoChannel(stereoChannel)
-
-        if scene == None:
-            scene = self.render
 
 
-        # By default, we do not clear 3-d display regions (the entire
-        # window will be cleared, which is normally sufficient).
-
-        # Now make a new Camera node.
+        If stereo is True, then a stereo camera is created, with a
+        pair of DisplayRegions.  If stereo is False, then a standard
+        camera is created.  If stereo is None or omitted, a stereo
+        camera is created if the window says it can render in stereo.
+        """
+        # First, make a new Camera node.
         camNode = Camera(camName)
         camNode = Camera(camName)
         if lens == None:
         if lens == None:
             lens = PerspectiveLens()
             lens = PerspectiveLens()
@@ -776,6 +769,9 @@ class ShowBase(DirectObject.DirectObject):
 
 
         camNode.setLens(lens)
         camNode.setLens(lens)
 
 
+        if scene != None:
+            camNode.setScene(scene)
+
         # self.camera is the parent node of all cameras: a node that
         # self.camera is the parent node of all cameras: a node that
         # we can move around to move all cameras as a group.
         # we can move around to move all cameras as a group.
         if self.camera == None:
         if self.camera == None:
@@ -783,7 +779,6 @@ class ShowBase(DirectObject.DirectObject):
             __builtins__["camera"] = self.camera
             __builtins__["camera"] = self.camera
 
 
         cam = self.camera.attachNewNode(camNode)
         cam = self.camera.attachNewNode(camNode)
-        dr.setCamera(cam)
 
 
         if self.cam == None:
         if self.cam == None:
             self.cam = cam
             self.cam = cam
@@ -792,6 +787,38 @@ class ShowBase(DirectObject.DirectObject):
 
 
         self.camList.append(cam)
         self.camList.append(cam)
 
 
+        # Now, make a DisplayRegion for the camera.
+        dr = win.makeDisplayRegion(*displayRegion)
+        dr.setSort(sort)
+
+        # By default, we do not clear 3-d display regions (the entire
+        # window will be cleared, which is normally sufficient).  But
+        # we will if clearDepth is specified.
+        if clearDepth:
+            dr.setClearDepthActive(1)
+        if clearColor:
+            dr.setClearColorActive(1)
+            dr.setClearColor(clearColor)
+
+        dr.setCamera(cam)
+
+        if stereo == None:
+            stereo = win.isStereo()
+        if stereo:
+            # A stereo camera!  The first DisplayRegion becomes the
+            # left channel.
+            dr.setStereoChannel(Lens.SCLeft)
+
+            # So we'll need another DisplayRegion for the right channel.
+            dr = win.makeDisplayRegion(*displayRegion)
+            dr.setSort(sort)
+            dr.setClearDepthActive(1)
+            if clearColor:
+                dr.setClearColorActive(1)
+                dr.setClearColor(clearColor)
+            dr.setCamera(cam)
+            dr.setStereoChannel(Lens.SCRight)
+
         return cam
         return cam
 
 
     def makeCamera2d(self, win, sort = 10,
     def makeCamera2d(self, win, sort = 10,

+ 12 - 0
panda/src/display/config_display.cxx

@@ -150,6 +150,18 @@ ConfigVariableBool window_inverted
           "they will render upside-down and backwards.  Normally this is useful only "
           "they will render upside-down and backwards.  Normally this is useful only "
           "for debugging."));
           "for debugging."));
 
 
+ConfigVariableBool red_blue_stereo
+("red-blue-stereo", false,
+ PRC_DESC("Set this true to create windows with red-blue stereo mode enabled "
+	  "by default, if the framebuffer does not support true stereo "
+	  "rendering."));
+
+ConfigVariableBool invert_red_blue_stereo
+("invert-red-blue-stereo", false,
+ PRC_DESC("When this is true, the red and blue colors of red-blue stereo mode "
+	  "are inverted from their default convention, so the blue is on the "
+	  "left and red is on the right."));
+
 ConfigVariableBool depth_offset_decals
 ConfigVariableBool depth_offset_decals
 ("depth-offset-decals", false,
 ("depth-offset-decals", false,
  PRC_DESC("Set this true to allow decals to be implemented via the advanced "
  PRC_DESC("Set this true to allow decals to be implemented via the advanced "

+ 2 - 0
panda/src/display/config_display.h

@@ -57,6 +57,8 @@ extern EXPCL_PANDA ConfigVariableBool support_render_texture;
 extern EXPCL_PANDA ConfigVariableBool support_rescale_normal;
 extern EXPCL_PANDA ConfigVariableBool support_rescale_normal;
 extern EXPCL_PANDA ConfigVariableBool copy_texture_inverted;
 extern EXPCL_PANDA ConfigVariableBool copy_texture_inverted;
 extern EXPCL_PANDA ConfigVariableBool window_inverted;
 extern EXPCL_PANDA ConfigVariableBool window_inverted;
+extern EXPCL_PANDA ConfigVariableBool red_blue_stereo;
+extern EXPCL_PANDA ConfigVariableBool invert_red_blue_stereo;
 extern EXPCL_PANDA ConfigVariableBool depth_offset_decals;
 extern EXPCL_PANDA ConfigVariableBool depth_offset_decals;
 extern EXPCL_PANDA ConfigVariableBool auto_generate_mipmaps;
 extern EXPCL_PANDA ConfigVariableBool auto_generate_mipmaps;
 extern EXPCL_PANDA ConfigVariableBool color_scale_via_lighting;
 extern EXPCL_PANDA ConfigVariableBool color_scale_via_lighting;

+ 14 - 0
panda/src/display/frameBufferProperties.I

@@ -121,6 +121,20 @@ is_single_buffered() const {
   return (_frame_buffer_mode & FM_buffer) == FM_single_buffer;
   return (_frame_buffer_mode & FM_buffer) == FM_single_buffer;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FrameBufferProperties::is_stereo
+//       Access: Published
+//  Description: Returns true if the frame buffer indicates a
+//               hardware stereo mode, false otherwise.  This is a
+//               convenience function to access this useful tidbit of
+//               data.
+////////////////////////////////////////////////////////////////////
+INLINE bool FrameBufferProperties::
+is_stereo() const {
+  nassertr(has_frame_buffer_mode(), false);
+  return (_frame_buffer_mode & FM_stereo) != 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: FrameBufferProperties::set_depth_bits
 //     Function: FrameBufferProperties::set_depth_bits
 //       Access: Published
 //       Access: Published

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

@@ -66,7 +66,7 @@ PUBLISHED:
   INLINE void clear_frame_buffer_mode();
   INLINE void clear_frame_buffer_mode();
 
 
   INLINE bool is_single_buffered() const;
   INLINE bool is_single_buffered() const;
-
+  INLINE bool is_stereo() const;
   INLINE void set_depth_bits(int depth_bits);
   INLINE void set_depth_bits(int depth_bits);
   INLINE int get_depth_bits() const;
   INLINE int get_depth_bits() const;
   INLINE bool has_depth_bits() const;
   INLINE bool has_depth_bits() const;

+ 66 - 0
panda/src/display/graphicsEngine.cxx

@@ -1178,6 +1178,31 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
     initial_state = initial_state->compose(get_invert_polygon_state());
     initial_state = initial_state->compose(get_invert_polygon_state());
   }
   }
 
 
+  if (window->get_red_blue_stereo()) {
+    // If the window has red-blue stereo mode, apply the appropriate
+    // color mask to the initial state.
+    switch (dr->get_stereo_channel()) {
+    case Lens::SC_left:
+      if (invert_red_blue_stereo) {
+	initial_state = initial_state->compose(get_blue_channel_state());
+      } else {
+	initial_state = initial_state->compose(get_red_channel_state());
+      }
+      break;
+
+    case Lens::SC_right:
+      if (invert_red_blue_stereo) {
+	initial_state = initial_state->compose(get_red_channel_state());
+      } else {
+	initial_state = initial_state->compose(get_blue_channel_state());
+      }
+      break;
+
+    case Lens::SC_both:
+      break;
+    }
+  }
+
   scene_setup->set_display_region(dr);
   scene_setup->set_display_region(dr);
   scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height());
   scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height());
   scene_setup->set_scene_root(scene_root);
   scene_setup->set_scene_root(scene_root);
@@ -1473,7 +1498,9 @@ pstats_count_cycler_type(TypeHandle type, int count, void *data) {
   }
   }
   (*ci).second.set_level(count);
   (*ci).second.set_level(count);
 }
 }
+#endif // DO_PSTATS
 
 
+#ifdef DO_PSTATS
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::pstats_count_dirty_cycler_type
 //     Function: GraphicsEngine::pstats_count_dirty_cycler_type
 //       Access: Private, Static
 //       Access: Private, Static
@@ -1492,6 +1519,7 @@ pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data) {
   (*ci).second.set_level(count);
   (*ci).second.set_level(count);
 }
 }
 #endif // DO_PSTATS
 #endif // DO_PSTATS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::get_invert_polygon_state
 //     Function: GraphicsEngine::get_invert_polygon_state
 //       Access: Protected, Static
 //       Access: Protected, Static
@@ -1512,6 +1540,44 @@ get_invert_polygon_state() {
   return state;
   return state;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::get_red_channel_state
+//       Access: Protected, Static
+//  Description: Returns a RenderState for rendering only to the red
+//               channel of the color buffer, for implementing
+//               red-blue stereo.
+////////////////////////////////////////////////////////////////////
+const RenderState *GraphicsEngine::
+get_red_channel_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(ColorWriteAttrib::make(ColorWriteAttrib::C_red));
+  }
+
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::get_blue_channel_state
+//       Access: Protected, Static
+//  Description: Returns a RenderState for rendering only to the blue
+//               channel of the color buffer, for implementing
+//               red-blue stereo.
+////////////////////////////////////////////////////////////////////
+const RenderState *GraphicsEngine::
+get_blue_channel_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(ColorWriteAttrib::make(ColorWriteAttrib::C_blue));
+  }
+
+  return state;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::get_window_renderer
 //     Function: GraphicsEngine::get_window_renderer
 //       Access: Private
 //       Access: Private

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

@@ -180,6 +180,8 @@ private:
 #endif  // DO_PSTATS
 #endif  // DO_PSTATS
 
 
   static const RenderState *get_invert_polygon_state();
   static const RenderState *get_invert_polygon_state();
+  static const RenderState *get_red_channel_state();
+  static const RenderState *get_blue_channel_state();
 
 
   // The WindowRenderer class records the stages of the pipeline that
   // The WindowRenderer class records the stages of the pipeline that
   // each thread (including the main thread, a.k.a. "app") should
   // each thread (including the main thread, a.k.a. "app") should

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

@@ -225,6 +225,48 @@ get_inverted() const {
   return _inverted;
   return _inverted;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::set_red_blue_stereo
+//       Access: Published
+//  Description: Enables red-blue stereo mode on this particular
+//               window.  When red-blue stereo mode is in effect,
+//               DisplayRegions that have the "left" channel set will
+//               render in the red channel only, while DisplayRegions
+//               that have the "right" channel set will render in the
+//               blue channel only.
+//
+//               This can be used to achieve a cheesy stereo mode in
+//               the absence of hardware-supported stereo.
+////////////////////////////////////////////////////////////////////
+INLINE void GraphicsOutput::
+set_red_blue_stereo(bool red_blue_stereo) {
+  _red_blue_stereo = red_blue_stereo;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_red_blue_stereo
+//       Access: Published
+//  Description: Returns whether red-blue stereo mode is in effect for
+//               this particular window.  See set_red_blue_stereo().
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+get_red_blue_stereo() const {
+  return _red_blue_stereo;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::is_stereo
+//       Access: Published
+//  Description: Returns Returns true if this window can render stereo
+//               DisplayRegions, either through red-blue stereo (see
+//               set_red_blue_stereo()) or through true hardware
+//               stereo rendering.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+is_stereo() const {
+  return _red_blue_stereo || _gsg->get_properties().is_stereo();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::clear_delete_flag
 //     Function: GraphicsOutput::clear_delete_flag
 //       Access: Published
 //       Access: Published

+ 2 - 2
panda/src/display/graphicsOutput.cxx

@@ -88,12 +88,12 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   _active = true;
   _active = true;
   _one_shot = false;
   _one_shot = false;
   _inverted = window_inverted;
   _inverted = window_inverted;
+  _red_blue_stereo = false;
   _delete_flag = false;
   _delete_flag = false;
   _texture_card = 0;
   _texture_card = 0;
   _trigger_copy = false;
   _trigger_copy = false;
 
 
-  int mode = gsg->get_properties().get_frame_buffer_mode();
-  if ((mode & FrameBufferProperties::FM_buffer) == FrameBufferProperties::FM_single_buffer) {
+  if (gsg->get_properties().is_single_buffered()) {
     // Single buffered; we must draw into the front buffer.
     // Single buffered; we must draw into the front buffer.
     _draw_buffer_type = RenderBuffer::T_front;
     _draw_buffer_type = RenderBuffer::T_front;
   }
   }

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

@@ -113,6 +113,10 @@ PUBLISHED:
   void set_inverted(bool inverted);
   void set_inverted(bool inverted);
   INLINE bool get_inverted() const;
   INLINE bool get_inverted() const;
 
 
+  INLINE void set_red_blue_stereo(bool red_blue_stereo);
+  INLINE bool get_red_blue_stereo() const;
+  INLINE bool is_stereo() const;
+
   INLINE void clear_delete_flag();
   INLINE void clear_delete_flag();
   INLINE bool get_delete_flag() const;
   INLINE bool get_delete_flag() const;
 
 
@@ -235,6 +239,7 @@ protected:
   bool _active;
   bool _active;
   bool _one_shot;
   bool _one_shot;
   bool _inverted;
   bool _inverted;
+  bool _red_blue_stereo;
   bool _delete_flag;
   bool _delete_flag;
 
 
   // These weak pointers are used to keep track of whether the
   // These weak pointers are used to keep track of whether the

+ 2 - 0
panda/src/display/graphicsWindow.cxx

@@ -49,6 +49,8 @@ GraphicsWindow(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
       << " using GSG " << (void *)gsg << "\n";
       << " using GSG " << (void *)gsg << "\n";
   }
   }
 
 
+  _red_blue_stereo = red_blue_stereo && !_gsg->get_properties().is_stereo();
+
   _properties.set_open(false);
   _properties.set_open(false);
   _properties.set_undecorated(false);
   _properties.set_undecorated(false);
   _properties.set_fullscreen(false);
   _properties.set_fullscreen(false);

+ 48 - 7
panda/src/framework/windowFramework.cxx

@@ -126,7 +126,8 @@ WindowFramework(const WindowFramework &copy, DisplayRegion *display_region) :
 
 
   set_background_type(copy._background_type);
   set_background_type(copy._background_type);
   // Set up a 3-d camera for the window by default.
   // Set up a 3-d camera for the window by default.
-  make_camera();
+  NodePath camera_np = make_camera();
+  _display_region_3d->set_camera(camera_np);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -175,16 +176,29 @@ open_window(const WindowProperties &props, GraphicsEngine *engine,
     
     
     // Create a display region that covers the entire window.
     // Create a display region that covers the entire window.
     _display_region_3d = _window->make_display_region();
     _display_region_3d = _window->make_display_region();
-    _display_region_3d->set_sort(0);
 
 
     // Make sure the DisplayRegion does the clearing, not the window,
     // Make sure the DisplayRegion does the clearing, not the window,
     // so we can have multiple DisplayRegions of different colors.
     // so we can have multiple DisplayRegions of different colors.
     _window->set_clear_color_active(false);
     _window->set_clear_color_active(false);
     _window->set_clear_depth_active(false);
     _window->set_clear_depth_active(false);
-    set_background_type(_background_type);
 
 
     // Set up a 3-d camera for the window by default.
     // Set up a 3-d camera for the window by default.
-    make_camera();
+    NodePath camera_np = make_camera();
+    _display_region_3d->set_camera(camera_np);
+
+    if (_window->is_stereo()) {
+      // Actually, let's make a stereo camera.  That means the
+      // _display_region_3d will be the left channel, and we need to
+      // make another one to be the right channel.
+
+      _display_region_3d->set_stereo_channel(Lens::SC_left);
+
+      _display_region_right = _window->make_display_region();
+      _display_region_right->set_stereo_channel(Lens::SC_right);
+      _display_region_right->set_camera(camera_np);
+    }
+
+    set_background_type(_background_type);
     
     
     if (show_frame_rate_meter) {
     if (show_frame_rate_meter) {
       _frame_rate_meter = new FrameRateMeter("frame_rate_meter");
       _frame_rate_meter = new FrameRateMeter("frame_rate_meter");
@@ -952,6 +966,12 @@ set_background_type(WindowFramework::BackgroundType type) {
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_color(_window->get_clear_color());
     _display_region_3d->set_clear_color(_window->get_clear_color());
     _display_region_3d->set_clear_depth(_window->get_clear_depth());
     _display_region_3d->set_clear_depth(_window->get_clear_depth());
+    if (_display_region_right) {
+      _display_region_right->set_clear_color_active(!_window->get_red_blue_stereo());
+      _display_region_right->set_clear_depth_active(true);
+      _display_region_right->set_clear_color(_window->get_clear_color());
+      _display_region_right->set_clear_depth(_window->get_clear_depth());
+    }
     break;
     break;
     
     
   case BT_black:
   case BT_black:
@@ -959,6 +979,12 @@ set_background_type(WindowFramework::BackgroundType type) {
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
     _display_region_3d->set_clear_color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
     _display_region_3d->set_clear_depth(1.0f);
     _display_region_3d->set_clear_depth(1.0f);
+    if (_display_region_right) {
+      _display_region_right->set_clear_color_active(!_window->get_red_blue_stereo());
+      _display_region_right->set_clear_depth_active(true);
+      _display_region_right->set_clear_color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
+      _display_region_right->set_clear_depth(1.0f);
+    }
     break;
     break;
     
     
   case BT_gray:
   case BT_gray:
@@ -966,6 +992,12 @@ set_background_type(WindowFramework::BackgroundType type) {
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_color(Colorf(0.3f, 0.3f, 0.3f, 0.0f));
     _display_region_3d->set_clear_color(Colorf(0.3f, 0.3f, 0.3f, 0.0f));
     _display_region_3d->set_clear_depth(1.0f);
     _display_region_3d->set_clear_depth(1.0f);
+    if (_display_region_right) {
+      _display_region_right->set_clear_color_active(!_window->get_red_blue_stereo());
+      _display_region_right->set_clear_depth_active(true);
+      _display_region_right->set_clear_color(Colorf(0.3f, 0.3f, 0.3f, 0.0f));
+      _display_region_right->set_clear_depth(1.0f);
+    }
     break;
     break;
     
     
   case BT_white:
   case BT_white:
@@ -973,11 +1005,21 @@ set_background_type(WindowFramework::BackgroundType type) {
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_depth_active(true);
     _display_region_3d->set_clear_color(Colorf(1.0f, 1.0f, 1.0f, 0.0f));
     _display_region_3d->set_clear_color(Colorf(1.0f, 1.0f, 1.0f, 0.0f));
     _display_region_3d->set_clear_depth(1.0f);
     _display_region_3d->set_clear_depth(1.0f);
+    if (_display_region_right) {
+      _display_region_right->set_clear_color_active(!_window->get_red_blue_stereo());
+      _display_region_right->set_clear_depth_active(true);
+      _display_region_right->set_clear_color(Colorf(1.0f, 1.0f, 1.0f, 0.0f));
+      _display_region_right->set_clear_depth(1.0f);
+    }
     break;
     break;
 
 
   case BT_none:
   case BT_none:
     _display_region_3d->set_clear_color_active(false);
     _display_region_3d->set_clear_color_active(false);
     _display_region_3d->set_clear_depth_active(false);
     _display_region_3d->set_clear_depth_active(false);
+    if (_display_region_right) {
+      _display_region_right->set_clear_color_active(false);
+      _display_region_right->set_clear_depth_active(false);
+    }
     break;
     break;
   }
   }
 }
 }
@@ -1012,7 +1054,7 @@ get_shuttle_controls_font() {
 //       Access: Protected
 //       Access: Protected
 //  Description: Makes a new 3-d camera for the window.
 //  Description: Makes a new 3-d camera for the window.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(Camera) WindowFramework::
+NodePath WindowFramework::
 make_camera() {
 make_camera() {
   // Finally, we need a camera to associate with the display region.
   // Finally, we need a camera to associate with the display region.
   PT(Camera) camera = new Camera("camera");
   PT(Camera) camera = new Camera("camera");
@@ -1038,9 +1080,8 @@ make_camera() {
   }
   }
 
 
   camera->set_lens(lens);
   camera->set_lens(lens);
-  _display_region_3d->set_camera(camera_np);
 
 
-  return camera;
+  return camera_np;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/framework/windowFramework.h

@@ -132,7 +132,7 @@ public:
   static TextFont *get_shuttle_controls_font();
   static TextFont *get_shuttle_controls_font();
 
 
 protected:
 protected:
-  PT(Camera) make_camera();
+  NodePath make_camera();
   void setup_lights();
   void setup_lights();
 
 
 private:
 private:
@@ -160,6 +160,7 @@ private:
   PT(GraphicsWindow) _window;
   PT(GraphicsWindow) _window;
   PT(DisplayRegion) _display_region_2d;
   PT(DisplayRegion) _display_region_2d;
   PT(DisplayRegion) _display_region_3d;
   PT(DisplayRegion) _display_region_3d;
+  PT(DisplayRegion) _display_region_right;
 
 
   NodePath _camera_group;
   NodePath _camera_group;
   typedef pvector< PT(Camera) > Cameras;
   typedef pvector< PT(Camera) > Cameras;

+ 7 - 0
panda/src/gobj/config_gobj.cxx

@@ -221,6 +221,13 @@ ConfigVariableDouble default_fov
 ("default-fov", 40.0,
 ("default-fov", 40.0,
  PRC_DESC("The default field of view in degrees for all cameras."));
  PRC_DESC("The default field of view in degrees for all cameras."));
 
 
+ConfigVariableDouble default_iod
+("default-iod", 0.2,
+ PRC_DESC("The default interocular distance for stereo cameras."));
+
+ConfigVariableDouble default_converge
+("default-converge", 25.0,
+ PRC_DESC("The default convergence distance for stereo cameras."));
 
 
 ConfigVariableDouble default_keystone
 ConfigVariableDouble default_keystone
 ("default-keystone", 0.0f,
 ("default-keystone", 0.0f,

+ 2 - 0
panda/src/gobj/config_gobj.h

@@ -69,6 +69,8 @@ extern EXPCL_PANDA ConfigVariableInt geom_cache_min_frames;
 extern ConfigVariableDouble default_near;
 extern ConfigVariableDouble default_near;
 extern ConfigVariableDouble default_far;
 extern ConfigVariableDouble default_far;
 extern ConfigVariableDouble default_fov;
 extern ConfigVariableDouble default_fov;
+extern ConfigVariableDouble default_iod;
+extern ConfigVariableDouble default_converge;
 extern ConfigVariableDouble default_keystone;
 extern ConfigVariableDouble default_keystone;
 
 
 #endif
 #endif

+ 3 - 2
panda/src/gobj/lens.cxx

@@ -112,13 +112,14 @@ clear() {
   _view_hpr.set(0.0f, 0.0f, 0.0f);
   _view_hpr.set(0.0f, 0.0f, 0.0f);
   _view_vector.set(0.0f, 1.0f, 0.0f);
   _view_vector.set(0.0f, 1.0f, 0.0f);
   _up_vector.set(0.0f, 0.0f, 1.0f);
   _up_vector.set(0.0f, 0.0f, 1.0f);
-  _interocular_distance = 0.0f;
-  _convergence_distance = 0.0f;
   _keystone.set(0.0f, 0.0f);
   _keystone.set(0.0f, 0.0f);
 
 
   _user_flags = 0;
   _user_flags = 0;
   _comp_flags = CF_fov;
   _comp_flags = CF_fov;
 
 
+  set_interocular_distance(default_iod);
+  set_convergence_distance(default_converge);
+
   if (default_keystone.has_value()) {
   if (default_keystone.has_value()) {
     _keystone.set(default_keystone[0], default_keystone[1]);
     _keystone.set(default_keystone[0], default_keystone[1]);
     _user_flags |= UF_keystone;
     _user_flags |= UF_keystone;