Browse Source

add nonlinearImager

David Rose 24 years ago
parent
commit
f030c6333f

+ 1 - 0
panda/src/distort/Sources.pp

@@ -10,6 +10,7 @@
     config_distort.cxx config_distort.h \
     cylindricalLens.cxx cylindricalLens.h cylindricalLens.I \
     fisheyeLens.cxx fisheyeLens.h fisheyeLens.I \
+    nonlinearImager.cxx nonlinearImager.h nonlinearImager.I \
     pSphereLens.cxx pSphereLens.h pSphereLens.I \
     projectionScreen.cxx projectionScreen.h projectionScreen.I
 

+ 43 - 0
panda/src/distort/nonlinearImager.I

@@ -0,0 +1,43 @@
+// Filename: nonlinearImager.I
+// Created by:  drose (12Dec01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::set_camera
+//       Access: Published
+//  Description: Specifies the virtual camera that will be used to
+//               view the various ProjectionScreens.  It should be in
+//               the same scene graph with the ProjectionScreens, to
+//               establish a relative coordinate system with them.
+////////////////////////////////////////////////////////////////////
+INLINE void NonlinearImager::
+set_camera(LensNode *camera) {
+  _camera = camera;
+  _stale = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::get_camera
+//       Access: Published
+//  Description: Returns the virtual camera that will be used to
+//               view the various ProjectionScreens.
+////////////////////////////////////////////////////////////////////
+INLINE LensNode *NonlinearImager::
+get_camera() const {
+  return _camera;
+}

+ 365 - 0
panda/src/distort/nonlinearImager.cxx

@@ -0,0 +1,365 @@
+// Filename: nonlinearImager.cxx
+// Created by:  drose (12Dec01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "nonlinearImager.h"
+#include "config_distort.h"
+
+#include "graphicsStateGuardian.h"
+#include "textureTransition.h"
+#include "matrixLens.h"
+#include "renderRelation.h"
+#include "graphicsWindow.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::Constructor
+//       Access: Published
+//  Description: The NonlinearImager is associated with a particular
+//               DisplayRegion when it is created.  It will throw away
+//               whatever camera is currently associated with the
+//               DisplayRegion, and create a speciality camera for
+//               itself.
+////////////////////////////////////////////////////////////////////
+NonlinearImager::
+NonlinearImager(DisplayRegion *dr) {
+  _dr = dr;
+
+  _internal_camera = new Camera("NonlinearImager");
+  _internal_camera->set_lens(new MatrixLens);
+  _internal_scene = new NamedNode("NonlinearImager");
+  _internal_camera->set_scene(_internal_scene);
+  _dr->set_camera(_internal_camera);
+
+  _stale = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NonlinearImager::
+~NonlinearImager() {
+  _internal_camera->set_scene((Node *)NULL);
+  _dr->set_camera((Camera *)NULL);
+  remove_all_screens();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::add_screen
+//       Access: Published
+//  Description: Adds a new ProjectionScreen to the list of screens
+//               that will be processed by the NonlinearImager.  Each
+//               ProjectionScreen represents a view into the world.
+//               It must be based on a linear camera (or whatever kind
+//               of camera is respected by the graphics engine).
+//
+//               width and height indicate the size of the texture
+//               that will be created to render the scene for the
+//               screen.  See set_size().
+//
+//               Each ProjectionScreen object should already have some
+//               screen geometry created.
+//
+//               When render() is called, the graphics state guardian
+//               will be used to render a scene for each
+//               ProjectionScreen object, and then each resulting
+//               image will be applied to a mesh to be rendered to the
+//               screen.
+//
+//               The return value is the index number of the new
+//               screen.
+////////////////////////////////////////////////////////////////////
+int NonlinearImager::
+add_screen(ProjectionScreen *screen) {
+  _screens.push_back(Screen());
+  Screen &new_screen = _screens.back();
+  new_screen._screen = screen;
+  new_screen._mesh_arc = (NodeRelation *)NULL;
+  new_screen._texture = (Texture *)NULL;
+  new_screen._tex_width = 256;
+  new_screen._tex_height = 256;
+
+  // If the LensNode associated with the ProjectionScreen is an actual
+  // Camera, then it has a scene associated.  Otherwise, the user will
+  // have to specify the scene later.
+  LensNode *projector = screen->get_projector();
+  if (projector->is_of_type(Camera::get_class_type())) {
+    Camera *camera = DCAST(Camera, projector);
+    new_screen._scene = camera->get_scene();
+  }
+
+  _stale = true;
+  return _screens.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::find_screen
+//       Access: Published
+//  Description: Returns the index number of the first appearance of
+//               the indicated screen within the imager's list, or -1
+//               if it does not appear.
+////////////////////////////////////////////////////////////////////
+int NonlinearImager::
+find_screen(ProjectionScreen *screen) const {
+  for (size_t i = 0; i < _screens.size(); i++) {
+    if (_screens[i]._screen == screen) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::remove_screen
+//       Access: Published
+//  Description: Removes the screen with the indicated index number
+//               from the imager.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+remove_screen(int index) {
+  nassertv_always(index >= 0 && index < (int)_screens.size());
+  Screen &screen = _screens[index];
+  if (screen._mesh_arc != (NodeRelation *)NULL) {
+    remove_arc(screen._mesh_arc);
+  }
+  _screens.erase(_screens.begin() + index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::remove_all_screens
+//       Access: Published
+//  Description: Removes all screens from the imager.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+remove_all_screens() {
+  Screens::iterator si;
+  for (si = _screens.begin(); si != _screens.end(); ++si) {
+    Screen &screen = (*si);
+    if (screen._mesh_arc != (NodeRelation *)NULL) {
+      remove_arc(screen._mesh_arc);
+    }
+  }
+
+  _screens.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::get_num_screens
+//       Access: Published
+//  Description: Returns the number of screens that have been added to
+//               the imager.
+////////////////////////////////////////////////////////////////////
+int NonlinearImager::
+get_num_screens() const {
+  return _screens.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::get_screen
+//       Access: Published
+//  Description: Returns the nth screen that has been added to the
+//               imager.
+////////////////////////////////////////////////////////////////////
+ProjectionScreen *NonlinearImager::
+get_screen(int index) const {
+  nassertr(index >= 0 && index < (int)_screens.size(), (ProjectionScreen *)NULL);
+  return _screens[index]._screen;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::set_size
+//       Access: Published
+//  Description: Sets the width and height of the texture used to
+//               render the scene for the indicated screen.  This must
+//               be less than or equal to the window size, and it
+//               should be a power of two.
+//
+//               In general, the larger the texture, the greater the
+//               detail of the rendered scene.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+set_size(int index, int width, int height) {
+  nassertv(index >= 0 && index < (int)_screens.size());
+  _screens[index]._tex_width = width;
+  _screens[index]._tex_height = height;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::set_source
+//       Access: Published
+//  Description: Specifies the camera and root of the scene that will
+//               be used to render the image for this particular
+//               screen.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+set_source(int index, LensNode *source, Node *scene) {
+  nassertv(index >= 0 && index < (int)_screens.size());
+  _screens[index]._source = source;
+  _screens[index]._scene = scene;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::set_source
+//       Access: Published
+//  Description: Specifies the camera and root of the scene that will
+//               be used to render the image for this particular
+//               screen.
+//
+//               Since this flavor accepts a Camera node, instead of
+//               just a LensNode, the scene is specified within the
+//               Camera itself.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+set_source(int index, Camera *source) {
+  nassertv(index >= 0 && index < (int)_screens.size());
+  _screens[index]._source = source;
+  _screens[index]._scene = source->get_scene();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::recompute
+//       Access: Published
+//  Description: Forces a regeneration of all the mesh objects, etc.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+recompute() {
+  Screens::iterator si;
+  for (si = _screens.begin(); si != _screens.end(); ++si) {
+    recompute_screen(*si);
+  }
+
+  if (_camera != (LensNode *)NULL && _camera->get_lens() != (Lens *)NULL) {
+    _camera_lens_change = _camera->get_lens()->get_last_change();
+  }
+  _stale = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::render
+//       Access: Published
+//  Description: Uses the DisplayRegion's GSG to render a scene for
+//               each ProjectionScreen, and makes our DisplayRegion
+//               ready to render the combined results.  This will
+//               destroy the contents of the frame buffer; it should
+//               be done before any of the actual frame has started
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+render() {
+  recompute_if_stale();
+
+  Screens::iterator si;
+  for (si = _screens.begin(); si != _screens.end(); ++si) {
+    render_screen(*si);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::recompute_if_stale
+//       Access: Private
+//  Description: Calls recompute() if it needs to be called.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+recompute_if_stale() {
+  if (_camera != (LensNode *)NULL && 
+      _camera->get_lens() != (Lens *)NULL) {
+    UpdateSeq lens_change = _camera->get_lens()->get_last_change();
+    if (_stale || lens_change != _camera_lens_change) {
+      recompute();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::recompute_screen
+//       Access: Private
+//  Description: Regenerates the mesh objects just for the indicated
+//               screen.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+recompute_screen(NonlinearImager::Screen &screen) {
+  if (screen._mesh_arc != (NodeRelation *)NULL) {
+    remove_arc(screen._mesh_arc);
+    screen._mesh_arc = (NodeRelation *)NULL;
+  }
+  screen._texture.clear();
+  if (_camera == (LensNode *)NULL) {
+    // Not much we can do without a camera.
+    return;
+  }
+
+  PT_Node mesh = screen._screen->make_flat_mesh(_camera);
+  screen._mesh_arc = new RenderRelation(_internal_scene, mesh);
+
+  PT(Texture) texture = new Texture;
+  texture->set_minfilter(Texture::FT_linear);
+  texture->set_magfilter(Texture::FT_linear);
+  texture->set_wrapu(Texture::WM_clamp);
+  texture->set_wrapv(Texture::WM_clamp);
+  texture->_pbuffer->set_xsize(screen._tex_width);
+  texture->_pbuffer->set_ysize(screen._tex_height);
+
+  screen._texture = texture;
+  screen._mesh_arc->set_transition(new TextureTransition(texture));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NonlinearImager::render_screen
+//       Access: Private
+//  Description: Renders the scene just for the indicated screen, into
+//               the screen's own texture.
+////////////////////////////////////////////////////////////////////
+void NonlinearImager::
+render_screen(NonlinearImager::Screen &screen) {
+  if (screen._source == (LensNode *)NULL) {
+    distort_cat.error()
+      << "No source lens specified for screen " << screen._screen->get_name()
+      << "\n";
+    return;
+  }
+
+  if (screen._scene == (Node *)NULL) {
+    distort_cat.error()
+      << "No scene specified for screen " << screen._screen->get_name()
+      << "\n";
+    return;
+  }
+
+  GraphicsStateGuardian *gsg = _dr->get_window()->get_gsg();
+
+  // Make a display region of the proper size and clear it to prepare for
+  // rendering the scene.
+  PT(DisplayRegion) scratch_region =
+    gsg->get_window()->make_scratch_display_region(screen._tex_width, screen._tex_height);
+  gsg->clear(gsg->get_render_buffer(RenderBuffer::T_back |
+                                    RenderBuffer::T_depth), 
+             scratch_region);
+
+  DisplayRegionStack old_dr = gsg->push_display_region(scratch_region);
+  gsg->prepare_display_region();
+  gsg->render_scene(screen._scene, screen._source);
+
+  // Copy the results of the render from the frame buffer into the
+  // screen's texture.
+  screen._texture->copy(gsg, scratch_region, 
+                        gsg->get_render_buffer(RenderBuffer::T_back));
+  
+  // Restore the original display region.
+  gsg->pop_display_region(old_dr);
+}

+ 101 - 0
panda/src/distort/nonlinearImager.h

@@ -0,0 +1,101 @@
+// Filename: nonlinearImager.h
+// Created by:  drose (12Dec01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef NONLINEARIMAGER_H
+#define NONLINEARIMAGER_H
+
+#include "pandabase.h"
+
+#include "projectionScreen.h"
+#include "displayRegion.h"
+#include "camera.h"
+#include "texture.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : NonlinearImager
+// Description : This class object combines the rendered output of a
+//               3-d from one or more linear cameras, as seen through
+//               a single, possibly non-linear camera.
+//
+//               This can be used to generate real-time imagery of a
+//               3-d scene using a nonlinear camera, for instance a
+//               fisheye camera, even though the 3-d graphics engine
+//               only supports linear cameras.
+//
+//               The NonlinearImager collects together a number of
+//               ProjectionScreens, each of which has a standard,
+//               linear Camera.  Each frame, the Imager renders each
+//               scene into a texture and then maps that texture onto
+//               a mesh which is presented to the graphics engine for
+//               rendering the final, non-linear output.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAFX NonlinearImager {
+PUBLISHED:
+  NonlinearImager(DisplayRegion *dr);
+  ~NonlinearImager();
+
+  int add_screen(ProjectionScreen *screen);
+  int find_screen(ProjectionScreen *screen) const;
+  void remove_screen(int index);
+  void remove_all_screens();
+
+  int get_num_screens() const;
+  ProjectionScreen *get_screen(int index) const;
+  void set_size(int index, int width, int height);
+  void set_source(int index, LensNode *source, Node *scene);
+  void set_source(int index, Camera *source);
+
+  INLINE void set_camera(LensNode *camera);
+  INLINE LensNode *get_camera() const;
+
+  void recompute();
+  void render();
+
+private:
+  class Screen {
+  public:
+    PT(ProjectionScreen) _screen;
+    NodeRelation *_mesh_arc;
+    PT(Texture) _texture;
+    PT(LensNode) _source;
+    PT_Node _scene;
+    int _tex_width, _tex_height;
+  };
+
+  void recompute_if_stale();
+  void recompute_screen(Screen &screen);
+  void render_screen(Screen &screen);
+
+  PT(DisplayRegion) _dr;
+
+  typedef pvector<Screen> Screens;
+  Screens _screens;
+
+  PT(LensNode) _camera;
+
+  PT(Camera) _internal_camera;
+  PT_Node _internal_scene;
+
+  bool _stale;
+  UpdateSeq _camera_lens_change;
+};
+
+#include "nonlinearImager.I"
+
+#endif

+ 8 - 1
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -610,11 +610,17 @@ render_subgraph(RenderTraverser *traverser,
 
   //  activate();
 
+  Lens *lens = projnode->get_lens();
+  if (!lens->is_linear()) {
+    glgsg_cat.error()
+      << "Cannot render with a nonlinear lens!\n";
+    return;
+  }
+
   LensNode *old_camera = _current_camera;
   _current_camera = projnode;
   LMatrix4f old_projection_mat = _current_projection_mat;
 
-  Lens *lens = projnode->get_lens();
   const LMatrix4f &projection_mat = lens->get_projection_mat();
 
   // The projection matrix must always be right-handed Y-up, even if
@@ -1998,6 +2004,7 @@ copy_texture(TextureContext *tc, const DisplayRegion *dr) {
                    get_internal_image_format(pb->get_format()),
                    pb->get_xorg(), pb->get_yorg(),
                    pb->get_xsize(), pb->get_ysize(), pb->get_border());
+  clear_attribute(TextureTransition::get_class_type());
 }
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 0
panda/src/sgattrib/billboardTransition.cxx

@@ -78,6 +78,11 @@ sub_render(NodeRelation *arc, const AllTransitionsWrapper &,
   // DisplayRegion instead of the GSG.
   const DisplayRegion *dr = gsg->get_current_display_region();
   LensNode *camera = dr->get_cull_frustum();
+  if (camera == (LensNode *)NULL) {
+    // Never mind; ask the gsg.
+    camera = gsg->get_current_camera();
+  }
+
   nassertr(camera != (LensNode *)NULL, true);
 
   // And the relative coordinate space.