From f030c6333f9cdf2ab386aff6308df691df636394 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 13 Dec 2001 23:43:24 +0000 Subject: [PATCH] add nonlinearImager --- panda/src/distort/Sources.pp | 1 + panda/src/distort/nonlinearImager.I | 43 +++ panda/src/distort/nonlinearImager.cxx | 365 ++++++++++++++++++++ panda/src/distort/nonlinearImager.h | 101 ++++++ panda/src/glgsg/glGraphicsStateGuardian.cxx | 9 +- panda/src/sgattrib/billboardTransition.cxx | 5 + 6 files changed, 523 insertions(+), 1 deletion(-) create mode 100644 panda/src/distort/nonlinearImager.I create mode 100644 panda/src/distort/nonlinearImager.cxx create mode 100644 panda/src/distort/nonlinearImager.h diff --git a/panda/src/distort/Sources.pp b/panda/src/distort/Sources.pp index 59572a2227..7858716e19 100644 --- a/panda/src/distort/Sources.pp +++ b/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 diff --git a/panda/src/distort/nonlinearImager.I b/panda/src/distort/nonlinearImager.I new file mode 100644 index 0000000000..f84824bcea --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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; +} diff --git a/panda/src/distort/nonlinearImager.cxx b/panda/src/distort/nonlinearImager.cxx new file mode 100644 index 0000000000..44f98a5f9b --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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); +} diff --git a/panda/src/distort/nonlinearImager.h b/panda/src/distort/nonlinearImager.h new file mode 100644 index 0000000000..8739dcd2af --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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 Screens; + Screens _screens; + + PT(LensNode) _camera; + + PT(Camera) _internal_camera; + PT_Node _internal_scene; + + bool _stale; + UpdateSeq _camera_lens_change; +}; + +#include "nonlinearImager.I" + +#endif diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index bfa69571f9..cda452d051 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/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()); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/sgattrib/billboardTransition.cxx b/panda/src/sgattrib/billboardTransition.cxx index a9bfdaf2c5..0e8b4dfdc4 100644 --- a/panda/src/sgattrib/billboardTransition.cxx +++ b/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.