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
// 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.
// Description:
////////////////////////////////////////////////////////////////////
NonlinearImager::
NonlinearImager(DisplayRegion *dr) {
_dr = dr;
// 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);
NonlinearImager() {
_gsg = (GraphicsStateGuardian *)NULL;
_stale = true;
}
@ -63,9 +43,8 @@ NonlinearImager(DisplayRegion *dr) {
////////////////////////////////////////////////////////////////////
NonlinearImager::
~NonlinearImager() {
_internal_camera->set_scene(NodePath());
_dr->set_camera(NodePath());
remove_all_screens();
remove_all_viewers();
}
////////////////////////////////////////////////////////////////////
@ -79,7 +58,7 @@ NonlinearImager::
//
// width and height indicate the size of the texture
// 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
// screen geometry created.
@ -101,9 +80,15 @@ add_screen(ProjectionScreen *screen) {
new_screen._texture = (Texture *)NULL;
new_screen._tex_width = 256;
new_screen._tex_height = 256;
new_screen._last_screen = screen->get_last_screen();
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;
return _screens.size() - 1;
}
@ -136,7 +121,9 @@ void NonlinearImager::
remove_screen(int index) {
nassertv_always(index >= 0 && index < (int)_screens.size());
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);
}
@ -147,13 +134,9 @@ remove_screen(int index) {
////////////////////////////////////////////////////////////////////
void NonlinearImager::
remove_all_screens() {
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
screen._mesh.remove_node();
while (!_screens.empty()) {
remove_screen(_screens.size() - 1);
}
_screens.clear();
}
////////////////////////////////////////////////////////////////////
@ -180,7 +163,7 @@ get_screen(int index) const {
}
////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::set_size
// Function: NonlinearImager::set_texture_size
// Access: Published
// Description: Sets the width and height of the texture used to
// render the scene for the indicated screen. This must
@ -191,7 +174,7 @@ get_screen(int index) const {
// detail of the rendered scene.
////////////////////////////////////////////////////////////////////
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());
_screens[index]._tex_width = width;
_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
// Description: Sets the active flag on the indicated screen. If the
// active flag is true, the screen will be used;
// otherwise, it will not appear.
////////////////////////////////////////////////////////////////////
void NonlinearImager::
set_active(int index, bool active) {
set_screen_active(int index, bool active) {
nassertv(index >= 0 && index < (int)_screens.size());
_screens[index]._active = active;
if (!active) {
Screen &screen = _screens[index];
// If we've just made this screen inactive, remove its mesh.
screen._mesh.remove_node();
// If we've just made this screen inactive, remove its meshes.
for (size_t vi = 0; vi < screen._meshes.size(); vi++) {
screen._meshes[vi]._mesh.remove_node();
}
screen._texture.clear();
} else {
// 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
// Description: Returns the active flag on the indicated screen.
////////////////////////////////////////////////////////////////////
bool NonlinearImager::
get_active(int index) const {
get_screen_active(int index) const {
nassertr(index >= 0 && index < (int)_screens.size(), false);
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
// Description: Specifies the LensNode that is to serve as the
// 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
// 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).
////////////////////////////////////////////////////////////////////
void NonlinearImager::
set_viewer(const NodePath &viewer) {
_viewer_node = (LensNode *)NULL;
_viewer = viewer;
set_viewer_camera(int index, const NodePath &viewer_camera) {
nassertv(index >= 0 && index < (int)_viewers.size());
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;
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::
recompute() {
// First, force all the textures to clear.
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
if ((*si)._active) {
recompute_screen(*si);
Screen &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;
}
@ -321,20 +503,37 @@ render(GraphicsEngine *engine) {
////////////////////////////////////////////////////////////////////
void NonlinearImager::
recompute_if_stale() {
if (_viewer_node != (LensNode *)NULL &&
_viewer_node->get_lens() != (Lens *)NULL) {
UpdateSeq lens_change = _viewer_node->get_lens()->get_last_change();
if (_stale || lens_change != _viewer_lens_change) {
recompute();
} else {
// We're not overall stale, but maybe we need to recompute one
// or more of our screens.
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si);
if (screen._active &&
screen._last_screen != screen._screen->get_last_screen()) {
recompute_screen(screen);
if (_stale) {
recompute();
} else {
size_t vi;
for (vi = 0; vi < _viewers.size(); ++vi) {
Viewer &viewer = _viewers[vi];
if (viewer._viewer_node != (LensNode *)NULL) {
UpdateSeq lens_change =
viewer._viewer_node->get_lens()->get_last_change();
if (lens_change != viewer._viewer_lens_change) {
// The viewer has changed, so we need to recompute all screens
// on this viewer.
Screens::iterator si;
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.
////////////////////////////////////////////////////////////////////
void NonlinearImager::
recompute_screen(NonlinearImager::Screen &screen) {
screen._mesh.remove_node();
screen._texture.clear();
if (_viewer_node == (LensNode *)NULL || !screen._active) {
// Not much we can do without a viewer.
recompute_screen(NonlinearImager::Screen &screen, size_t vi) {
nassertv(vi < screen._meshes.size());
screen._meshes[vi]._mesh.remove_node();
if (!screen._active) {
return;
}
PT(PandaNode) mesh = screen._screen->make_flat_mesh(_viewer);
screen._mesh = _internal_scene.attach_new_node(mesh);
Viewer &viewer = _viewers[vi];
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;
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);
if (screen._texture == (Texture *)NULL ||
screen._texture->_pbuffer == (PixelBuffer *)NULL ||
screen._texture->_pbuffer->get_xsize() != screen._tex_width ||
screen._texture->_pbuffer->get_ysize() != screen._tex_height) {
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.set_texture(texture);
screen._last_screen = screen._screen->get_last_screen();
screen._texture = texture;
}
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;
}
// Got to update this to new scene graph.
GraphicsStateGuardian *gsg = _dr->get_window()->get_gsg();
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
// 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->get_window()->make_scratch_display_region(screen._tex_width, screen._tex_height);
scratch_region->set_camera(screen._source_camera);
gsg->clear(gsg->get_render_buffer(RenderBuffer::T_back |
RenderBuffer::T_depth),
scratch_region);
engine->render_subframe(gsg, scratch_region);
_gsg->clear(_gsg->get_render_buffer(RenderBuffer::T_back |
RenderBuffer::T_depth),
scratch_region);
engine->render_subframe(_gsg, scratch_region);
// 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));
screen._texture->copy(_gsg, scratch_region,
_gsg->get_render_buffer(RenderBuffer::T_back));
// 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

View File

@ -31,12 +31,14 @@
#include "pvector.h"
class GraphicsEngine;
class GraphicsStateGuardian;
////////////////////////////////////////////////////////////////////
// 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.
// 3-d from one or more linear (e.g. perspective)
// cameras, as seen through a single, possibly nonlinear
// camera.
//
// This can be used to generate real-time imagery of a
// 3-d scene using a nonlinear camera, for instance a
@ -44,7 +46,7 @@ class GraphicsEngine;
// 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
// placed, of arbitrary size and shape and at any
// arbitrary position and orientation to each other.
@ -52,14 +54,33 @@ class GraphicsEngine;
// seen by a normal perspective camera that exists in
// the world (that is, under render).
//
// There is also in the theater a single, possibly
// nonlinear, camera that observes these screens. The
// user's window (or DisplayRegion) will display the
// output of this camera.
// There also exists in the theater one or more
// (possibly nonlinear) cameras, called viewers, that
// observe these screens. Each of these viewers is
// 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 {
PUBLISHED:
NonlinearImager(DisplayRegion *dr);
NonlinearImager();
~NonlinearImager();
int add_screen(ProjectionScreen *screen);
@ -69,49 +90,69 @@ PUBLISHED:
int get_num_screens() 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_active(int index, bool active);
bool get_active(int index) const;
void set_screen_active(int index, bool active);
bool get_screen_active(int index) const;
void set_viewer(const NodePath &viewer);
INLINE const NodePath &get_viewer() const;
int add_viewer(DisplayRegion *dr);
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 render(GraphicsEngine *engine);
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 {
public:
PT(ProjectionScreen) _screen;
NodePath _mesh;
PT(Texture) _texture;
NodePath _source_camera;
int _tex_width, _tex_height;
UpdateSeq _last_screen;
bool _active;
// One mesh per viewer.
Meshes _meshes;
};
typedef pvector<Screen> Screens;
void recompute_if_stale();
void recompute_screen(Screen &screen);
void recompute_screen(Screen &screen, size_t vi);
void render_screen(GraphicsEngine *engine, Screen &screen);
PT(DisplayRegion) _dr;
typedef pvector<Screen> Screens;
Viewers _viewers;
Screens _screens;
NodePath _viewer;
PT(LensNode) _viewer_node;
PT(Camera) _internal_camera;
NodePath _internal_scene;
GraphicsStateGuardian *_gsg;
bool _stale;
UpdateSeq _viewer_lens_change;
};
#include "nonlinearImager.I"