use new offscreen buffer mechanism

This commit is contained in:
David Rose 2004-03-02 02:48:35 +00:00
parent abd0db5458
commit 6f956e1fe7
2 changed files with 90 additions and 93 deletions

View File

@ -32,7 +32,7 @@
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
NonlinearImager:: NonlinearImager::
NonlinearImager() { NonlinearImager() {
_gsg = (GraphicsStateGuardian *)NULL; _engine = (GraphicsEngine *)NULL;
_stale = true; _stale = true;
} }
@ -45,6 +45,11 @@ NonlinearImager::
~NonlinearImager() { ~NonlinearImager() {
remove_all_screens(); remove_all_screens();
remove_all_viewers(); remove_all_viewers();
if (_engine != (GraphicsEngine *)NULL) {
_engine->remove_callback("", GraphicsEngine::CB_pre_frame,
recompute_callback, (void *)this);
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -73,11 +78,12 @@ NonlinearImager::
// screen. // screen.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int NonlinearImager:: int NonlinearImager::
add_screen(ProjectionScreen *screen) { add_screen(ProjectionScreen *screen, const string &name) {
_screens.push_back(Screen()); _screens.push_back(Screen());
Screen &new_screen = _screens.back(); Screen &new_screen = _screens.back();
new_screen._screen = screen; 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_width = 256;
new_screen._tex_height = 256; new_screen._tex_height = 256;
new_screen._active = true; new_screen._active = true;
@ -162,6 +168,20 @@ get_screen(int index) const {
return _screens[index]._screen; 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 // Function: NonlinearImager::set_texture_size
// Access: Published // Access: Published
@ -176,8 +196,18 @@ get_screen(int index) const {
void NonlinearImager:: void NonlinearImager::
set_texture_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_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++) { for (size_t vi = 0; vi < screen._meshes.size(); vi++) {
screen._meshes[vi]._mesh.remove_node(); 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 { } 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.
_stale = true; _stale = true;
@ -250,18 +286,19 @@ get_screen_active(int index) const {
// camera are desired, you should use the // camera are desired, you should use the
// set_viewer_camera() interface. // set_viewer_camera() interface.
// //
// All viewers must share the same // All viewers must share the same GraphicsEngine.
// GraphicsStateGuardian.
// //
// The return value is the index of the new viewer. // The return value is the index of the new viewer.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int NonlinearImager:: int NonlinearImager::
add_viewer(DisplayRegion *dr) { add_viewer(DisplayRegion *dr) {
GraphicsOutput *win = dr->get_window(); GraphicsEngine *engine = dr->get_window()->get_gsg()->get_engine();
GraphicsStateGuardian *gsg = win->get_gsg(); nassertr(_viewers.empty() || (engine == _engine), -1);
nassertr(_viewers.empty() || (gsg == _gsg && win == _win), -1); if (_engine == (GraphicsEngine *)NULL) {
_gsg = gsg; _engine = engine;
_win = win; _engine->add_callback("", GraphicsEngine::CB_pre_frame,
recompute_callback, (void *)this);
}
int previous_vi = find_viewer(dr); int previous_vi = find_viewer(dr);
if (previous_vi >= 0) { if (previous_vi >= 0) {
@ -447,17 +484,11 @@ get_viewer(int index) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
recompute() { 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; size_t vi;
for (vi = 0; vi < _viewers.size(); ++vi) { for (vi = 0; vi < _viewers.size(); ++vi) {
Viewer &viewer = _viewers[vi]; Viewer &viewer = _viewers[vi];
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) { for (si = _screens.begin(); si != _screens.end(); ++si) {
Screen &screen = (*si); Screen &screen = (*si);
if (screen._active) { if (screen._active) {
@ -476,25 +507,16 @@ recompute() {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: NonlinearImager::render // Function: NonlinearImager::recompute_callback
// Access: Published // Access: Private, Static
// Description: Uses the DisplayRegion's GSG to render a scene for // Description: This function is added as a callback to the beginning
// each ProjectionScreen, and makes our DisplayRegion // of the graphics engine's frame, to ensure that all
// ready to render the combined results. This will // frames are up-to-date.
// destroy the contents of the frame buffer; it should
// be done before any of the actual frame has started
// rendering.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void NonlinearImager:: void NonlinearImager::
render(GraphicsEngine *engine) { recompute_callback(void *data) {
recompute_if_stale(); NonlinearImager *self = (NonlinearImager *)data;
self->recompute_if_stale();
Screens::iterator si;
for (si = _screens.begin(); si != _screens.end(); ++si) {
if ((*si)._active) {
render_screen(engine, *si);
}
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -533,6 +555,8 @@ recompute_if_stale() {
if (screen._active && if (screen._active &&
screen._meshes[vi]._last_screen != screen._screen->get_last_screen()) { screen._meshes[vi]._last_screen != screen._screen->get_last_screen()) {
recompute_screen(screen, vi); recompute_screen(screen, vi);
} else {
screen._screen->recompute_if_stale();
} }
} }
} }
@ -555,64 +579,32 @@ recompute_screen(NonlinearImager::Screen &screen, size_t vi) {
return; return;
} }
screen._screen->recompute_if_stale();
Viewer &viewer = _viewers[vi]; Viewer &viewer = _viewers[vi];
PT(PandaNode) mesh = screen._screen->make_flat_mesh(viewer._viewer); PT(PandaNode) mesh = screen._screen->make_flat_mesh(viewer._viewer);
if (mesh != (PandaNode *)NULL) { if (mesh != (PandaNode *)NULL) {
screen._meshes[vi]._mesh = viewer._internal_scene.attach_new_node(mesh); screen._meshes[vi]._mesh = viewer._internal_scene.attach_new_node(mesh);
} }
if (screen._texture == (Texture *)NULL || if (screen._buffer == (GraphicsOutput *)NULL) {
screen._texture->_pbuffer == (PixelBuffer *)NULL || GraphicsOutput *win = viewer._dr->get_window();
screen._texture->_pbuffer->get_xsize() != screen._tex_width || GraphicsOutput *buffer = win->make_texture_buffer(screen._name, screen._tex_width, screen._tex_height);
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; 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(); 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.
}

View File

@ -23,6 +23,7 @@
#include "projectionScreen.h" #include "projectionScreen.h"
#include "displayRegion.h" #include "displayRegion.h"
#include "graphicsOutput.h"
#include "camera.h" #include "camera.h"
#include "texture.h" #include "texture.h"
#include "pandaNode.h" #include "pandaNode.h"
@ -95,13 +96,14 @@ PUBLISHED:
NonlinearImager(); NonlinearImager();
~NonlinearImager(); ~NonlinearImager();
int add_screen(ProjectionScreen *screen); int add_screen(ProjectionScreen *screen, const string &name = string());
int find_screen(ProjectionScreen *screen) const; int find_screen(ProjectionScreen *screen) const;
void remove_screen(int index); void remove_screen(int index);
void remove_all_screens(); void remove_all_screens();
int get_num_screens() const; int get_num_screens() const;
ProjectionScreen *get_screen(int index) 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_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);
@ -122,7 +124,10 @@ PUBLISHED:
DisplayRegion *get_viewer(int index) const; DisplayRegion *get_viewer(int index) const;
void recompute(); void recompute();
void render(GraphicsEngine *engine);
public:
static void recompute_callback(void *data);
void recompute_if_stale();
private: private:
class Viewer { class Viewer {
@ -146,7 +151,8 @@ private:
class Screen { class Screen {
public: public:
PT(ProjectionScreen) _screen; PT(ProjectionScreen) _screen;
PT(Texture) _texture; string _name;
PT(GraphicsOutput) _buffer;
NodePath _source_camera; NodePath _source_camera;
int _tex_width, _tex_height; int _tex_width, _tex_height;
bool _active; bool _active;
@ -156,14 +162,13 @@ private:
}; };
typedef pvector<Screen> Screens; typedef pvector<Screen> Screens;
void recompute_if_stale();
void recompute_screen(Screen &screen, size_t vi); void recompute_screen(Screen &screen, size_t vi);
void render_screen(GraphicsEngine *engine, Screen &screen); void render_screen(GraphicsEngine *engine, Screen &screen);
Viewers _viewers; Viewers _viewers;
Screens _screens; Screens _screens;
GraphicsStateGuardian *_gsg;
GraphicsOutput *_win; GraphicsEngine *_engine;
bool _stale; bool _stale;
}; };