Panda3D

graphicsOutput.cxx

00001 // Filename: graphicsOutput.cxx
00002 // Created by:  drose (06Feb04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "graphicsOutput.h"
00016 #include "graphicsPipe.h"
00017 #include "graphicsEngine.h"
00018 #include "graphicsWindow.h"
00019 #include "config_display.h"
00020 #include "lightMutexHolder.h"
00021 #include "renderBuffer.h"
00022 #include "indirectLess.h"
00023 #include "pStatTimer.h"
00024 #include "configVariableBool.h"
00025 #include "camera.h"
00026 #include "displayRegion.h"
00027 #include "lens.h"
00028 #include "perspectiveLens.h"
00029 #include "pointerTo.h"
00030 #include "compassEffect.h"
00031 #include "geom.h"
00032 #include "geomNode.h"
00033 #include "geomTristrips.h"
00034 #include "geomVertexWriter.h"
00035 #include "throw_event.h"
00036 #include "config_gobj.h"
00037 
00038 TypeHandle GraphicsOutput::_type_handle;
00039 
00040 PStatCollector GraphicsOutput::_make_current_pcollector("Draw:Make current");
00041 PStatCollector GraphicsOutput::_copy_texture_pcollector("Draw:Copy texture");
00042 PStatCollector GraphicsOutput::_cull_pcollector("Cull");
00043 PStatCollector GraphicsOutput::_draw_pcollector("Draw");
00044 
00045 struct CubeFaceDef {
00046   CubeFaceDef(const char *name, const LPoint3 &look_at, const LVector3 &up) :
00047     _name(name), _look_at(look_at), _up(up) { }
00048 
00049   const char *_name;
00050   LPoint3 _look_at;
00051   LVector3 _up;
00052 };
00053 
00054 static CubeFaceDef cube_faces[6] = {
00055   CubeFaceDef("positive_x", LPoint3(1, 0, 0), LVector3(0, -1, 0)),
00056   CubeFaceDef("negative_x", LPoint3(-1, 0, 0), LVector3(0, -1, 0)),
00057   CubeFaceDef("positive_y", LPoint3(0, 1, 0), LVector3(0, 0, 1)),
00058   CubeFaceDef("negative_y", LPoint3(0, -1, 0), LVector3(0, 0, -1)),
00059   CubeFaceDef("positive_z", LPoint3(0, 0, 1), LVector3(0, -1, 0)),
00060   CubeFaceDef("negative_z", LPoint3(0, 0, -1), LVector3(0, -1, 0))
00061 };
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: GraphicsOutput::Constructor
00065 //       Access: Protected
00066 //  Description: Normally, the GraphicsOutput constructor is not
00067 //               called directly; these are created instead via the
00068 //               GraphicsEngine::make_window() function.
00069 ////////////////////////////////////////////////////////////////////
00070 GraphicsOutput::
00071 GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
00072                const string &name,
00073                const FrameBufferProperties &fb_prop,
00074                const WindowProperties &win_prop,
00075                int flags,
00076                GraphicsStateGuardian *gsg,
00077                GraphicsOutput *host,
00078                bool default_stereo_flags) :
00079   _lock("GraphicsOutput"),
00080   _cull_window_pcollector(_cull_pcollector, name),
00081   _draw_window_pcollector(_draw_pcollector, name)
00082 {
00083 #ifdef DO_MEMORY_USAGE
00084   MemoryUsage::update_type(this, this);
00085 #endif
00086   _engine = engine;
00087   _pipe = pipe;
00088   _gsg = gsg;
00089   _host = host;
00090   _fb_properties = fb_prop;
00091   _name = name;
00092   _creation_flags = flags;
00093   _x_size = _y_size = 0;
00094   _has_size = win_prop.has_size();
00095   _is_nonzero_size = false;
00096   if (_has_size) {
00097     _x_size = win_prop.get_x_size();
00098     _y_size = win_prop.get_y_size();
00099     _is_nonzero_size = (_x_size > 0 && _y_size > 0);
00100   }
00101   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
00102     // If we're tracking the host size, we assume we'll be nonzero
00103     // eventually.
00104     _is_nonzero_size = true;
00105   }
00106 
00107   _is_valid = false;
00108   _flip_ready = false;
00109   _cube_map_index = -1;
00110   _cube_map_dr = NULL;
00111   _sort = 0;
00112   _child_sort = 0;
00113   _got_child_sort = false;
00114   _internal_sort_index = 0;
00115   _inverted = window_inverted;
00116   _swap_eyes = swap_eyes;
00117   _red_blue_stereo = false;
00118   _left_eye_color_mask = 0x0f;
00119   _right_eye_color_mask = 0x0f;
00120   _side_by_side_stereo = false;
00121   _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
00122   _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
00123   _delete_flag = false;
00124   _texture_card = 0;
00125   _trigger_copy = false;
00126 
00127   if (_fb_properties.is_single_buffered()) {
00128     _draw_buffer_type = RenderBuffer::T_front;
00129   } else {
00130     _draw_buffer_type = RenderBuffer::T_back;
00131   }
00132 
00133   if (default_stereo_flags) {
00134     // Check the config variables to see if we should make this a
00135     // "stereo" buffer or window.
00136     _red_blue_stereo = red_blue_stereo && !fb_prop.is_stereo();
00137     if (_red_blue_stereo) {
00138       _left_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(0));
00139       _right_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(1));
00140     }
00141     _side_by_side_stereo = side_by_side_stereo && !fb_prop.is_stereo();
00142     if (_side_by_side_stereo) {
00143       _sbs_left_dimensions.set(sbs_left_dimensions[0], sbs_left_dimensions[1], 
00144                                sbs_left_dimensions[2], sbs_left_dimensions[3]);
00145       _sbs_right_dimensions.set(sbs_right_dimensions[0], sbs_right_dimensions[1], 
00146                                 sbs_right_dimensions[2], sbs_right_dimensions[3]);
00147     }
00148   }
00149 
00150   // We start out with one DisplayRegion that covers the whole window,
00151   // which we may use internally for full-window operations like
00152   // clear() and get_screenshot().
00153   _overlay_display_region = make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
00154   _overlay_display_region->set_active(false);
00155 
00156   // Make sure the "active" flag is set true for pipeline stage 0.
00157   {
00158     CDWriter cdata(_cycler, true);
00159     cdata->_active = true;
00160   }
00161 
00162   // By default, each new GraphicsOutput is set up to clear color and
00163   // depth.
00164   set_clear_color_active(true);
00165   set_clear_depth_active(true);
00166   set_clear_stencil_active(true);
00167 
00168   switch (background_color.get_num_words()) {
00169   case 1:
00170     set_clear_color(LColor(background_color[0], background_color[0], background_color[0], 0.0f));
00171     break;
00172 
00173   case 2:
00174     set_clear_color(LColor(background_color[0], background_color[0], background_color[0], background_color[1]));
00175     break;
00176 
00177   case 3:
00178     set_clear_color(LColor(background_color[0], background_color[1], background_color[2], 0.0f));
00179     break;
00180 
00181   case 4:
00182     set_clear_color(LColor(background_color[0], background_color[1], background_color[2], background_color[3]));
00183     break;
00184 
00185   default:
00186     display_cat.warning()
00187       << "Invalid background-color specification: "
00188       << background_color.get_string_value() << "\n";
00189   }
00190 }
00191 
00192 ////////////////////////////////////////////////////////////////////
00193 //     Function: GraphicsOutput::Copy Constructor
00194 //       Access: Private
00195 //  Description:
00196 ////////////////////////////////////////////////////////////////////
00197 GraphicsOutput::
00198 GraphicsOutput(const GraphicsOutput &) :
00199   _cull_window_pcollector(_cull_pcollector, "Invalid"),
00200   _draw_window_pcollector(_draw_pcollector, "Invalid")
00201 {
00202   nassertv(false);
00203 }
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: GraphicsOutput::Copy Assignment Operator
00207 //       Access: Private
00208 //  Description:
00209 ////////////////////////////////////////////////////////////////////
00210 void GraphicsOutput::
00211 operator = (const GraphicsOutput &) {
00212   nassertv(false);
00213 }
00214 
00215 ////////////////////////////////////////////////////////////////////
00216 //     Function: GraphicsOutput::Destructor
00217 //       Access: Published, Virtual
00218 //  Description:
00219 ////////////////////////////////////////////////////////////////////
00220 GraphicsOutput::
00221 ~GraphicsOutput() {
00222   // The window should be closed by the time we destruct.
00223   nassertv(!is_valid());
00224 
00225   // We shouldn't have a GraphicsPipe pointer anymore.
00226   nassertv(_pipe == (GraphicsPipe *)NULL);
00227 
00228   // We don't have to destruct our child display regions explicitly,
00229   // since they are all reference-counted and will go away when their
00230   // pointers do.  However, we do need to zero out their pointers to
00231   // us.
00232   TotalDisplayRegions::iterator dri;
00233   for (dri = _total_display_regions.begin();
00234        dri != _total_display_regions.end();
00235        ++dri) {
00236     (*dri)->_window = NULL;
00237   }
00238 
00239   _total_display_regions.clear();
00240   _overlay_display_region = NULL;
00241 }
00242 
00243 ////////////////////////////////////////////////////////////////////
00244 //     Function: GraphicsOutput::clear_render_textures
00245 //       Access: Published
00246 //  Description: If the GraphicsOutput is currently rendering to
00247 //               a texture, then all textures are dissociated from
00248 //               the GraphicsOuput.
00249 ////////////////////////////////////////////////////////////////////
00250 void GraphicsOutput::
00251 clear_render_textures() {
00252   CDWriter cdata(_cycler, true);
00253   cdata->_textures.clear();
00254   ++(cdata->_textures_seq);
00255   throw_event("render-texture-targets-changed");
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: GraphicsOutput::add_render_texture
00260 //       Access: Published
00261 //  Description: Creates a new Texture object, suitable for rendering
00262 //               the contents of this buffer into, and appends it to
00263 //               the list of render textures.
00264 //
00265 //               If tex is not NULL, it is the texture that will be
00266 //               set up for rendering into; otherwise, a new Texture
00267 //               object will be created, in which case you may call
00268 //               get_texture() to retrieve the new texture pointer.
00269 //
00270 //               You can specify a bitplane to attach the texture to.
00271 //               the legal choices are:
00272 //
00273 //               * RTP_depth
00274 //               * RTP_depth_stencil
00275 //               * RTP_color
00276 //               * RTP_aux_rgba_0
00277 //               * RTP_aux_rgba_1
00278 //               * RTP_aux_rgba_2
00279 //               * RTP_aux_rgba_3
00280 //
00281 //               If you do not specify a bitplane to attach the
00282 //               texture to, this routine will use a default based
00283 //               on the texture's format:
00284 //
00285 //               * F_depth_component attaches to RTP_depth
00286 //               * F_depth_stencil attaches to RTP_depth_stencil
00287 //               * all other formats attach to RTP_color.
00288 //
00289 //               The texture's format will be changed to match
00290 //               the format of the bitplane to which it is attached.
00291 //               For example, if you pass in an F_rgba texture and
00292 //               order that it be attached to RTP_depth_stencil, it will turn
00293 //               into an F_depth_stencil texture.
00294 //
00295 //               Also see make_texture_buffer(), which is a
00296 //               higher-level interface for preparing
00297 //               render-to-a-texture mode.
00298 ////////////////////////////////////////////////////////////////////
00299 void GraphicsOutput::
00300 add_render_texture(Texture *tex, RenderTextureMode mode,
00301                    RenderTexturePlane plane) {
00302 
00303   if (mode == RTM_none) {
00304     return;
00305   }
00306   LightMutexHolder holder(_lock);
00307 
00308   // Create texture if necessary.
00309   if (tex == (Texture *)NULL) {
00310     tex = new Texture(get_name());
00311     tex->set_wrap_u(Texture::WM_clamp);
00312     tex->set_wrap_v(Texture::WM_clamp);
00313   } else {
00314     tex->clear_ram_image();
00315   }
00316 
00317   // Set it to have no compression by default.  You can restore
00318   // compression later if you really, really want it; but this freaks
00319   // out some drivers, and presumably it's a mistake if you have
00320   // compression enabled for a rendered texture.
00321   tex->set_compression(Texture::CM_off);
00322 
00323   // Choose a default bitplane.
00324   if (plane == RTP_COUNT) {
00325     if (tex->get_format()==Texture::F_depth_stencil) {
00326       plane = RTP_depth_stencil;
00327     } else if (tex->get_format()==Texture::F_depth_component) {
00328       plane = RTP_depth;
00329     } else {
00330       plane = RTP_color;
00331     }
00332   }
00333 
00334   // Set the texture's format to match the bitplane.
00335   // (And validate the bitplane, while we're at it).
00336 
00337   if (plane == RTP_depth) {
00338     tex->set_format(Texture::F_depth_component);
00339     tex->set_match_framebuffer_format(true);
00340   } else if (plane == RTP_depth_stencil) {
00341     tex->set_format(Texture::F_depth_stencil);
00342     tex->set_match_framebuffer_format(true);
00343   } else if ((plane == RTP_color)||
00344              (plane == RTP_aux_rgba_0)||
00345              (plane == RTP_aux_rgba_1)||
00346              (plane == RTP_aux_rgba_2)||
00347              (plane == RTP_aux_rgba_3)) {
00348     tex->set_format(Texture::F_rgba);
00349     tex->set_match_framebuffer_format(true);
00350   } else if  ((plane == RTP_aux_hrgba_0)||
00351               (plane == RTP_aux_hrgba_1)||
00352               (plane == RTP_aux_hrgba_2)||
00353               (plane == RTP_aux_hrgba_3)) {
00354     tex->set_format(Texture::F_rgba16);
00355     tex->set_match_framebuffer_format(true);
00356   } else if ((plane == RTP_aux_float_0)||
00357               (plane == RTP_aux_float_1)||
00358               (plane == RTP_aux_float_2)||
00359               (plane == RTP_aux_float_3)) {
00360     tex->set_format(Texture::F_rgba32);
00361     tex->set_match_framebuffer_format(true);
00362   } else {
00363     display_cat.error() <<
00364       "add_render_texture: invalid bitplane specified.\n";
00365     return;
00366   }
00367 
00368   // Go ahead and tell the texture our anticipated size, even if it
00369   // might be inaccurate (particularly if this is a GraphicsWindow,
00370   // which has system-imposed restrictions on size).
00371   tex->set_size_padded(get_x_size(), get_y_size());
00372 
00373   if (mode == RTM_bind_or_copy) {
00374     if (!support_render_texture || !get_supports_render_texture()) {
00375       // Binding is not supported or it is disabled, so just fall back
00376       // to copy instead.
00377       mode = RTM_copy_texture;
00378     }
00379   }
00380 
00381   if (mode == RTM_bind_or_copy) {
00382     // If we're still planning on binding, indicate it in texture
00383     // properly.
00384     tex->set_render_to_texture(true);
00385   }
00386 
00387   CDWriter cdata(_cycler, true);
00388   RenderTexture result;
00389   result._texture = tex;
00390   result._plane = plane;
00391   result._rtm_mode = mode;
00392   cdata->_textures.push_back(result);
00393   ++(cdata->_textures_seq);
00394 
00395   throw_event("render-texture-targets-changed");
00396 }
00397 
00398 ////////////////////////////////////////////////////////////////////
00399 //     Function: GraphicsOutput::setup_render_texture
00400 //       Access: Published
00401 //  Description: This is a deprecated interface that made sense back
00402 //               when GraphicsOutputs could only render into one
00403 //               texture at a time.  From now on, use
00404 //               clear_render_textures and add_render_texture
00405 //               instead.
00406 ////////////////////////////////////////////////////////////////////
00407 void GraphicsOutput::
00408 setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) {
00409   display_cat.warning() <<
00410     "Using deprecated setup_render_texture interface.\n";
00411   clear_render_textures();
00412   if (to_ram) {
00413     add_render_texture(tex, RTM_copy_ram);
00414   } else if (allow_bind) {
00415     add_render_texture(tex, RTM_bind_or_copy);
00416   } else {
00417     add_render_texture(tex, RTM_copy_texture);
00418   }
00419 }
00420 
00421 ////////////////////////////////////////////////////////////////////
00422 //     Function: GraphicsOutput::set_active
00423 //       Access: Published
00424 //  Description: Sets the active flag associated with the
00425 //               GraphicsOutput.  If the GraphicsOutput is marked
00426 //               inactive, nothing is rendered.
00427 ////////////////////////////////////////////////////////////////////
00428 void GraphicsOutput::
00429 set_active(bool active) {
00430   CDLockedReader cdata(_cycler);
00431   if (cdata->_active != active) {
00432     CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
00433     cdataw->_active = active;
00434   }
00435 }
00436 
00437 ////////////////////////////////////////////////////////////////////
00438 //     Function: GraphicsOutput::is_active
00439 //       Access: Published, Virtual
00440 //  Description: Returns true if the window is ready to be rendered
00441 //               into, false otherwise.
00442 ////////////////////////////////////////////////////////////////////
00443 bool GraphicsOutput::
00444 is_active() const {
00445   if (!is_valid()) {
00446     return false;
00447   }
00448 
00449   CDReader cdata(_cycler);
00450   if (cdata->_one_shot_frame != -1) {
00451     // If one_shot is in effect, then we are active only for the one
00452     // indicated frame.
00453     if (cdata->_one_shot_frame != ClockObject::get_global_clock()->get_frame_count()) {
00454       return false;
00455     }
00456   }
00457   return cdata->_active;
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////
00461 //     Function: GraphicsOutput::set_one_shot
00462 //       Access: Published
00463 //  Description: Changes the current setting of the one-shot flag.
00464 //               When this is true, the GraphicsOutput will render the
00465 //               current frame and then automatically set itself
00466 //               inactive.  This is particularly useful for buffers
00467 //               that are created for the purposes of
00468 //               render-to-texture, for static textures that don't
00469 //               need to be continually re-rendered once they have
00470 //               been rendered the first time.
00471 //
00472 //               Setting the buffer inactive is not the same thing as
00473 //               destroying it.  You are still responsible for passing
00474 //               this buffer to GraphicsEngine::remove_window() when
00475 //               you no longer need the texture, in order to clean up
00476 //               fully.  (However, you should not call remove_window()
00477 //               on this buffer while the texture is still needed,
00478 //               because depending on the render-to-texture mechanism
00479 //               in use, this may invalidate the texture contents.)
00480 ////////////////////////////////////////////////////////////////////
00481 void GraphicsOutput::
00482 set_one_shot(bool one_shot) {
00483   CDWriter cdata(_cycler, true);
00484   if (one_shot) {
00485     cdata->_one_shot_frame = ClockObject::get_global_clock()->get_frame_count();
00486   } else {
00487     cdata->_one_shot_frame = -1;
00488   }
00489 }
00490 
00491 ////////////////////////////////////////////////////////////////////
00492 //     Function: GraphicsOutput::get_one_shot
00493 //       Access: Published
00494 //  Description: Returns the current setting of the one-shot flag.
00495 //               When this is true, the GraphicsOutput will
00496 //               automatically set itself inactive after the next
00497 //               frame.
00498 ////////////////////////////////////////////////////////////////////
00499 bool GraphicsOutput::
00500 get_one_shot() const {
00501   CDReader cdata(_cycler);
00502   return (cdata->_one_shot_frame == ClockObject::get_global_clock()->get_frame_count());
00503 }
00504 
00505 ////////////////////////////////////////////////////////////////////
00506 //     Function: GraphicsOutput::set_inverted
00507 //       Access: Published
00508 //  Description: Changes the current setting of the inverted flag.
00509 //               When this is true, the scene is rendered into the
00510 //               window upside-down and backwards, that is, inverted
00511 //               as if viewed through a mirror placed on the floor.
00512 //
00513 //               This is primarily intended to support DirectX (and a
00514 //               few buggy OpenGL graphics drivers) that perform a
00515 //               framebuffer-to-texture copy upside-down from the
00516 //               usual OpenGL (and Panda) convention.  Panda will
00517 //               automatically set this flag for offscreen buffers on
00518 //               hardware that is known to do this, to compensate when
00519 //               rendering offscreen into a texture.
00520 ////////////////////////////////////////////////////////////////////
00521 void GraphicsOutput::
00522 set_inverted(bool inverted) {
00523   if (_inverted != inverted) {
00524     _inverted = inverted;
00525 
00526     if (_y_size != 0) {
00527       // All of our DisplayRegions need to recompute their pixel
00528       // positions now.
00529       TotalDisplayRegions::iterator dri;
00530       for (dri = _total_display_regions.begin();
00531            dri != _total_display_regions.end();
00532            ++dri) {
00533         (*dri)->compute_pixels(_x_size, _y_size);
00534       }
00535     }
00536   }
00537 }
00538 
00539 ////////////////////////////////////////////////////////////////////
00540 //     Function: GraphicsOutput::set_side_by_side_stereo
00541 //       Access: Published
00542 //  Description: Enables side-by-side stereo mode on this particular
00543 //               window.  When side-by-side stereo mode is in effect,
00544 //               DisplayRegions that have the "left" channel set will
00545 //               render on the part of the window specified by
00546 //               sbs_left_dimensions (typically the left half: (0,
00547 //               0.5, 0, 1)), while DisplayRegions that have the
00548 //               "right" channel set will render on the part of the
00549 //               window specified by sbs_right_dimensions (typically
00550 //               the right half: (0.5, 1, 0, 1)).
00551 //
00552 //               This is commonly used in a dual-monitor mode, where a
00553 //               window is opened that spans two monitors, and each
00554 //               monitor represents a different eye.
00555 ////////////////////////////////////////////////////////////////////
00556 void GraphicsOutput::
00557 set_side_by_side_stereo(bool side_by_side_stereo) {
00558   LVecBase4 left, right;
00559   left.set(sbs_left_dimensions[0], sbs_left_dimensions[1], 
00560            sbs_left_dimensions[2], sbs_left_dimensions[3]);
00561   right.set(sbs_right_dimensions[0], sbs_right_dimensions[1], 
00562             sbs_right_dimensions[2], sbs_right_dimensions[3]);
00563   set_side_by_side_stereo(side_by_side_stereo, left, right);
00564 }
00565 
00566 ////////////////////////////////////////////////////////////////////
00567 //     Function: GraphicsOutput::set_side_by_side_stereo
00568 //       Access: Published
00569 //  Description: Enables side-by-side stereo mode on this particular
00570 //               window.  When side-by-side stereo mode is in effect,
00571 //               DisplayRegions that have the "left" channel set will
00572 //               render on the part of the window specified by
00573 //               sbs_left_dimensions (typically the left half: (0,
00574 //               0.5, 0, 1)), while DisplayRegions that have the
00575 //               "right" channel set will render on the part of the
00576 //               window specified by sbs_right_dimensions (typically
00577 //               the right half: (0.5, 1, 0, 1)).
00578 //
00579 //               This is commonly used in a dual-monitor mode, where a
00580 //               window is opened that spans two monitors, and each
00581 //               monitor represents a different eye.
00582 ////////////////////////////////////////////////////////////////////
00583 void GraphicsOutput::
00584 set_side_by_side_stereo(bool side_by_side_stereo,
00585                         const LVecBase4 &sbs_left_dimensions,
00586                         const LVecBase4 &sbs_right_dimensions) {
00587   _side_by_side_stereo = side_by_side_stereo;
00588   if (_side_by_side_stereo) {
00589     _sbs_left_dimensions = sbs_left_dimensions;
00590     _sbs_right_dimensions = sbs_right_dimensions;
00591   } else {
00592     _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
00593     _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
00594   }
00595 }
00596 
00597 ////////////////////////////////////////////////////////////////////
00598 //     Function: GraphicsOutput::get_delete_flag
00599 //       Access: Published
00600 //  Description: Returns the current setting of the delete flag.  When
00601 //               this is true, the GraphicsOutput will automatically
00602 //               be removed before the beginning of the next frame by
00603 //               the GraphicsEngine.
00604 ////////////////////////////////////////////////////////////////////
00605 bool GraphicsOutput::
00606 get_delete_flag() const {
00607   // We only delete the window or buffer automatically when it is
00608   // no longer associated with a texture.
00609   for (int i = 0; i < (int)_hold_textures.size(); i++) {
00610     if (_hold_textures[i].is_valid_pointer()) {
00611       return false;
00612     }
00613   }
00614 
00615   return _delete_flag;
00616 }
00617 
00618 ////////////////////////////////////////////////////////////////////
00619 //     Function: GraphicsOutput::set_sort
00620 //       Access: Published, Virtual
00621 //  Description: Adjusts the sorting order of this particular
00622 //               GraphicsOutput, relative to other GraphicsOutputs.
00623 ////////////////////////////////////////////////////////////////////
00624 void GraphicsOutput::
00625 set_sort(int sort) {
00626   if (_sort != sort) {
00627     if (_gsg != (GraphicsStateGuardian *)NULL &&
00628         _gsg->get_engine() != (GraphicsEngine *)NULL) {
00629       _gsg->get_engine()->set_window_sort(this, sort);
00630     }
00631   }
00632 }
00633 
00634 ////////////////////////////////////////////////////////////////////
00635 //     Function: GraphicsOutput::make_display_region
00636 //       Access: Published
00637 //  Description: Creates a new DisplayRegion that covers the indicated
00638 //               sub-rectangle within the window.  The range on all
00639 //               parameters is 0..1.
00640 //
00641 //               If is_stereo() is true for this window, and
00642 //               default-stereo-camera is configured true, this
00643 //               actually makes a StereoDisplayRegion.  Call
00644 //               make_mono_display_region() or
00645 //               make_stereo_display_region() if you want to insist on
00646 //               one or the other.
00647 ////////////////////////////////////////////////////////////////////
00648 DisplayRegion *GraphicsOutput::
00649 make_display_region(const LVecBase4 &dimensions) {
00650   if (is_stereo() && default_stereo_camera) {
00651     return make_stereo_display_region(dimensions);
00652   } else {
00653     return make_mono_display_region(dimensions);
00654   }
00655 }
00656 
00657 ////////////////////////////////////////////////////////////////////
00658 //     Function: GraphicsOutput::make_mono_display_region
00659 //       Access: Published
00660 //  Description: Creates a new DisplayRegion that covers the indicated
00661 //               sub-rectangle within the window.  The range on all
00662 //               parameters is 0..1.
00663 //
00664 //               This generally returns a mono DisplayRegion, even if
00665 //               is_stereo() is true.  However, if side-by-side stereo
00666 //               is enabled, this will return a StereoDisplayRegion
00667 //               whose two eyes are both set to SC_mono.  (This is
00668 //               necessary because in side-by-side stereo mode, it is
00669 //               necessary to draw even mono DisplayRegions twice).
00670 ////////////////////////////////////////////////////////////////////
00671 DisplayRegion *GraphicsOutput::
00672 make_mono_display_region(const LVecBase4 &dimensions) {
00673   if (_side_by_side_stereo) {
00674     StereoDisplayRegion *dr = make_stereo_display_region(dimensions);
00675     dr->get_left_eye()->set_stereo_channel(Lens::SC_mono);
00676     dr->get_right_eye()->set_stereo_channel(Lens::SC_mono);
00677     return dr;
00678   }
00679 
00680   return new DisplayRegion(this, dimensions);
00681 }
00682 
00683 ////////////////////////////////////////////////////////////////////
00684 //     Function: GraphicsOutput::make_stereo_display_region
00685 //       Access: Published
00686 //  Description: Creates a new DisplayRegion that covers the indicated
00687 //               sub-rectangle within the window.  The range on all
00688 //               parameters is 0..1.
00689 //
00690 //               This always returns a stereo DisplayRegion, even if
00691 //               is_stereo() is false.
00692 ////////////////////////////////////////////////////////////////////
00693 StereoDisplayRegion *GraphicsOutput::
00694 make_stereo_display_region(const LVecBase4 &dimensions) {
00695   PT(DisplayRegion) left, right;
00696 
00697   if (_side_by_side_stereo) {
00698     // On a side-by-side stereo window, each eye gets the
00699     // corresponding dimensions of its own sub-region.
00700     PN_stdfloat left_l = _sbs_left_dimensions[0];
00701     PN_stdfloat left_b = _sbs_left_dimensions[2];
00702     PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
00703     PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
00704     LVecBase4 left_dimensions(dimensions[0] * left_w + left_l,
00705                               dimensions[1] * left_w + left_l,
00706                               dimensions[2] * left_h + left_b,
00707                               dimensions[3] * left_h + left_b);
00708     left = new DisplayRegion(this, left_dimensions);
00709 
00710     PN_stdfloat right_l = _sbs_right_dimensions[0];
00711     PN_stdfloat right_b = _sbs_right_dimensions[2];
00712     PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
00713     PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
00714     LVecBase4 right_dimensions(dimensions[0] * right_w + right_l,
00715                                dimensions[1] * right_w + right_l,
00716                                dimensions[2] * right_h + right_b,
00717                                dimensions[3] * right_h + right_b);
00718     right = new DisplayRegion(this, right_dimensions);
00719 
00720     if (_swap_eyes) {
00721       DisplayRegion *t = left;
00722       left = right;
00723       right = t;
00724     }
00725 
00726   } else {
00727     // Not a side-by-side stereo window; thus, both the left and right
00728     // eyes are the same region: the region specified.
00729     left = new DisplayRegion(this, dimensions);
00730     right = new DisplayRegion(this, dimensions);
00731 
00732     // In this case, we assume that the two eyes will share the same
00733     // depth buffer, which means the right eye should clear the depth
00734     // buffer by default.
00735     if (get_clear_depth_active()) {
00736       right->set_clear_depth_active(true);
00737     }
00738     if (get_clear_stencil_active()) {
00739       right->set_clear_stencil_active(true);
00740     }
00741   }
00742 
00743   PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, dimensions,
00744                                                            left, right);
00745 
00746   return stereo;
00747 }
00748 
00749 ////////////////////////////////////////////////////////////////////
00750 //     Function: GraphicsOutput::remove_display_region
00751 //       Access: Published
00752 //  Description: Removes the indicated DisplayRegion from the window,
00753 //               and destructs it if there are no other references.
00754 //
00755 //               Returns true if the DisplayRegion is found and
00756 //               removed, false if it was not a part of the window.
00757 ////////////////////////////////////////////////////////////////////
00758 bool GraphicsOutput::
00759 remove_display_region(DisplayRegion *display_region) {
00760   LightMutexHolder holder(_lock);
00761 
00762   nassertr(display_region != _overlay_display_region, false);
00763 
00764   if (display_region->is_stereo()) {
00765     StereoDisplayRegion *sdr;
00766     DCAST_INTO_R(sdr, display_region, false);
00767     do_remove_display_region(sdr->get_left_eye());
00768     do_remove_display_region(sdr->get_right_eye());
00769   }
00770 
00771   return do_remove_display_region(display_region);
00772 }
00773 
00774 ////////////////////////////////////////////////////////////////////
00775 //     Function: GraphicsOutput::remove_all_display_regions
00776 //       Access: Published
00777 //  Description: Removes all display regions from the window, except
00778 //               the default one that is created with the window.
00779 ////////////////////////////////////////////////////////////////////
00780 void GraphicsOutput::
00781 remove_all_display_regions() {
00782   LightMutexHolder holder(_lock);
00783 
00784   CDWriter cdata(_cycler, true);
00785   cdata->_active_display_regions_stale = true;
00786 
00787   TotalDisplayRegions::iterator dri;
00788   for (dri = _total_display_regions.begin();
00789        dri != _total_display_regions.end();
00790        ++dri) {
00791     DisplayRegion *display_region = (*dri);
00792     if (display_region != _overlay_display_region) {
00793       // Let's aggressively clean up the display region too.
00794       display_region->cleanup();
00795       display_region->_window = NULL;
00796     }
00797   }
00798   _total_display_regions.clear();
00799   _total_display_regions.push_back(_overlay_display_region);
00800 }
00801 
00802 ////////////////////////////////////////////////////////////////////
00803 //     Function: GraphicsOutput::set_overlay_display_region
00804 //       Access: Published
00805 //  Description: Replaces the special "overlay" DisplayRegion that is
00806 //               created for each window or buffer.  See
00807 //               get_overlay_display_region().  This must be a new
00808 //               DisplayRegion that has already been created for this
00809 //               window, for instance via a call to
00810 //               make_mono_display_region().  You are responsible for
00811 //               ensuring that the new DisplayRegion covers the entire
00812 //               window.  The previous overlay display region is not
00813 //               automatically removed; you must explicitly call
00814 //               remove_display_region() on it after replacing it with
00815 //               this method, if you wish it to be removed.
00816 //
00817 //               Normally, there is no reason to change the overlay
00818 //               DisplayRegion, so this method should be used only
00819 //               in very unusual circumstances.
00820 ////////////////////////////////////////////////////////////////////
00821 void GraphicsOutput::
00822 set_overlay_display_region(DisplayRegion *display_region) {
00823   nassertv(display_region->get_window() == this);
00824   _overlay_display_region = display_region;
00825 }
00826 
00827 ////////////////////////////////////////////////////////////////////
00828 //     Function: GraphicsOutput::get_num_display_regions
00829 //       Access: Published
00830 //  Description: Returns the number of DisplayRegions that have
00831 //               been created within the window, active or otherwise.
00832 ////////////////////////////////////////////////////////////////////
00833 int GraphicsOutput::
00834 get_num_display_regions() const {
00835   determine_display_regions();
00836   int result;
00837   {
00838     LightMutexHolder holder(_lock);
00839     result = _total_display_regions.size();
00840   }
00841   return result;
00842 }
00843 
00844 ////////////////////////////////////////////////////////////////////
00845 //     Function: GraphicsOutput::get_display_region
00846 //       Access: Published
00847 //  Description: Returns the nth DisplayRegion of those that have been
00848 //               created within the window.  This may return NULL if n
00849 //               is out of bounds; particularly likely if the number
00850 //               of display regions has changed since the last call to
00851 //               get_num_display_regions().
00852 ////////////////////////////////////////////////////////////////////
00853 PT(DisplayRegion) GraphicsOutput::
00854 get_display_region(int n) const {
00855   determine_display_regions();
00856   PT(DisplayRegion) result;
00857   {
00858     LightMutexHolder holder(_lock);
00859     if (n >= 0 && n < (int)_total_display_regions.size()) {
00860       result = _total_display_regions[n];
00861     } else {
00862       result = NULL;
00863     }
00864   }
00865   return result;
00866 }
00867 
00868 ////////////////////////////////////////////////////////////////////
00869 //     Function: GraphicsOutput::get_num_active_display_regions
00870 //       Access: Published
00871 //  Description: Returns the number of active DisplayRegions that have
00872 //               been created within the window.
00873 ////////////////////////////////////////////////////////////////////
00874 int GraphicsOutput::
00875 get_num_active_display_regions() const {
00876   determine_display_regions();
00877   CDReader cdata(_cycler);
00878   return cdata->_active_display_regions.size();
00879 }
00880 
00881 ////////////////////////////////////////////////////////////////////
00882 //     Function: GraphicsOutput::get_active_display_region
00883 //       Access: Published
00884 //  Description: Returns the nth active DisplayRegion of those that
00885 //               have been created within the window.  This may return
00886 //               NULL if n is out of bounds; particularly likely if
00887 //               the number of display regions has changed since the
00888 //               last call to get_num_active_display_regions().
00889 ////////////////////////////////////////////////////////////////////
00890 PT(DisplayRegion) GraphicsOutput::
00891 get_active_display_region(int n) const {
00892   determine_display_regions();
00893 
00894   CDReader cdata(_cycler);
00895   if (n >= 0 && n < (int)cdata->_active_display_regions.size()) {
00896     return cdata->_active_display_regions[n];
00897   }
00898   return NULL;
00899 }
00900 
00901 ////////////////////////////////////////////////////////////////////
00902 //     Function: GraphicsOutput::make_texture_buffer
00903 //       Access: Published
00904 //  Description: Creates and returns an offscreen buffer for rendering
00905 //               into, the result of which will be a texture suitable
00906 //               for applying to geometry within the scene rendered
00907 //               into this window.
00908 //
00909 //               If tex is not NULL, it is the texture that will be
00910 //               set up for rendering into; otherwise, a new Texture
00911 //               object will be created.  In either case, the target
00912 //               texture can be retrieved from the return value with
00913 //               buffer->get_texture() (assuming the return value is
00914 //               not NULL).
00915 //
00916 //               If to_ram is true, the buffer will be set up to
00917 //               download its contents to the system RAM memory
00918 //               associated with the Texture object, instead of
00919 //               keeping it strictly within texture memory; this is
00920 //               much slower, but it allows using the texture with any
00921 //               GSG.
00922 //
00923 //               This will attempt to be smart about maximizing render
00924 //               performance while minimizing framebuffer waste.  It
00925 //               might return a GraphicsBuffer set to render directly
00926 //               into a texture, if possible; or it might return a
00927 //               ParasiteBuffer that renders into this window.  The
00928 //               return value is NULL if the buffer could not be
00929 //               created for some reason.
00930 //
00931 //               When you are done using the buffer, you should remove
00932 //               it with a call to GraphicsEngine::remove_window().
00933 ////////////////////////////////////////////////////////////////////
00934 GraphicsOutput *GraphicsOutput::
00935 make_texture_buffer(const string &name, int x_size, int y_size,
00936                     Texture *tex, bool to_ram, FrameBufferProperties *fbp) {
00937 
00938   FrameBufferProperties props;
00939   props.set_rgb_color(1);
00940   props.set_depth_bits(1);
00941 
00942   if (fbp == NULL) {
00943     fbp = &props;
00944   }
00945 
00946   int flags = GraphicsPipe::BF_refuse_window;
00947   if (textures_power_2 != ATS_none) {
00948     flags |= GraphicsPipe::BF_size_power_2;
00949   }
00950   if (tex != (Texture *)NULL &&
00951       tex->get_texture_type() == Texture::TT_cube_map) {
00952     flags |= GraphicsPipe::BF_size_square;
00953   }
00954 
00955   GraphicsOutput *buffer = get_gsg()->get_engine()->
00956     make_output(get_gsg()->get_pipe(),
00957                 name, get_child_sort(),
00958                 *fbp, WindowProperties::size(x_size, y_size),
00959                 flags, get_gsg(), get_host());
00960 
00961   if (buffer != (GraphicsOutput *)NULL) {
00962     if (buffer->get_gsg() == (GraphicsStateGuardian *)NULL || 
00963         buffer->get_gsg()->get_prepared_objects() != get_gsg()->get_prepared_objects()) {
00964       // If the newly-created buffer doesn't share texture objects
00965       // with the current GSG, then we will have to force the texture
00966       // copy to go through RAM.
00967       to_ram = true;
00968     }
00969 
00970     buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_bind_or_copy);
00971     return buffer;
00972   }
00973 
00974   return NULL;
00975 }
00976 
00977 ////////////////////////////////////////////////////////////////////
00978 //     Function: GraphicsOutput::make_cube_map
00979 //       Access: Published
00980 //  Description: This is similar to make_texture_buffer() in that it
00981 //               allocates a separate buffer suitable for rendering to
00982 //               a texture that can be assigned to geometry in this
00983 //               window, but in this case, the buffer is set up to
00984 //               render the six faces of a cube map.
00985 //
00986 //               The buffer is automatically set up with six display
00987 //               regions and six cameras, each of which are assigned
00988 //               the indicated draw_mask and parented to the given
00989 //               camera_rig node (which you should then put in your
00990 //               scene to render the cube map from the appropriate
00991 //               point of view).
00992 //
00993 //               You may take the texture associated with the buffer
00994 //               and apply it to geometry, particularly with
00995 //               TexGenAttrib::M_world_cube_map also in effect, to
00996 //               apply a reflection of everything seen by the camera
00997 //               rig.
00998 ////////////////////////////////////////////////////////////////////
00999 GraphicsOutput *GraphicsOutput::
01000 make_cube_map(const string &name, int size, NodePath &camera_rig,
01001               DrawMask camera_mask, bool to_ram, FrameBufferProperties *fbp) {
01002   if (!to_ram) {
01003     // Check the limits imposed by the GSG.  (However, if we're
01004     // rendering the texture to RAM only, these limits may be
01005     // irrelevant.)
01006     GraphicsStateGuardian *gsg = get_gsg();
01007     int max_dimension = gsg->get_max_cube_map_dimension();
01008     if (max_dimension == 0 || !gsg->get_supports_cube_map()) {
01009       // The GSG doesn't support cube mapping; too bad for you.
01010       display_cat.warning()
01011         << "Cannot make dynamic cube map; GSG does not support cube maps.\n";
01012       return NULL;
01013     }
01014     if (max_dimension > 0) {
01015       size = min(max_dimension, size);
01016     }
01017   }
01018 
01019   // Usually, we want the whole camera_rig to keep itself unrotated
01020   // with respect to the world coordinate space, so the user can apply
01021   // TexGenAttrib::M_world_cube_map to the objects on which the cube
01022   // map texture is applied.  If for some reason the user doesn't want
01023   // this behavior, he can take this effect off again.
01024   camera_rig.node()->set_effect(CompassEffect::make(NodePath()));
01025 
01026   PT(Texture) tex = new Texture(name);
01027   tex->setup_cube_map();
01028   tex->set_wrap_u(Texture::WM_clamp);
01029   tex->set_wrap_v(Texture::WM_clamp);
01030   GraphicsOutput *buffer;
01031 
01032   buffer = make_texture_buffer(name, size, size, tex, to_ram, fbp);
01033 
01034   // We don't need to clear the overall buffer; instead, we'll clear
01035   // each display region.
01036   buffer->set_clear_color_active(false);
01037   buffer->set_clear_depth_active(false);
01038   buffer->set_clear_stencil_active(false);
01039 
01040   PT(Lens) lens = new PerspectiveLens;
01041   lens->set_fov(90.0f);
01042 
01043   for (int i = 0; i < 6; i++) {
01044     PT(Camera) camera = new Camera(cube_faces[i]._name);
01045     camera->set_lens(lens);
01046     camera->set_camera_mask(camera_mask);
01047     NodePath camera_np = camera_rig.attach_new_node(camera);
01048     camera_np.look_at(cube_faces[i]._look_at, cube_faces[i]._up);
01049 
01050     DisplayRegion *dr;
01051     dr = buffer->make_display_region();
01052 
01053     dr->set_cube_map_index(i);
01054     dr->copy_clear_settings(*this);
01055     dr->set_camera(camera_np);
01056   }
01057 
01058   return buffer;
01059 }
01060 
01061 ////////////////////////////////////////////////////////////////////
01062 //     Function: GraphicsOutput::get_texture_card
01063 //       Access: Published
01064 //  Description: Returns a PandaNode containing a square polygon.
01065 //               The dimensions are (-1,0,-1) to (1,0,1). The texture
01066 //               coordinates are such that the texture of this
01067 //               GraphicsOutput is aligned properly to the polygon.
01068 //               The GraphicsOutput promises to surgically update
01069 //               the Geom inside the PandaNode if necessary to maintain
01070 //               this invariant.
01071 //
01072 //               Each invocation of this function returns a freshly-
01073 //               allocated PandaNode.  You can therefore safely modify
01074 //               the RenderAttribs of the PandaNode.  The
01075 //               PandaNode is initially textured with the texture
01076 //               of this GraphicOutput.
01077 ////////////////////////////////////////////////////////////////////
01078 NodePath GraphicsOutput::
01079 get_texture_card() {
01080   if (_texture_card == 0) {
01081     PT(GeomVertexData) vdata = create_texture_card_vdata(_x_size, _y_size);
01082     PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
01083     strip->set_shade_model(Geom::SM_uniform);
01084     strip->add_next_vertices(4);
01085     strip->close_primitive();
01086     _texture_card = new Geom(vdata);
01087     _texture_card->add_primitive(strip);
01088   }
01089 
01090   PT(GeomNode) gnode = new GeomNode("texture card");
01091   gnode->add_geom(_texture_card);
01092   NodePath path(gnode);
01093 
01094   // The texture card, by default, is textured with the first
01095   // render-to-texture output texture.  Depth and stencil
01096   // textures are ignored.  The user can freely alter the
01097   // card's texture attrib.
01098   CDReader cdata(_cycler);
01099   RenderTextures::const_iterator ri;
01100   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
01101     Texture *texture = (*ri)._texture;
01102     if ((texture->get_format() != Texture::F_depth_stencil)) {
01103       path.set_texture(texture, 0);
01104       break;
01105     }
01106   }
01107 
01108   return path;
01109 }
01110 
01111 ////////////////////////////////////////////////////////////////////
01112 //     Function: GraphicsOutput::share_depth_buffer
01113 //       Access: Published, Virtual
01114 //  Description: Will attempt to use the depth buffer of the input
01115 //               graphics_output. The buffer sizes must be exactly
01116 //               the same.
01117 ////////////////////////////////////////////////////////////////////
01118 bool GraphicsOutput::
01119 share_depth_buffer(GraphicsOutput *graphics_output) {
01120   return false;
01121 }
01122 
01123 ////////////////////////////////////////////////////////////////////
01124 //     Function: GraphicsOutput::unshare_depth_buffer
01125 //       Access: Published, Virtual
01126 //  Description: Discontinue sharing the depth buffer.
01127 ////////////////////////////////////////////////////////////////////
01128 void GraphicsOutput::
01129 unshare_depth_buffer() {
01130 }
01131 
01132 ////////////////////////////////////////////////////////////////////
01133 //     Function: GraphicsOutput::get_supports_render_texture
01134 //       Access: Published, Virtual
01135 //  Description: Returns true if this particular GraphicsOutput can
01136 //               render directly into a texture, or false if it must
01137 //               always copy-to-texture at the end of each frame to
01138 //               achieve this effect.
01139 ////////////////////////////////////////////////////////////////////
01140 bool GraphicsOutput::
01141 get_supports_render_texture() const {
01142   return false;
01143 }
01144 
01145 ////////////////////////////////////////////////////////////////////
01146 //     Function: GraphicsOutput::flip_ready
01147 //       Access: Public, Virtual
01148 //  Description: Returns true if a frame has been rendered and needs
01149 //               to be flipped, false otherwise.
01150 ////////////////////////////////////////////////////////////////////
01151 bool GraphicsOutput::
01152 flip_ready() const {
01153   return _flip_ready;
01154 }
01155 
01156 ////////////////////////////////////////////////////////////////////
01157 //     Function: GraphicsOutput::get_host
01158 //       Access: Public, Virtual
01159 //  Description: This is normally called only from within
01160 //               make_texture_buffer().  When called on a
01161 //               ParasiteBuffer, it returns the host of that buffer;
01162 //               but when called on some other buffer, it returns the
01163 //               buffer itself.
01164 ////////////////////////////////////////////////////////////////////
01165 GraphicsOutput *GraphicsOutput::
01166 get_host() {
01167   return this;
01168 }
01169 
01170 ////////////////////////////////////////////////////////////////////
01171 //     Function: GraphicsOutput::request_open
01172 //       Access: Public, Virtual
01173 //  Description: This is called by the GraphicsEngine to request that
01174 //               the window (or whatever) open itself or, in general,
01175 //               make itself valid, at the next call to
01176 //               process_events().
01177 ////////////////////////////////////////////////////////////////////
01178 void GraphicsOutput::
01179 request_open() {
01180 }
01181 
01182 ////////////////////////////////////////////////////////////////////
01183 //     Function: GraphicsOutput::request_close
01184 //       Access: Public, Virtual
01185 //  Description: This is called by the GraphicsEngine to request that
01186 //               the window (or whatever) close itself or, in general,
01187 //               make itself invalid, at the next call to
01188 //               process_events().  By that time we promise the gsg
01189 //               pointer will be cleared.
01190 ////////////////////////////////////////////////////////////////////
01191 void GraphicsOutput::
01192 request_close() {
01193 }
01194 
01195 ////////////////////////////////////////////////////////////////////
01196 //     Function: GraphicsOutput::set_close_now
01197 //       Access: Public, Virtual
01198 //  Description: This is called by the GraphicsEngine to insist that
01199 //               the output be closed immediately.  This is only
01200 //               called from the window thread.
01201 ////////////////////////////////////////////////////////////////////
01202 void GraphicsOutput::
01203 set_close_now() {
01204 }
01205 
01206 ////////////////////////////////////////////////////////////////////
01207 //     Function: GraphicsOutput::reset_window
01208 //       Access: Protected, Virtual
01209 //  Description: Resets the window framebuffer from its derived
01210 //               children. Does nothing here.
01211 ////////////////////////////////////////////////////////////////////
01212 void GraphicsOutput::
01213 reset_window(bool swapchain) {
01214   display_cat.info()
01215     << "Resetting " << get_type() << "\n";
01216 }
01217 
01218 ////////////////////////////////////////////////////////////////////
01219 //     Function: GraphicsOutput::clear_pipe
01220 //       Access: Protected, Virtual
01221 //  Description: Sets the window's _pipe pointer to NULL; this is
01222 //               generally called only as a precursor to deleting the
01223 //               window.
01224 ////////////////////////////////////////////////////////////////////
01225 void GraphicsOutput::
01226 clear_pipe() {
01227   _pipe = (GraphicsPipe *)NULL;
01228 }
01229 
01230 ////////////////////////////////////////////////////////////////////
01231 //     Function: GraphicsOutput::set_size_and_recalc
01232 //       Access: Public
01233 //  Description: Changes the x_size and y_size, then recalculates
01234 //               structures that depend on size.  The recalculation
01235 //               currently includes:
01236 //               - compute_pixels on all the graphics regions.
01237 //               - updating the texture card, if one is present.
01238 ////////////////////////////////////////////////////////////////////
01239 void GraphicsOutput::
01240 set_size_and_recalc(int x, int y) {
01241   _x_size = x;
01242   _y_size = y;
01243   _has_size = true;
01244 
01245   _is_nonzero_size = (_x_size > 0 && _y_size > 0);
01246 
01247   int fb_x_size = get_fb_x_size();
01248   int fb_y_size = get_fb_y_size();
01249 
01250   TotalDisplayRegions::iterator dri;
01251   for (dri = _total_display_regions.begin();
01252        dri != _total_display_regions.end();
01253        ++dri) {
01254     (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
01255   }
01256 
01257   if (_texture_card != 0) {
01258     _texture_card->set_vertex_data(create_texture_card_vdata(x, y));
01259   }
01260 }
01261 
01262 ////////////////////////////////////////////////////////////////////
01263 //     Function: GraphicsOutput::clear
01264 //       Access: Public
01265 //  Description: Clears the entire framebuffer before rendering,
01266 //               according to the settings of get_color_clear_active()
01267 //               and get_depth_clear_active() (inherited from
01268 //               DrawableRegion).
01269 //
01270 //               This function is called only within the draw thread.
01271 ////////////////////////////////////////////////////////////////////
01272 void GraphicsOutput::
01273 clear(Thread *current_thread) {
01274   if (is_any_clear_active()) {
01275     if (display_cat.is_spam()) {
01276       display_cat.spam()
01277         << "clear(): " << get_type() << " "
01278         << get_name() << " " << (void *)this << "\n";
01279     }
01280 
01281     nassertv(_gsg != (GraphicsStateGuardian *)NULL);
01282 
01283     DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread);
01284     _gsg->prepare_display_region(&dr_reader);
01285     _gsg->clear(this);
01286   }
01287 }
01288 
01289 ////////////////////////////////////////////////////////////////////
01290 //     Function: GraphicsOutput::begin_frame
01291 //       Access: Public, Virtual
01292 //  Description: This function will be called within the draw thread
01293 //               before beginning rendering for a given frame.  It
01294 //               should do whatever setup is required, and return true
01295 //               if the frame should be rendered, or false if it
01296 //               should be skipped.
01297 ////////////////////////////////////////////////////////////////////
01298 bool GraphicsOutput::
01299 begin_frame(FrameMode mode, Thread *current_thread) {
01300   return false;
01301 }
01302 
01303 ////////////////////////////////////////////////////////////////////
01304 //     Function: GraphicsOutput::end_frame
01305 //       Access: Public, Virtual
01306 //  Description: This function will be called within the draw thread
01307 //               after rendering is completed for a given frame.  It
01308 //               should do whatever finalization is required.
01309 ////////////////////////////////////////////////////////////////////
01310 void GraphicsOutput::
01311 end_frame(FrameMode mode, Thread *current_thread) {
01312 }
01313 
01314 ////////////////////////////////////////////////////////////////////
01315 //     Function: GraphicsOutput::change_scenes
01316 //       Access: Public
01317 //  Description: Called by the GraphicsEngine when the window is about
01318 //               to change to another DisplayRegion.  This exists
01319 //               mainly to provide a callback for switching the cube
01320 //               map face, if we are rendering to the different faces
01321 //               of a cube map.
01322 ////////////////////////////////////////////////////////////////////
01323 void GraphicsOutput::
01324 change_scenes(DisplayRegionPipelineReader *new_dr) {
01325   int new_cube_map_index = new_dr->get_cube_map_index();
01326   if (new_cube_map_index != -1 &&
01327       new_cube_map_index != _cube_map_index) {
01328     int old_cube_map_index = _cube_map_index;
01329     DisplayRegion *old_cube_map_dr = _cube_map_dr;
01330     _cube_map_index = new_cube_map_index;
01331     _cube_map_dr = new_dr->get_object();
01332 
01333     CDReader cdata(_cycler);
01334     RenderTextures::const_iterator ri;
01335     for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
01336       RenderTextureMode rtm_mode = (*ri)._rtm_mode;
01337       Texture *texture = (*ri)._texture;
01338       if (rtm_mode != RTM_none) {
01339         if (rtm_mode == RTM_bind_or_copy) {
01340           // In render-to-texture mode, switch the rendering backend to
01341           // the new cube map face, so that the subsequent frame will be
01342           // rendered to the new face.
01343 
01344           select_cube_map(new_cube_map_index);
01345 
01346         } else if (old_cube_map_index != -1) {
01347           // In copy-to-texture mode, copy the just-rendered framebuffer
01348           // to the old cube map face.
01349           nassertv(old_cube_map_dr != (DisplayRegion *)NULL);
01350           if (display_cat.is_debug()) {
01351             display_cat.debug()
01352               << "Copying texture for " << get_name() << " at scene change.\n";
01353             display_cat.debug()
01354               << "cube_map_index = " << old_cube_map_index << "\n";
01355           }
01356           RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
01357                                                         get_fb_properties());
01358           if (rtm_mode == RTM_copy_ram) {
01359             _gsg->framebuffer_copy_to_ram(texture, old_cube_map_index,
01360                                           old_cube_map_dr, buffer);
01361           } else {
01362             _gsg->framebuffer_copy_to_texture(texture, old_cube_map_index,
01363                                               old_cube_map_dr, buffer);
01364           }
01365         }
01366       }
01367     }
01368   }
01369 }
01370 
01371 ////////////////////////////////////////////////////////////////////
01372 //     Function: GraphicsOutput::select_cube_map
01373 //       Access: Public, Virtual
01374 //  Description: Called internally when the window is in
01375 //               render-to-a-texture mode and we are in the process of
01376 //               rendering the six faces of a cube map.  This should
01377 //               do whatever needs to be done to switch the buffer to
01378 //               the indicated face.
01379 ////////////////////////////////////////////////////////////////////
01380 void GraphicsOutput::
01381 select_cube_map(int) {
01382 }
01383 
01384 ////////////////////////////////////////////////////////////////////
01385 //     Function: GraphicsOutput::begin_flip
01386 //       Access: Public, Virtual
01387 //  Description: This function will be called within the draw thread
01388 //               after end_frame() has been called on all windows, to
01389 //               initiate the exchange of the front and back buffers.
01390 //
01391 //               This should instruct the window to prepare for the
01392 //               flip at the next video sync, but it should not wait.
01393 //
01394 //               We have the two separate functions, begin_flip() and
01395 //               end_flip(), to make it easier to flip all of the
01396 //               windows at the same time.
01397 ////////////////////////////////////////////////////////////////////
01398 void GraphicsOutput::
01399 begin_flip() {
01400 }
01401 
01402 ////////////////////////////////////////////////////////////////////
01403 //     Function: GraphicsOutput::ready_flip
01404 //       Access: Public, Virtual
01405 //  Description: This function will be called within the draw thread
01406 //               after end_frame() has been called on all windows, to
01407 //               initiate the exchange of the front and back buffers.
01408 //
01409 //               This should instruct the window to prepare for the
01410 //               flip when it is command but not actually flip
01411 //
01412 ////////////////////////////////////////////////////////////////////
01413 void GraphicsOutput::
01414 ready_flip() {
01415 }
01416 
01417 ////////////////////////////////////////////////////////////////////
01418 //     Function: GraphicsOutput::end_flip
01419 //       Access: Public, Virtual
01420 //  Description: This function will be called within the draw thread
01421 //               after begin_flip() has been called on all windows, to
01422 //               finish the exchange of the front and back buffers.
01423 //
01424 //               This should cause the window to wait for the flip, if
01425 //               necessary.
01426 ////////////////////////////////////////////////////////////////////
01427 void GraphicsOutput::
01428 end_flip() {
01429   _flip_ready = false;
01430 }
01431 
01432 ////////////////////////////////////////////////////////////////////
01433 //     Function: GraphicsOutput::process_events
01434 //       Access: Public, Virtual
01435 //  Description: Do whatever processing in the window thread is
01436 //               appropriate for this output object each frame.
01437 //
01438 //               This function is called only within the window
01439 //               thread.
01440 ////////////////////////////////////////////////////////////////////
01441 void GraphicsOutput::
01442 process_events() {
01443 }
01444 
01445 ////////////////////////////////////////////////////////////////////
01446 //     Function: GraphicsOutput::pixel_factor_changed
01447 //       Access: Published, Virtual
01448 //  Description: Called internally when the pixel factor changes.
01449 ////////////////////////////////////////////////////////////////////
01450 void GraphicsOutput::
01451 pixel_factor_changed() {
01452   if (_has_size) {
01453     set_size_and_recalc(_x_size, _y_size);
01454   }
01455 }
01456 
01457 ////////////////////////////////////////////////////////////////////
01458 //     Function: GraphicsOutput::prepare_for_deletion
01459 //       Access: Protected
01460 //  Description: Set the delete flag, and do the usual cleanup
01461 //               activities associated with that.
01462 ////////////////////////////////////////////////////////////////////
01463 void GraphicsOutput::
01464 prepare_for_deletion() {
01465   CDWriter cdata(_cycler, true);
01466   cdata->_active = false;
01467 
01468   // If we were rendering directly to texture, we can't delete the
01469   // buffer until all the textures are gone too.
01470   RenderTextures::iterator ri;
01471   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
01472     if ((*ri)._rtm_mode == RTM_bind_or_copy) {
01473       _hold_textures.push_back((*ri)._texture);
01474     }
01475   }
01476   cdata->_textures.clear();
01477 
01478   _delete_flag = true;
01479 
01480   // We have to be sure to remove all of the display regions
01481   // immediately, so that circular reference counts can be cleared
01482   // up (each display region keeps a pointer to a CullResult,
01483   // which can hold all sorts of pointers).
01484   remove_all_display_regions();
01485 }
01486 
01487 ////////////////////////////////////////////////////////////////////
01488 //     Function: GraphicsOutput::promote_to_copy_texture
01489 //       Access: Protected
01490 //  Description: If any textures are marked RTM_bind_or_copy, change
01491 //               them to RTM_copy_texture.
01492 ////////////////////////////////////////////////////////////////////
01493 void GraphicsOutput::
01494 promote_to_copy_texture() {
01495   CDLockedReader cdata(_cycler);
01496   RenderTextures::const_iterator ri;
01497 
01498   bool any_bind = false;
01499   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
01500     if ((*ri)._rtm_mode == RTM_bind_or_copy) {
01501       any_bind = true;
01502       break;
01503     }
01504   }
01505   if (any_bind) {
01506     CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
01507     RenderTextures::iterator ri;
01508     for (ri = cdataw->_textures.begin(); ri != cdataw->_textures.end(); ++ri) {
01509       if ((*ri)._rtm_mode == RTM_bind_or_copy) {
01510         (*ri)._rtm_mode = RTM_copy_texture;
01511       }
01512     }
01513   }
01514 }
01515 
01516 ////////////////////////////////////////////////////////////////////
01517 //     Function: GraphicsOutput::copy_to_textures
01518 //       Access: Protected
01519 //  Description: For all textures marked RTM_copy_texture,
01520 //               RTM_copy_ram, RTM_triggered_copy_texture, or
01521 //               RTM_triggered_copy_ram, do the necessary copies.
01522 //
01523 //               Returns true if all copies are successful, false
01524 //               otherwise.
01525 ////////////////////////////////////////////////////////////////////
01526 bool GraphicsOutput::
01527 copy_to_textures() {
01528   bool okflag = true;
01529 
01530   CDReader cdata(_cycler);
01531   RenderTextures::const_iterator ri;
01532   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
01533     RenderTextureMode rtm_mode = (*ri)._rtm_mode;
01534     if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
01535       continue;
01536     }
01537 
01538     Texture *texture = (*ri)._texture;
01539     PStatTimer timer(_copy_texture_pcollector);
01540 
01541     if ((rtm_mode == RTM_copy_texture)||
01542         (rtm_mode == RTM_copy_ram)||
01543         ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
01544         ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
01545       if (display_cat.is_debug()) {
01546         display_cat.debug()
01547           << "Copying texture for " << get_name() << " at frame end.\n";
01548         display_cat.debug()
01549           << "cube_map_index = " << _cube_map_index << "\n";
01550       }
01551       RenderTexturePlane plane = (*ri)._plane;
01552       RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(plane));
01553       if (plane == RTP_color) {
01554         buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
01555                                          get_fb_properties());
01556       }
01557 
01558       bool copied = false;
01559       if (_cube_map_dr != (DisplayRegion *)NULL) {
01560         if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
01561           copied =
01562             _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
01563                                           _cube_map_dr, buffer);
01564         } else {
01565           copied =
01566             _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
01567                                               _cube_map_dr, buffer);
01568         }
01569       } else {
01570         if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
01571           copied =
01572             _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
01573                                           _overlay_display_region, buffer);
01574         } else {
01575           copied =
01576             _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
01577                                               _overlay_display_region, buffer);
01578         }
01579       }
01580       if (!copied) {
01581         okflag = false;
01582       }
01583     }
01584   }
01585   _trigger_copy = false;
01586 
01587   return okflag;
01588 }
01589 
01590 ////////////////////////////////////////////////////////////////////
01591 //     Function: GraphicsOuput::create_texture_card_vdata
01592 //       Access: Private
01593 //  Description: Generates a GeomVertexData for a texture card.
01594 ////////////////////////////////////////////////////////////////////
01595 PT(GeomVertexData) GraphicsOutput::
01596 create_texture_card_vdata(int x, int y) {
01597   PN_stdfloat xhi = 1.0;
01598   PN_stdfloat yhi = 1.0;
01599 
01600   if (Texture::get_textures_power_2() != ATS_none) {
01601     int xru = Texture::up_to_power_2(x);
01602     int yru = Texture::up_to_power_2(y);
01603     xhi = (x * 1.0f) / xru;
01604     yhi = (y * 1.0f) / yru;
01605   }
01606 
01607   CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3n3t2();
01608 
01609   PT(GeomVertexData) vdata = new GeomVertexData
01610     ("card", format, Geom::UH_static);
01611 
01612   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
01613   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
01614   GeomVertexWriter normal(vdata, InternalName::get_normal());
01615 
01616   vertex.add_data3(LVertex::rfu(-1.0f, 0.0f,  1.0f));
01617   vertex.add_data3(LVertex::rfu(-1.0f, 0.0f, -1.0f));
01618   vertex.add_data3(LVertex::rfu( 1.0f, 0.0f,  1.0f));
01619   vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, -1.0f));
01620 
01621   texcoord.add_data2( 0.0f,  yhi);
01622   texcoord.add_data2( 0.0f, 0.0f);
01623   texcoord.add_data2(  xhi,  yhi);
01624   texcoord.add_data2(  xhi, 0.0f);
01625 
01626   normal.add_data3(LVector3::back());
01627   normal.add_data3(LVector3::back());
01628   normal.add_data3(LVector3::back());
01629   normal.add_data3(LVector3::back());
01630 
01631   return vdata;
01632 }
01633 
01634 ////////////////////////////////////////////////////////////////////
01635 //     Function: GraphicsOutput::add_display_region
01636 //       Access: Private
01637 //  Description: Called by the DisplayRegion constructor to
01638 //               add the new DisplayRegion to the list.
01639 ////////////////////////////////////////////////////////////////////
01640 DisplayRegion *GraphicsOutput::
01641 add_display_region(DisplayRegion *display_region) {
01642   LightMutexHolder holder(_lock);
01643   CDWriter cdata(_cycler, true);
01644   cdata->_active_display_regions_stale = true;
01645   
01646   _total_display_regions.push_back(display_region);
01647 
01648   return display_region;
01649 }
01650 
01651 ////////////////////////////////////////////////////////////////////
01652 //     Function: GraphicsOutput::do_remove_display_region
01653 //       Access: Private
01654 //  Description: Internal implementation of remove_display_region.
01655 //               Assumes the lock is already held.
01656 ////////////////////////////////////////////////////////////////////
01657 bool GraphicsOutput::
01658 do_remove_display_region(DisplayRegion *display_region) {
01659   nassertr(display_region != _overlay_display_region, false);
01660 
01661   PT(DisplayRegion) drp = display_region;
01662   TotalDisplayRegions::iterator dri =
01663     find(_total_display_regions.begin(), _total_display_regions.end(), drp);
01664   if (dri != _total_display_regions.end()) {
01665     // Let's aggressively clean up the display region too.
01666     CDWriter cdata(_cycler, true);
01667     display_region->cleanup();
01668     display_region->_window = NULL;
01669     _total_display_regions.erase(dri);
01670 
01671     cdata->_active_display_regions_stale = true;
01672 
01673     return true;
01674   }
01675 
01676   return false;
01677 }
01678 
01679 ////////////////////////////////////////////////////////////////////
01680 //     Function: GraphicsOutput::do_determine_display_regions
01681 //       Access: Private
01682 //  Description: Re-sorts the list of active DisplayRegions within
01683 //               the window.
01684 ////////////////////////////////////////////////////////////////////
01685 void GraphicsOutput::
01686 do_determine_display_regions(GraphicsOutput::CData *cdata) {
01687   cdata->_active_display_regions_stale = false;
01688 
01689   cdata->_active_display_regions.clear();
01690   cdata->_active_display_regions.reserve(_total_display_regions.size());
01691 
01692   int index = 0;
01693   TotalDisplayRegions::const_iterator dri;
01694   for (dri = _total_display_regions.begin();
01695        dri != _total_display_regions.end();
01696        ++dri) {
01697     DisplayRegion *display_region = (*dri);
01698     if (display_region->is_active()) {
01699       cdata->_active_display_regions.push_back(display_region);
01700       display_region->set_active_index(index);
01701       ++index;
01702     } else {
01703       display_region->set_active_index(-1);
01704     }
01705   }
01706 
01707   stable_sort(cdata->_active_display_regions.begin(), cdata->_active_display_regions.end(), IndirectLess<DisplayRegion>());
01708 }
01709 
01710 ////////////////////////////////////////////////////////////////////
01711 //     Function: GraphicsOutput::parse_color_mask
01712 //       Access: Private, Static
01713 //  Description: Parses one of the keywords in the
01714 //               red-blue-stereo-colors Config.prc variable, and
01715 //               returns the corresponding bitmask.
01716 //
01717 //               These bitmask values are taken from ColorWriteAttrib.
01718 ////////////////////////////////////////////////////////////////////
01719 unsigned int GraphicsOutput::
01720 parse_color_mask(const string &word) {
01721   unsigned int result = 0;
01722   vector_string components;
01723   tokenize(word, components, "|");
01724 
01725   vector_string::const_iterator ci;
01726   for (ci = components.begin(); ci != components.end(); ++ci) {
01727     string w = downcase(*ci);
01728     if (w == "red" || w == "r") {
01729       result |= 0x001;
01730 
01731     } else if (w == "green" || w == "g") {
01732       result |= 0x002;
01733 
01734     } else if (w == "blue" || w == "b") {
01735       result |= 0x004;
01736 
01737     } else if (w == "yellow" || w == "y") {
01738       result |= 0x003;
01739 
01740     } else if (w == "magenta" || w == "m") {
01741       result |= 0x005;
01742 
01743     } else if (w == "cyan" || w == "c") {
01744       result |= 0x006;
01745 
01746     } else if (w == "alpha" || w == "a") {
01747       result |= 0x008;
01748 
01749     } else if (w == "off") {
01750       
01751     } else {
01752       display_cat.warning()
01753         << "Invalid color in red-blue-stereo-colors: " << (*ci) << "\n";
01754     }
01755   }
01756 
01757   return result;
01758 }
01759 
01760 ////////////////////////////////////////////////////////////////////
01761 //     Function: GraphicsOutput::CData::Constructor
01762 //       Access: Public
01763 //  Description: 
01764 ////////////////////////////////////////////////////////////////////
01765 GraphicsOutput::CData::
01766 CData() {
01767   // The default is *not* active, so the entire pipeline stage is
01768   // initially populated with inactive outputs.  Pipeline stage 0 is
01769   // set to active in the constructor.
01770   _active = false;
01771   _one_shot_frame = -1;
01772   _active_display_regions_stale = false;
01773 }
01774 
01775 ////////////////////////////////////////////////////////////////////
01776 //     Function: GraphicsOutput::CData::Constructor
01777 //       Access: Public
01778 //  Description: 
01779 ////////////////////////////////////////////////////////////////////
01780 GraphicsOutput::CData::
01781 CData(const GraphicsOutput::CData &copy) :
01782   _textures(copy._textures),
01783   _active(copy._active),
01784   _one_shot_frame(copy._one_shot_frame),
01785   _active_display_regions(copy._active_display_regions),
01786   _active_display_regions_stale(copy._active_display_regions_stale)
01787 {
01788 }
01789 
01790 ////////////////////////////////////////////////////////////////////
01791 //     Function: GraphicsOutput::CData::make_copy
01792 //       Access: Public, Virtual
01793 //  Description:
01794 ////////////////////////////////////////////////////////////////////
01795 CycleData *GraphicsOutput::CData::
01796 make_copy() const {
01797   return new CData(*this);
01798 }
01799 
01800 ////////////////////////////////////////////////////////////////////
01801 //     Function: GraphicsOutput::FrameMode output operator
01802 //  Description:
01803 ////////////////////////////////////////////////////////////////////
01804 ostream &
01805 operator << (ostream &out, GraphicsOutput::FrameMode fm) {
01806   switch (fm) {
01807   case GraphicsOutput::FM_render:
01808     return out << "render";
01809   case GraphicsOutput::FM_parasite:
01810     return out << "parasite";
01811   case GraphicsOutput::FM_refresh:
01812     return out << "refresh";
01813   }
01814 
01815   return out << "(**invalid GraphicsOutput::FrameMode(" << (int)fm << ")**)";
01816 }
 All Classes Functions Variables Enumerations