support multiple viewers

This commit is contained in:
David Rose 2002-06-13 23:17:37 +00:00
parent 99413ba1b3
commit df65deb57f
3 changed files with 369 additions and 146 deletions

View File

@ -16,27 +16,3 @@
// //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_viewer
// Access: Published
// Description: Returns the NodePath to the LensNode that is to serve
// as the viewer for this screen, or empty if no
// viewer is associated.
////////////////////////////////////////////////////////////////////
INLINE const NodePath &NonlinearImager::
get_viewer() const {
return _viewer;
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_internal_scene
// Access: Published
// Description: Returns a pointer to the root node of the internal
// scene graph, which is used to render all of the
// screen meshes.
////////////////////////////////////////////////////////////////////
INLINE NodePath NonlinearImager::
get_internal_scene() const {
return _internal_scene;
}

View File

@ -28,31 +28,11 @@
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::Constructor // Function: NonlinearImager::Constructor
// Access: Published // Access: Published
// Description: The NonlinearImager is associated with a particular // Description:
// 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::
NonlinearImager(DisplayRegion *dr) { NonlinearImager() {
_dr = dr; _gsg = (GraphicsStateGuardian *)NULL;
// The internal camera is an identity-matrix camera that simply
// views the meshes that represent the user's specified camera.
_internal_camera = new Camera("NonlinearImager");
_internal_camera->set_lens(new MatrixLens);
_internal_scene = NodePath("screens");
_internal_camera->set_scene(_internal_scene);
NodePath camera_np = _internal_scene.attach_new_node(_internal_camera);
_dr->set_camera(camera_np);
// Enable face culling on the wireframe mesh. This will help us to
// cull out invalid polygons that result from vertices crossing a
// singularity (for instance, at the back of a fisheye lens).
_internal_scene.set_two_sided(0);
_stale = true; _stale = true;
} }
@ -63,9 +43,8 @@ NonlinearImager(DisplayRegion *dr) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
NonlinearImager:: NonlinearImager::
~NonlinearImager() { ~NonlinearImager() {
_internal_camera->set_scene(NodePath());
_dr->set_camera(NodePath());
remove_all_screens(); remove_all_screens();
remove_all_viewers();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -79,7 +58,7 @@ NonlinearImager::
// //
// width and height indicate the size of the texture // width and height indicate the size of the texture
// that will be created to render the scene for the // that will be created to render the scene for the
// screen. See set_size(). // screen. See set_texture_size().
// //
// Each ProjectionScreen object should already have some // Each ProjectionScreen object should already have some
// screen geometry created. // screen geometry created.
@ -101,9 +80,15 @@ add_screen(ProjectionScreen *screen) {
new_screen._texture = (Texture *)NULL; new_screen._texture = (Texture *)NULL;
new_screen._tex_width = 256; new_screen._tex_width = 256;
new_screen._tex_height = 256; new_screen._tex_height = 256;
new_screen._last_screen = screen->get_last_screen();
new_screen._active = true; new_screen._active = true;
// Slot a mesh for each viewer.
size_t vi;
for (vi = 0; vi < _viewers.size(); ++vi) {
new_screen._meshes.push_back(Mesh());
new_screen._meshes[vi]._last_screen = screen->get_last_screen();
}
_stale = true; _stale = true;
return _screens.size() - 1; return _screens.size() - 1;
} }
@ -136,7 +121,9 @@ void NonlinearImager::
remove_screen(int index) { remove_screen(int index) {
nassertv_always(index >= 0 && index < (int)_screens.size()); nassertv_always(index >= 0 && index < (int)_screens.size());
Screen &screen = _screens[index]; Screen &screen = _screens[index];
screen._mesh.remove_node(); for (size_t vi = 0; vi < screen._meshes.size(); vi++) {
screen._meshes[vi]._mesh.remove_node();
}
_screens.erase(_screens.begin() + index); _screens.erase(_screens.begin() + index);
} }
@ -147,13 +134,9 @@ remove_screen(int index) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
remove_all_screens() { remove_all_screens() {
Screens::iterator si; while (!_screens.empty()) {
for (si = _screens.begin(); si != _screens.end(); ++si) { remove_screen(_screens.size() - 1);
Screen &screen = (*si);
screen._mesh.remove_node();
} }
_screens.clear();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -180,7 +163,7 @@ get_screen(int index) const {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::set_size // Function: NonlinearImager::set_texture_size
// Access: Published // Access: Published
// Description: Sets the width and height of the texture used to // Description: Sets the width and height of the texture used to
// render the scene for the indicated screen. This must // render the scene for the indicated screen. This must
@ -191,7 +174,7 @@ get_screen(int index) const {
// detail of the rendered scene. // detail of the rendered scene.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
set_size(int index, int width, int height) { set_texture_size(int index, int width, int height) {
nassertv(index >= 0 && index < (int)_screens.size()); nassertv(index >= 0 && index < (int)_screens.size());
_screens[index]._tex_width = width; _screens[index]._tex_width = width;
_screens[index]._tex_height = height; _screens[index]._tex_height = height;
@ -216,21 +199,23 @@ set_source_camera(int index, const NodePath &source_camera) {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::set_active // Function: NonlinearImager::set_screen_active
// Access: Published // Access: Published
// Description: Sets the active flag on the indicated screen. If the // Description: Sets the active flag on the indicated screen. If the
// active flag is true, the screen will be used; // active flag is true, the screen will be used;
// otherwise, it will not appear. // otherwise, it will not appear.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
set_active(int index, bool active) { set_screen_active(int index, bool active) {
nassertv(index >= 0 && index < (int)_screens.size()); nassertv(index >= 0 && index < (int)_screens.size());
_screens[index]._active = active; _screens[index]._active = active;
if (!active) { if (!active) {
Screen &screen = _screens[index]; Screen &screen = _screens[index];
// If we've just made this screen inactive, remove its mesh. // If we've just made this screen inactive, remove its meshes.
screen._mesh.remove_node(); for (size_t vi = 0; vi < screen._meshes.size(); vi++) {
screen._meshes[vi]._mesh.remove_node();
}
screen._texture.clear(); screen._texture.clear();
} else { } else {
// If we've just made it active, it needs to be recomputed. // If we've just made it active, it needs to be recomputed.
@ -239,18 +224,147 @@ set_active(int index, bool active) {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_active // Function: NonlinearImager::get_screen_active
// Access: Published // Access: Published
// Description: Returns the active flag on the indicated screen. // Description: Returns the active flag on the indicated screen.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool NonlinearImager:: bool NonlinearImager::
get_active(int index) const { get_screen_active(int index) const {
nassertr(index >= 0 && index < (int)_screens.size(), false); nassertr(index >= 0 && index < (int)_screens.size(), false);
return _screens[index]._active; return _screens[index]._active;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::set_viewer // Function: NonlinearImager::add_viewer
// Access: Published
// Description: Adds the indicated DisplayRegion as a viewer into the
// NonlinearImager room. The camera associated with the
// DisplayRegion at the time add_viewer() is called is
// used as the initial viewer camera; it may have a
// nonlinear lens, like a fisheye or cylindrical lens.
//
// This sets up a special scene graph for this
// DisplayRegion alone and sets up the DisplayRegion
// with a specialty camera. If future changes to the
// camera are desired, you should use the
// set_viewer_camera() interface.
//
// All viewers must share the same
// GraphicsStateGuardian.
//
// The return value is the index of the new viewer.
////////////////////////////////////////////////////////////////////
int NonlinearImager::
add_viewer(DisplayRegion *dr) {
GraphicsWindow *win = dr->get_window();
GraphicsStateGuardian *gsg = win->get_gsg();
nassertr(_viewers.empty() || gsg == _gsg, -1);
_gsg = gsg;
int previous_vi = find_viewer(dr);
if (previous_vi >= 0) {
return previous_vi;
}
size_t vi = _viewers.size();
_viewers.push_back(Viewer());
Viewer &viewer = _viewers[vi];
viewer._dr = dr;
// Get the current camera off of the DisplayRegion, if any.
viewer._viewer = dr->get_camera();
if (viewer._viewer.is_empty()) {
viewer._viewer_node = (LensNode *)NULL;
} else {
viewer._viewer_node = DCAST(LensNode, viewer._viewer.node());
}
// The internal camera is an identity-matrix camera that simply
// views the meshes that represent the user's specified camera.
viewer._internal_camera = new Camera("NonlinearImager");
viewer._internal_camera->set_lens(new MatrixLens);
viewer._internal_scene = NodePath("screens");
viewer._internal_camera->set_scene(viewer._internal_scene);
NodePath camera_np = viewer._internal_scene.attach_new_node(viewer._internal_camera);
viewer._dr->set_camera(camera_np);
// Enable face culling on the wireframe mesh. This will help us to
// cull out invalid polygons that result from vertices crossing a
// singularity (for instance, at the back of a fisheye lens).
viewer._internal_scene.set_two_sided(0);
// Finally, slot a new mesh for each screen.
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
screen._meshes.push_back(Mesh());
nassertr(screen._meshes.size() == _viewers.size(), -1);
}
_stale = true;
return vi;
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::find_viewer
// Access: Published
// Description: Returns the index number of the indicated
// DisplayRegion within the list of viewers, or -1 if it
// is not found.
////////////////////////////////////////////////////////////////////
int NonlinearImager::
find_viewer(DisplayRegion *dr) const {
for (size_t vi = 0; vi < _viewers.size(); vi++) {
if (_viewers[vi]._dr == dr) {
return vi;
}
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::remove_viewer
// Access: Published
// Description: Removes the viewer with the indicated index number
// from the imager.
////////////////////////////////////////////////////////////////////
void NonlinearImager::
remove_viewer(int index) {
nassertv_always(index >= 0 && index < (int)_viewers.size());
Viewer &viewer = _viewers[index];
viewer._internal_camera->set_scene(NodePath());
viewer._dr->set_camera(viewer._viewer);
// Also remove the corresponding mesh from each screen.
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
nassertv(index < (int)screen._meshes.size());
screen._meshes[index]._mesh.remove_node();
screen._meshes.erase(screen._meshes.begin() + index);
}
_viewers.erase(_viewers.begin() + index);
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::remove_all_viewers
// Access: Published
// Description: Removes all viewers from the imager.
////////////////////////////////////////////////////////////////////
void NonlinearImager::
remove_all_viewers() {
while (!_viewers.empty()) {
remove_viewer(_viewers.size() - 1);
}
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::set_viewer_camera
// Access: Published // Access: Published
// Description: Specifies the LensNode that is to serve as the // Description: Specifies the LensNode that is to serve as the
// viewer for this screen. The relative position of // viewer for this screen. The relative position of
@ -259,16 +373,70 @@ get_active(int index) const {
// determines the UV's that will be assigned to the // determines the UV's that will be assigned to the
// geometry within the NonlinearImager. // geometry within the NonlinearImager.
// //
// It is not necessary to call this except to change the
// camera after a viewer has been added, since the
// default is to use whatever camera is associated with
// the DisplayRegion at the time the viewer is added.
//
// The NodePath must refer to a LensNode (or a Camera). // The NodePath must refer to a LensNode (or a Camera).
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
set_viewer(const NodePath &viewer) { set_viewer_camera(int index, const NodePath &viewer_camera) {
_viewer_node = (LensNode *)NULL; nassertv(index >= 0 && index < (int)_viewers.size());
_viewer = viewer; nassertv(!viewer_camera.is_empty() &&
viewer_camera.node()->is_of_type(LensNode::get_class_type()));
Viewer &viewer = _viewers[index];
viewer._viewer = viewer_camera;
viewer._viewer_node = DCAST(LensNode, viewer_camera.node());
_stale = true; _stale = true;
nassertv(!viewer.is_empty() && }
viewer.node()->is_of_type(LensNode::get_class_type()));
_viewer_node = DCAST(LensNode, viewer.node()); ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_viewer_camera
// Access: Published
// Description: Returns the NodePath to the LensNode that is to serve
// as nth viewer for this screen.
////////////////////////////////////////////////////////////////////
NodePath NonlinearImager::
get_viewer_camera(int index) const {
nassertr(index >= 0 && index < (int)_viewers.size(), NodePath());
return _viewers[index]._viewer;
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_internal_scene
// Access: Published
// Description: Returns a pointer to the root node of the internal
// scene graph for the nth viewer, which is used to
// render all of the screen meshes for this viewer.
////////////////////////////////////////////////////////////////////
NodePath NonlinearImager::
get_internal_scene(int index) const {
nassertr(index >= 0 && index < (int)_viewers.size(), NodePath());
return _viewers[index]._internal_scene;
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_num_viewers
// Access: Published
// Description: Returns the number of viewers that have been added to
// the imager.
////////////////////////////////////////////////////////////////////
int NonlinearImager::
get_num_viewers() const {
return _viewers.size();
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::get_viewer
// Access: Published
// Description: Returns the nth viewer's DisplayRegion that has been
// added to the imager.
////////////////////////////////////////////////////////////////////
DisplayRegion *NonlinearImager::
get_viewer(int index) const {
nassertr(index >= 0 && index < (int)_viewers.size(), (DisplayRegion *)NULL);
return _viewers[index]._dr;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -278,17 +446,31 @@ set_viewer(const NodePath &viewer) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
recompute() { recompute() {
// First, force all the textures to clear.
Screens::iterator si; Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) { for (si = _screens.begin(); si != _screens.end(); ++si) {
if ((*si)._active) { Screen &screen = (*si);
recompute_screen(*si); screen._texture.clear();
}
size_t vi;
for (vi = 0; vi < _viewers.size(); ++vi) {
Viewer &viewer = _viewers[vi];
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
if (screen._active) {
recompute_screen(screen, vi);
}
}
if (viewer._viewer_node != (LensNode *)NULL &&
viewer._viewer_node->get_lens() != (Lens *)NULL) {
viewer._viewer_lens_change =
viewer._viewer_node->get_lens()->get_last_change();
} }
} }
if (_viewer_node != (LensNode *)NULL &&
_viewer_node->get_lens() != (Lens *)NULL) {
_viewer_lens_change = _viewer_node->get_lens()->get_last_change();
}
_stale = false; _stale = false;
} }
@ -321,20 +503,37 @@ render(GraphicsEngine *engine) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
recompute_if_stale() { recompute_if_stale() {
if (_viewer_node != (LensNode *)NULL && if (_stale) {
_viewer_node->get_lens() != (Lens *)NULL) { recompute();
UpdateSeq lens_change = _viewer_node->get_lens()->get_last_change(); } else {
if (_stale || lens_change != _viewer_lens_change) { size_t vi;
recompute(); for (vi = 0; vi < _viewers.size(); ++vi) {
} else { Viewer &viewer = _viewers[vi];
// We're not overall stale, but maybe we need to recompute one if (viewer._viewer_node != (LensNode *)NULL) {
// or more of our screens. UpdateSeq lens_change =
Screens::iterator si; viewer._viewer_node->get_lens()->get_last_change();
for (si = _screens.begin(); si != _screens.end(); ++si) { if (lens_change != viewer._viewer_lens_change) {
Screen &screen = (*si); // The viewer has changed, so we need to recompute all screens
if (screen._active && // on this viewer.
screen._last_screen != screen._screen->get_last_screen()) { Screens::iterator si;
recompute_screen(screen); for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
if (screen._active) {
recompute_screen(screen, vi);
}
}
} else {
// We may not need to recompute all screens, but maybe some of
// them.
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
if (screen._active &&
screen._meshes[vi]._last_screen != screen._screen->get_last_screen()) {
recompute_screen(screen, vi);
}
}
} }
} }
} }
@ -348,28 +547,36 @@ recompute_if_stale() {
// screen. // screen.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
recompute_screen(NonlinearImager::Screen &screen) { recompute_screen(NonlinearImager::Screen &screen, size_t vi) {
screen._mesh.remove_node(); nassertv(vi < screen._meshes.size());
screen._texture.clear(); screen._meshes[vi]._mesh.remove_node();
if (_viewer_node == (LensNode *)NULL || !screen._active) { if (!screen._active) {
// Not much we can do without a viewer.
return; return;
} }
PT(PandaNode) mesh = screen._screen->make_flat_mesh(_viewer); Viewer &viewer = _viewers[vi];
screen._mesh = _internal_scene.attach_new_node(mesh); PT(PandaNode) mesh = screen._screen->make_flat_mesh(viewer._viewer);
if (mesh != (PandaNode *)NULL) {
screen._meshes[vi]._mesh = viewer._internal_scene.attach_new_node(mesh);
}
PT(Texture) texture = new Texture; if (screen._texture == (Texture *)NULL ||
texture->set_minfilter(Texture::FT_linear); screen._texture->_pbuffer == (PixelBuffer *)NULL ||
texture->set_magfilter(Texture::FT_linear); screen._texture->_pbuffer->get_xsize() != screen._tex_width ||
texture->set_wrapu(Texture::WM_clamp); screen._texture->_pbuffer->get_ysize() != screen._tex_height) {
texture->set_wrapv(Texture::WM_clamp); PT(Texture) texture = new Texture;
texture->_pbuffer->set_xsize(screen._tex_width); texture->set_minfilter(Texture::FT_linear);
texture->_pbuffer->set_ysize(screen._tex_height); 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._texture = texture;
screen._mesh.set_texture(texture); }
screen._last_screen = screen._screen->get_last_screen();
screen._meshes[vi]._mesh.set_texture(screen._texture);
screen._meshes[vi]._last_screen = screen._screen->get_last_screen();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -387,24 +594,23 @@ render_screen(GraphicsEngine *engine, NonlinearImager::Screen &screen) {
return; return;
} }
// Got to update this to new scene graph. nassertv(_gsg != (GraphicsStateGuardian *)NULL);
GraphicsStateGuardian *gsg = _dr->get_window()->get_gsg();
// Make a display region of the proper size and clear it to prepare for // Make a display region of the proper size and clear it to prepare for
// rendering the scene. // rendering the scene.
PT(DisplayRegion) scratch_region = PT(DisplayRegion) scratch_region =
gsg->get_window()->make_scratch_display_region(screen._tex_width, screen._tex_height); _gsg->get_window()->make_scratch_display_region(screen._tex_width, screen._tex_height);
scratch_region->set_camera(screen._source_camera); scratch_region->set_camera(screen._source_camera);
gsg->clear(gsg->get_render_buffer(RenderBuffer::T_back | _gsg->clear(_gsg->get_render_buffer(RenderBuffer::T_back |
RenderBuffer::T_depth), RenderBuffer::T_depth),
scratch_region); scratch_region);
engine->render_subframe(gsg, scratch_region); engine->render_subframe(_gsg, scratch_region);
// Copy the results of the render from the frame buffer into the // Copy the results of the render from the frame buffer into the
// screen's texture. // screen's texture.
screen._texture->copy(gsg, scratch_region, screen._texture->copy(_gsg, scratch_region,
gsg->get_render_buffer(RenderBuffer::T_back)); _gsg->get_render_buffer(RenderBuffer::T_back));
// It might be nice if we didn't throw away the scratch region every // It might be nice if we didn't throw away the scratch region every
// time, which prevents us from preserving cull state from one frame // time, which prevents us from preserving cull state from one frame

View File

@ -31,12 +31,14 @@
#include "pvector.h" #include "pvector.h"
class GraphicsEngine; class GraphicsEngine;
class GraphicsStateGuardian;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Class : NonlinearImager // Class : NonlinearImager
// Description : This class object combines the rendered output of a // Description : This class object combines the rendered output of a
// 3-d from one or more linear cameras, as seen through // 3-d from one or more linear (e.g. perspective)
// a single, possibly non-linear camera. // cameras, as seen through a single, possibly nonlinear
// camera.
// //
// This can be used to generate real-time imagery of a // This can be used to generate real-time imagery of a
// 3-d scene using a nonlinear camera, for instance a // 3-d scene using a nonlinear camera, for instance a
@ -44,7 +46,7 @@ class GraphicsEngine;
// only supports linear cameras. // only supports linear cameras.
// //
// //
// A NonlinearImager may be visualized as a theater room // A NonlinearImager may be visualized as a dark room
// into which a number of projection screens have been // into which a number of projection screens have been
// placed, of arbitrary size and shape and at any // placed, of arbitrary size and shape and at any
// arbitrary position and orientation to each other. // arbitrary position and orientation to each other.
@ -52,14 +54,33 @@ class GraphicsEngine;
// seen by a normal perspective camera that exists in // seen by a normal perspective camera that exists in
// the world (that is, under render). // the world (that is, under render).
// //
// There is also in the theater a single, possibly // There also exists in the theater one or more
// nonlinear, camera that observes these screens. The // (possibly nonlinear) cameras, called viewers, that
// user's window (or DisplayRegion) will display the // observe these screens. Each of these viewers is
// output of this camera. // associated with a single DisplayRegion, where the
// final results are presented.
//
//
// There are several different LensNode (Camera) objects
// involved at each stage in the process. To help keep
// them all straight, different words are used to refer
// to each different kind of Camera used within this
// object. The camera(s) under render, that capture the
// original view of the world to be projected onto the
// screens, are called source cameras, and are set per
// screen via set_source_camera(). The LensNode that is
// associated with each screen to project the image as
// seen from the screen's source camera is called a
// projector; these are set via the
// ProjectionScreen::set_projector() interface.
// Finally, the (possibly nonlinear) cameras that view
// the whole configuration of screens are called
// viewers; each of these is associated with a
// DisplayRegion, and they are set via set_viewer_camera().
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
class EXPCL_PANDAFX NonlinearImager { class EXPCL_PANDAFX NonlinearImager {
PUBLISHED: PUBLISHED:
NonlinearImager(DisplayRegion *dr); NonlinearImager();
~NonlinearImager(); ~NonlinearImager();
int add_screen(ProjectionScreen *screen); int add_screen(ProjectionScreen *screen);
@ -69,49 +90,69 @@ PUBLISHED:
int get_num_screens() const; int get_num_screens() const;
ProjectionScreen *get_screen(int index) const; ProjectionScreen *get_screen(int index) const;
void set_size(int index, int width, int height); void set_texture_size(int index, int width, int height);
void set_source_camera(int index, const NodePath &source_camera); void set_source_camera(int index, const NodePath &source_camera);
void set_active(int index, bool active); void set_screen_active(int index, bool active);
bool get_active(int index) const; bool get_screen_active(int index) const;
void set_viewer(const NodePath &viewer); int add_viewer(DisplayRegion *dr);
INLINE const NodePath &get_viewer() const; int find_viewer(DisplayRegion *dr) const;
void remove_viewer(int index);
void remove_all_viewers();
INLINE NodePath get_internal_scene() const; void set_viewer_camera(int index, const NodePath &viewer_camera);
NodePath get_viewer_camera(int index) const;
NodePath get_internal_scene(int index) const;
int get_num_viewers() const;
DisplayRegion *get_viewer(int index) const;
void recompute(); void recompute();
void render(GraphicsEngine *engine); void render(GraphicsEngine *engine);
private: private:
class Viewer {
public:
PT(DisplayRegion) _dr;
PT(Camera) _internal_camera;
NodePath _internal_scene;
NodePath _viewer;
PT(LensNode) _viewer_node;
UpdateSeq _viewer_lens_change;
};
typedef pvector<Viewer> Viewers;
class Mesh {
public:
NodePath _mesh;
UpdateSeq _last_screen;
};
typedef pvector<Mesh> Meshes;
class Screen { class Screen {
public: public:
PT(ProjectionScreen) _screen; PT(ProjectionScreen) _screen;
NodePath _mesh;
PT(Texture) _texture; PT(Texture) _texture;
NodePath _source_camera; NodePath _source_camera;
int _tex_width, _tex_height; int _tex_width, _tex_height;
UpdateSeq _last_screen;
bool _active; bool _active;
// One mesh per viewer.
Meshes _meshes;
}; };
typedef pvector<Screen> Screens;
void recompute_if_stale(); void recompute_if_stale();
void recompute_screen(Screen &screen); void recompute_screen(Screen &screen, size_t vi);
void render_screen(GraphicsEngine *engine, Screen &screen); void render_screen(GraphicsEngine *engine, Screen &screen);
PT(DisplayRegion) _dr; Viewers _viewers;
typedef pvector<Screen> Screens;
Screens _screens; Screens _screens;
GraphicsStateGuardian *_gsg;
NodePath _viewer;
PT(LensNode) _viewer_node;
PT(Camera) _internal_camera;
NodePath _internal_scene;
bool _stale; bool _stale;
UpdateSeq _viewer_lens_change;
}; };
#include "nonlinearImager.I" #include "nonlinearImager.I"