diff --git a/panda/src/distort/nonlinearImager.cxx b/panda/src/distort/nonlinearImager.cxx index 1b0af4c59e..1ca3954298 100644 --- a/panda/src/distort/nonlinearImager.cxx +++ b/panda/src/distort/nonlinearImager.cxx @@ -32,7 +32,7 @@ //////////////////////////////////////////////////////////////////// NonlinearImager:: NonlinearImager() { - _gsg = (GraphicsStateGuardian *)NULL; + _engine = (GraphicsEngine *)NULL; _stale = true; } @@ -45,6 +45,11 @@ NonlinearImager:: ~NonlinearImager() { remove_all_screens(); remove_all_viewers(); + + if (_engine != (GraphicsEngine *)NULL) { + _engine->remove_callback("", GraphicsEngine::CB_pre_frame, + recompute_callback, (void *)this); + } } //////////////////////////////////////////////////////////////////// @@ -73,11 +78,12 @@ NonlinearImager:: // screen. //////////////////////////////////////////////////////////////////// int NonlinearImager:: -add_screen(ProjectionScreen *screen) { +add_screen(ProjectionScreen *screen, const string &name) { _screens.push_back(Screen()); Screen &new_screen = _screens.back(); new_screen._screen = screen; - new_screen._texture = (Texture *)NULL; + new_screen._name = name; + new_screen._buffer = (GraphicsOutput *)NULL; new_screen._tex_width = 256; new_screen._tex_height = 256; new_screen._active = true; @@ -162,6 +168,20 @@ get_screen(int index) const { return _screens[index]._screen; } +//////////////////////////////////////////////////////////////////// +// Function: NonlinearImager::get_buffer +// Access: Published +// Description: Returns the offscreen buffer that is automatically +// created for the nth projection screen. This may +// return NULL if the screen is inactive or if it has +// not been rendered yet. +//////////////////////////////////////////////////////////////////// +GraphicsOutput *NonlinearImager:: +get_buffer(int index) const { + nassertr(index >= 0 && index < (int)_screens.size(), (GraphicsOutput *)NULL); + return _screens[index]._buffer; +} + //////////////////////////////////////////////////////////////////// // Function: NonlinearImager::set_texture_size // Access: Published @@ -176,8 +196,18 @@ get_screen(int index) const { void NonlinearImager:: 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; + + Screen &screen = _screens[index]; + + screen._tex_width = width; + screen._tex_height = height; + + if (screen._buffer != (GraphicsOutput *)NULL) { + _engine->remove_window(screen._buffer); + screen._buffer = (GraphicsOutput *)NULL; + } + + _stale = true; } //////////////////////////////////////////////////////////////////// @@ -216,7 +246,13 @@ set_screen_active(int index, bool active) { for (size_t vi = 0; vi < screen._meshes.size(); vi++) { screen._meshes[vi]._mesh.remove_node(); } - screen._texture.clear(); + + // Also remove its buffer. + if (screen._buffer != (GraphicsOutput *)NULL) { + _engine->remove_window(screen._buffer); + screen._buffer = (GraphicsOutput *)NULL; + } + } else { // If we've just made it active, it needs to be recomputed. _stale = true; @@ -250,18 +286,19 @@ get_screen_active(int index) const { // camera are desired, you should use the // set_viewer_camera() interface. // -// All viewers must share the same -// GraphicsStateGuardian. +// All viewers must share the same GraphicsEngine. // // The return value is the index of the new viewer. //////////////////////////////////////////////////////////////////// int NonlinearImager:: add_viewer(DisplayRegion *dr) { - GraphicsOutput *win = dr->get_window(); - GraphicsStateGuardian *gsg = win->get_gsg(); - nassertr(_viewers.empty() || (gsg == _gsg && win == _win), -1); - _gsg = gsg; - _win = win; + GraphicsEngine *engine = dr->get_window()->get_gsg()->get_engine(); + nassertr(_viewers.empty() || (engine == _engine), -1); + if (_engine == (GraphicsEngine *)NULL) { + _engine = engine; + _engine->add_callback("", GraphicsEngine::CB_pre_frame, + recompute_callback, (void *)this); + } int previous_vi = find_viewer(dr); if (previous_vi >= 0) { @@ -447,17 +484,11 @@ get_viewer(int index) const { //////////////////////////////////////////////////////////////////// void NonlinearImager:: recompute() { - // First, force all the textures to clear. - Screens::iterator si; - for (si = _screens.begin(); si != _screens.end(); ++si) { - Screen &screen = (*si); - screen._texture.clear(); - } - size_t vi; for (vi = 0; vi < _viewers.size(); ++vi) { Viewer &viewer = _viewers[vi]; + Screens::iterator si; for (si = _screens.begin(); si != _screens.end(); ++si) { Screen &screen = (*si); if (screen._active) { @@ -476,25 +507,16 @@ recompute() { } //////////////////////////////////////////////////////////////////// -// 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. +// Function: NonlinearImager::recompute_callback +// Access: Private, Static +// Description: This function is added as a callback to the beginning +// of the graphics engine's frame, to ensure that all +// frames are up-to-date. //////////////////////////////////////////////////////////////////// void NonlinearImager:: -render(GraphicsEngine *engine) { - recompute_if_stale(); - - Screens::iterator si; - for (si = _screens.begin(); si != _screens.end(); ++si) { - if ((*si)._active) { - render_screen(engine, *si); - } - } +recompute_callback(void *data) { + NonlinearImager *self = (NonlinearImager *)data; + self->recompute_if_stale(); } //////////////////////////////////////////////////////////////////// @@ -533,6 +555,8 @@ recompute_if_stale() { if (screen._active && screen._meshes[vi]._last_screen != screen._screen->get_last_screen()) { recompute_screen(screen, vi); + } else { + screen._screen->recompute_if_stale(); } } } @@ -555,64 +579,32 @@ recompute_screen(NonlinearImager::Screen &screen, size_t vi) { return; } + screen._screen->recompute_if_stale(); + 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); } - 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); + if (screen._buffer == (GraphicsOutput *)NULL) { + GraphicsOutput *win = viewer._dr->get_window(); + GraphicsOutput *buffer = win->make_texture_buffer(screen._name, screen._tex_width, screen._tex_height); - screen._texture = texture; + if (buffer != (GraphicsOutput *)NULL) { + screen._buffer = buffer; + GraphicsLayer *layer = buffer->get_channel(0)->make_layer(); + DisplayRegion *dr = layer->make_display_region(); + dr->set_camera(screen._source_camera); + + } else { + screen._meshes[vi]._mesh.clear_texture(); + } + } + + if (screen._buffer != (GraphicsOutput *)NULL) { + screen._meshes[vi]._mesh.set_texture(screen._buffer->get_texture()); } - screen._meshes[vi]._mesh.set_texture(screen._texture); screen._meshes[vi]._last_screen = screen._screen->get_last_screen(); } - -//////////////////////////////////////////////////////////////////// -// 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(GraphicsEngine *engine, NonlinearImager::Screen &screen) { - if (screen._source_camera.is_empty()) { - distort_cat.error() - << "No source camera specified for screen " << screen._screen->get_name() - << "\n"; - return; - } - - nassertv(_gsg != (GraphicsStateGuardian *)NULL); - - screen._screen->recompute_if_stale(); - - // Make a display region of the proper size and clear it to prepare for - // rendering the scene. - PT(DisplayRegion) scratch_region = - _win->make_scratch_display_region(screen._tex_width, screen._tex_height); - scratch_region->set_camera(screen._source_camera); - engine->render_subframe(_gsg, scratch_region, true); - - // Copy the results of the render from the frame buffer into the - // screen's texture. - TextureContext *tc = screen._texture->prepare_now(_gsg->get_prepared_objects(), _gsg); - _gsg->copy_texture(tc, 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 - // to the next. -} diff --git a/panda/src/distort/nonlinearImager.h b/panda/src/distort/nonlinearImager.h index bca0a623a3..b99a7bb54a 100644 --- a/panda/src/distort/nonlinearImager.h +++ b/panda/src/distort/nonlinearImager.h @@ -23,6 +23,7 @@ #include "projectionScreen.h" #include "displayRegion.h" +#include "graphicsOutput.h" #include "camera.h" #include "texture.h" #include "pandaNode.h" @@ -95,13 +96,14 @@ PUBLISHED: NonlinearImager(); ~NonlinearImager(); - int add_screen(ProjectionScreen *screen); + int add_screen(ProjectionScreen *screen, const string &name = string()); 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; + GraphicsOutput *get_buffer(int index) const; void set_texture_size(int index, int width, int height); void set_source_camera(int index, const NodePath &source_camera); @@ -122,7 +124,10 @@ PUBLISHED: DisplayRegion *get_viewer(int index) const; void recompute(); - void render(GraphicsEngine *engine); + +public: + static void recompute_callback(void *data); + void recompute_if_stale(); private: class Viewer { @@ -146,7 +151,8 @@ private: class Screen { public: PT(ProjectionScreen) _screen; - PT(Texture) _texture; + string _name; + PT(GraphicsOutput) _buffer; NodePath _source_camera; int _tex_width, _tex_height; bool _active; @@ -156,14 +162,13 @@ private: }; typedef pvector Screens; - void recompute_if_stale(); void recompute_screen(Screen &screen, size_t vi); void render_screen(GraphicsEngine *engine, Screen &screen); Viewers _viewers; Screens _screens; - GraphicsStateGuardian *_gsg; - GraphicsOutput *_win; + + GraphicsEngine *_engine; bool _stale; };