Panda3D
graphicsOutput.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file graphicsOutput.cxx
10  * @author drose
11  * @date 2004-02-06
12  */
13 
14 #include "graphicsOutput.h"
15 #include "graphicsPipe.h"
16 #include "graphicsEngine.h"
17 #include "graphicsWindow.h"
18 #include "config_display.h"
19 #include "lightMutexHolder.h"
20 #include "renderBuffer.h"
21 #include "indirectLess.h"
22 #include "pStatTimer.h"
23 #include "configVariableBool.h"
24 #include "camera.h"
25 #include "displayRegion.h"
26 #include "lens.h"
27 #include "perspectiveLens.h"
28 #include "pointerTo.h"
29 #include "compassEffect.h"
30 #include "geom.h"
31 #include "geomNode.h"
32 #include "geomTristrips.h"
33 #include "geomVertexWriter.h"
34 #include "throw_event.h"
35 #include "config_gobj.h"
36 
37 using std::string;
38 
39 TypeHandle GraphicsOutput::_type_handle;
40 
41 PStatCollector GraphicsOutput::_make_current_pcollector("Draw:Make current");
42 PStatCollector GraphicsOutput::_copy_texture_pcollector("Draw:Copy texture");
43 PStatCollector GraphicsOutput::_cull_pcollector("Cull");
44 PStatCollector GraphicsOutput::_draw_pcollector("Draw");
45 
46 struct CubeFaceDef {
47  CubeFaceDef(const char *name, const LPoint3 &look_at, const LVector3 &up) :
48  _name(name), _look_at(look_at), _up(up) { }
49 
50  const char *_name;
51  LPoint3 _look_at;
52  LVector3 _up;
53 };
54 
55 static CubeFaceDef cube_faces[6] = {
56  CubeFaceDef("positive_x", LPoint3(1, 0, 0), LVector3(0, -1, 0)),
57  CubeFaceDef("negative_x", LPoint3(-1, 0, 0), LVector3(0, -1, 0)),
58  CubeFaceDef("positive_y", LPoint3(0, 1, 0), LVector3(0, 0, 1)),
59  CubeFaceDef("negative_y", LPoint3(0, -1, 0), LVector3(0, 0, -1)),
60  CubeFaceDef("positive_z", LPoint3(0, 0, 1), LVector3(0, -1, 0)),
61  CubeFaceDef("negative_z", LPoint3(0, 0, -1), LVector3(0, -1, 0))
62 };
63 
64 /**
65  * Normally, the GraphicsOutput constructor is not called directly; these are
66  * created instead via the GraphicsEngine::make_output() function.
67  */
68 GraphicsOutput::
69 GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
70  const string &name,
71  const FrameBufferProperties &fb_prop,
72  const WindowProperties &win_prop,
73  int flags,
75  GraphicsOutput *host,
76  bool default_stereo_flags) :
77  _lock("GraphicsOutput"),
78  _cull_window_pcollector(_cull_pcollector, name),
79  _draw_window_pcollector(_draw_pcollector, name),
80  _clear_window_pcollector(_draw_window_pcollector, "Clear"),
81  _size(0, 0)
82 {
83 #ifdef DO_MEMORY_USAGE
84  MemoryUsage::update_type(this, this);
85 #endif
86  _engine = engine;
87  _pipe = pipe;
88  _gsg = gsg;
89  _host = host;
90  _fb_properties = fb_prop;
91  _name = name;
92  _creation_flags = flags;
93  _has_size = win_prop.has_size();
94  _is_nonzero_size = false;
95  if (_has_size) {
96  _size = win_prop.get_size();
97  _is_nonzero_size = (_size[0] > 0 && _size[1] > 0);
98  }
99  if (_creation_flags & GraphicsPipe::BF_size_track_host) {
100  // If we're tracking the host size, we assume we'll be nonzero eventually.
101  _is_nonzero_size = true;
102  }
103 
104  _is_valid = false;
105  _flip_ready = false;
106  _target_tex_page = -1;
107  _prev_page_dr = nullptr;
108  _sort = 0;
109  _child_sort = 0;
110  _got_child_sort = false;
111  _internal_sort_index = 0;
112  _inverted = window_inverted;
113  _swap_eyes = swap_eyes;
114  _red_blue_stereo = false;
115  _left_eye_color_mask = 0x0f;
116  _right_eye_color_mask = 0x0f;
117  _side_by_side_stereo = false;
118  _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
119  _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
120  _delete_flag = false;
121 
122  if (_fb_properties.is_single_buffered()) {
123  _draw_buffer_type = RenderBuffer::T_front;
124  } else {
125  _draw_buffer_type = RenderBuffer::T_back;
126  }
127 
128  if (default_stereo_flags) {
129  // Check the config variables to see if we should make this a "stereo"
130  // buffer or window.
131  _red_blue_stereo = red_blue_stereo && !fb_prop.is_stereo();
132  if (_red_blue_stereo) {
133  _left_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(0));
134  _right_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(1));
135  }
136  _side_by_side_stereo = side_by_side_stereo && !fb_prop.is_stereo();
137  if (_side_by_side_stereo) {
138  _sbs_left_dimensions.set(sbs_left_dimensions[0], sbs_left_dimensions[1],
139  sbs_left_dimensions[2], sbs_left_dimensions[3]);
140  _sbs_right_dimensions.set(sbs_right_dimensions[0], sbs_right_dimensions[1],
141  sbs_right_dimensions[2], sbs_right_dimensions[3]);
142  }
143  }
144 
145  // We start out with one DisplayRegion that covers the whole window, which
146  // we may use internally for full-window operations like clear() and
147  // get_screenshot().
148  _overlay_display_region = make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
149  _overlay_display_region->set_active(false);
150  _overlay_display_region->set_scissor_enabled(false);
151 
152  // Make sure the "active" flag is set true for pipeline stage 0.
153  {
154  CDWriter cdata(_cycler, true);
155  cdata->_active = true;
156  }
157 
158  // By default, each new GraphicsOutput is set up to clear color and depth.
159  set_clear_color_active(true);
160  set_clear_depth_active(true);
161  set_clear_stencil_active(true);
162  set_clear_color(background_color.get_value());
163 }
164 
165 /**
166  *
167  */
168 GraphicsOutput::
169 ~GraphicsOutput() {
170  // The window should be closed by the time we destruct.
171  nassertv(!is_valid());
172 
173  // We shouldn't have a GraphicsPipe pointer anymore.
174  nassertv(_pipe == nullptr);
175 
176  // We don't have to destruct our child display regions explicitly, since
177  // they are all reference-counted and will go away when their pointers do.
178  // However, we do need to zero out their pointers to us.
179  TotalDisplayRegions::iterator dri;
180  for (dri = _total_display_regions.begin();
181  dri != _total_display_regions.end();
182  ++dri) {
183  (*dri)->_window = nullptr;
184  }
185 
186  _total_display_regions.clear();
187  _overlay_display_region = nullptr;
188 }
189 
190 /**
191  * If the GraphicsOutput is currently rendering to a texture, then all
192  * textures are dissociated from the GraphicsOuput.
193  */
196  CDWriter cdata(_cycler, true);
197  cdata->_textures.clear();
198  ++(cdata->_textures_seq);
199  throw_event("render-texture-targets-changed");
200 }
201 
202 /**
203  * Creates a new Texture object, suitable for rendering the contents of this
204  * buffer into, and appends it to the list of render textures.
205  *
206  * If tex is not NULL, it is the texture that will be set up for rendering
207  * into; otherwise, a new Texture object will be created, in which case you
208  * may call get_texture() to retrieve the new texture pointer.
209  *
210  * You can specify a bitplane to attach the texture to. the legal choices
211  * are:
212  *
213  * - RTP_depth
214  * - RTP_depth_stencil
215  * - RTP_color
216  * - RTP_aux_rgba_0
217  * - RTP_aux_rgba_1
218  * - RTP_aux_rgba_2
219  * - RTP_aux_rgba_3
220  *
221  * If you do not specify a bitplane to attach the texture to, this routine
222  * will use a default based on the texture's format:
223  *
224  * - F_depth_component attaches to RTP_depth
225  * - F_depth_stencil attaches to RTP_depth_stencil
226  * - all other formats attach to RTP_color.
227  *
228  * The texture's format will be changed to match the format of the bitplane to
229  * which it is attached. For example, if you pass in an F_rgba texture and
230  * order that it be attached to RTP_depth_stencil, it will turn into an
231  * F_depth_stencil texture.
232  *
233  * Also see make_texture_buffer(), which is a higher-level interface for
234  * preparing render-to-a-texture mode.
235  */
237 add_render_texture(Texture *tex, RenderTextureMode mode,
238  RenderTexturePlane plane) {
239 
240  if (mode == RTM_none) {
241  return;
242  }
243  LightMutexHolder holder(_lock);
244 
245  // Create texture if necessary.
246  if (tex == nullptr) {
247  tex = new Texture(get_name());
248  tex->set_wrap_u(SamplerState::WM_clamp);
249  tex->set_wrap_v(SamplerState::WM_clamp);
250  } else {
251  tex->clear_ram_image();
252  }
253 
254  // Set it to have no compression by default. You can restore compression
255  // later if you really, really want it; but this freaks out some drivers,
256  // and presumably it's a mistake if you have compression enabled for a
257  // rendered texture.
258  tex->set_compression(Texture::CM_off);
259 
260  // Choose a default bitplane.
261  if (plane == RTP_COUNT) {
262  switch (tex->get_format()) {
263  case Texture::F_depth_stencil:
264  plane = RTP_depth_stencil;
265  break;
266 
267  case Texture::F_depth_component:
268  case Texture::F_depth_component16:
269  case Texture::F_depth_component24:
270  case Texture::F_depth_component32:
271  plane = RTP_depth;
272  break;
273 
274  default:
275  plane = RTP_color;
276  break;
277  }
278  }
279 
280  // Set the texture's format to match the bitplane. (And validate the
281  // bitplane, while we're at it).
282 
283  if (plane == RTP_depth) {
284  _fb_properties.setup_depth_texture(tex);
285  tex->set_match_framebuffer_format(true);
286 
287  } else if (plane == RTP_depth_stencil) {
288  tex->set_format(Texture::F_depth_stencil);
289  if (_fb_properties.get_float_depth()) {
290  tex->set_component_type(Texture::T_float);
291  } else {
292  tex->set_component_type(Texture::T_unsigned_int_24_8);
293  }
294  tex->set_match_framebuffer_format(true);
295 
296  } else if (plane == RTP_color ||
297  plane == RTP_aux_rgba_0 ||
298  plane == RTP_aux_rgba_1 ||
299  plane == RTP_aux_rgba_2 ||
300  plane == RTP_aux_rgba_3) {
301  _fb_properties.setup_color_texture(tex);
302  tex->set_match_framebuffer_format(true);
303 
304  } else if (plane == RTP_aux_hrgba_0 ||
305  plane == RTP_aux_hrgba_1 ||
306  plane == RTP_aux_hrgba_2 ||
307  plane == RTP_aux_hrgba_3) {
308  tex->set_format(Texture::F_rgba16);
309  tex->set_match_framebuffer_format(true);
310 
311  } else if (plane == RTP_aux_float_0 ||
312  plane == RTP_aux_float_1 ||
313  plane == RTP_aux_float_2 ||
314  plane == RTP_aux_float_3) {
315  tex->set_format(Texture::F_rgba32);
316  tex->set_component_type(Texture::T_float);
317  tex->set_match_framebuffer_format(true);
318 
319  } else {
320  display_cat.error() <<
321  "add_render_texture: invalid bitplane specified.\n";
322  return;
323  }
324 
325  // Go ahead and tell the texture our anticipated size, even if it might be
326  // inaccurate (particularly if this is a GraphicsWindow, which has system-
327  // imposed restrictions on size).
328  tex->set_size_padded(get_x_size(), get_y_size(), tex->get_z_size());
329 
330  if (_fb_properties.is_stereo() && plane == RTP_color) {
331  if (tex->get_num_views() < 2) {
332  tex->set_num_views(2);
333  }
334  }
335 
336  if (!support_render_texture || !get_supports_render_texture()) {
337  // Binding is not supported or it is disabled, so just fall back to copy
338  // instead.
339  if (mode == RTM_bind_or_copy) {
340  mode = RTM_copy_texture;
341  } else if (mode == RTM_bind_layered) {
342  // We can't fallback to copy, because that doesn't work for layered
343  // textures. The best thing we can do is raise an error message.
344  display_cat.error() <<
345  "add_render_texture: RTM_bind_layered was requested but "
346  "render-to-texture is not supported or has been disabled!\n";
347  }
348  }
349 
350  if (mode == RTM_bind_layered && _gsg != nullptr && !_gsg->get_supports_geometry_shaders()) {
351  // Layered FBOs require a geometry shader to write to any but the first
352  // layer.
353  display_cat.warning() <<
354  "add_render_texture: RTM_bind_layered was requested but "
355  "geometry shaders are not supported!\n";
356  }
357 
358  if (mode == RTM_bind_or_copy || mode == RTM_bind_layered) {
359  // If we're still planning on binding, indicate it in texture properly.
360  tex->set_render_to_texture(true);
361  }
362 
363  CDWriter cdata(_cycler, true);
364  RenderTexture result;
365  result._texture = tex;
366  result._plane = plane;
367  result._rtm_mode = mode;
368  cdata->_textures.push_back(result);
369  ++(cdata->_textures_seq);
370 
371  throw_event("render-texture-targets-changed");
372 }
373 
374 /**
375  * This is a deprecated interface that made sense back when GraphicsOutputs
376  * could only render into one texture at a time. From now on, use
377  * clear_render_textures and add_render_texture instead.
378  *
379  * @deprecated Use add_render_texture() instead.
380  */
382 setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) {
383  display_cat.warning() <<
384  "Using deprecated setup_render_texture interface.\n";
386  if (to_ram) {
387  add_render_texture(tex, RTM_copy_ram);
388  } else if (allow_bind) {
389  add_render_texture(tex, RTM_bind_or_copy);
390  } else {
391  add_render_texture(tex, RTM_copy_texture);
392  }
393 }
394 
395 /**
396  * Sets the active flag associated with the GraphicsOutput. If the
397  * GraphicsOutput is marked inactive, nothing is rendered.
398  */
399 void GraphicsOutput::
400 set_active(bool active) {
401  CDLockedReader cdata(_cycler);
402  if (cdata->_active != active) {
403  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
404  cdataw->_active = active;
405  }
406 }
407 
408 /**
409  * Returns true if the window is ready to be rendered into, false otherwise.
410  */
411 bool GraphicsOutput::
412 is_active() const {
413  if (!is_valid()) {
414  return false;
415  }
416 
417  CDLockedReader cdata(_cycler);
418  if (!cdata->_active) {
419  return false;
420  }
421 
422  if (cdata->_one_shot_frame != -1) {
423  // If one_shot is in effect, then we are active only for the one indicated
424  // frame.
425  if (cdata->_one_shot_frame != ClockObject::get_global_clock()->get_frame_count()) {
426  return false;
427  } else {
428  return true;
429  }
430  }
431 
432  // If the window has a clear value set, it is active.
433  if (is_any_clear_active()) {
434  return true;
435  }
436 
437  // If we triggered a copy operation, it is also active.
438  if (_trigger_copy) {
439  return true;
440  }
441 
442  // The window is active if at least one display region is active.
443  if (cdata->_active_display_regions_stale) {
444  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, false);
445  ((GraphicsOutput *)this)->do_determine_display_regions(cdataw);
446  return !cdataw->_active_display_regions.empty();
447  } else {
448  return !cdata->_active_display_regions.empty();
449  }
450 }
451 
452 /**
453  * Changes the current setting of the one-shot flag. When this is true, the
454  * GraphicsOutput will render the current frame and then automatically set
455  * itself inactive. This is particularly useful for buffers that are created
456  * for the purposes of render-to-texture, for static textures that don't need
457  * to be continually re-rendered once they have been rendered the first time.
458  *
459  * Setting the buffer inactive is not the same thing as destroying it. You
460  * are still responsible for passing this buffer to
461  * GraphicsEngine::remove_window() when you no longer need the texture, in
462  * order to clean up fully. (However, you should not call remove_window() on
463  * this buffer while the texture is still needed, because depending on the
464  * render-to-texture mechanism in use, this may invalidate the texture
465  * contents.)
466  */
467 void GraphicsOutput::
468 set_one_shot(bool one_shot) {
469  CDWriter cdata(_cycler, true);
470  if (one_shot) {
471  cdata->_one_shot_frame = ClockObject::get_global_clock()->get_frame_count();
472  } else {
473  cdata->_one_shot_frame = -1;
474  }
475 }
476 
477 /**
478  * Returns the current setting of the one-shot flag. When this is true, the
479  * GraphicsOutput will automatically set itself inactive after the next frame.
480  */
481 bool GraphicsOutput::
482 get_one_shot() const {
483  CDReader cdata(_cycler);
484  return (cdata->_one_shot_frame == ClockObject::get_global_clock()->get_frame_count());
485 }
486 
487 /**
488  * Changes the current setting of the inverted flag. When this is true, the
489  * scene is rendered into the window upside-down and backwards, that is,
490  * inverted as if viewed through a mirror placed on the floor.
491  *
492  * This is primarily intended to support DirectX (and a few buggy OpenGL
493  * graphics drivers) that perform a framebuffer-to-texture copy upside-down
494  * from the usual OpenGL (and Panda) convention. Panda will automatically set
495  * this flag for offscreen buffers on hardware that is known to do this, to
496  * compensate when rendering offscreen into a texture.
497  */
498 void GraphicsOutput::
499 set_inverted(bool inverted) {
500  if (_inverted != inverted) {
501  _inverted = inverted;
502 
503  if (get_y_size() != 0) {
504  // All of our DisplayRegions need to recompute their pixel positions
505  // now.
506  TotalDisplayRegions::iterator dri;
507  for (dri = _total_display_regions.begin();
508  dri != _total_display_regions.end();
509  ++dri) {
510  (*dri)->compute_pixels(get_x_size(), get_y_size());
511  }
512  }
513  }
514 }
515 
516 /**
517  * Enables side-by-side stereo mode on this particular window. When side-by-
518  * side stereo mode is in effect, DisplayRegions that have the "left" channel
519  * set will render on the part of the window specified by sbs_left_dimensions
520  * (typically the left half: (0, 0.5, 0, 1)), while DisplayRegions that have
521  * the "right" channel set will render on the part of the window specified by
522  * sbs_right_dimensions (typically the right half: (0.5, 1, 0, 1)).
523  *
524  * This is commonly used in a dual-monitor mode, where a window is opened that
525  * spans two monitors, and each monitor represents a different eye.
526  */
528 set_side_by_side_stereo(bool side_by_side_stereo) {
529  LVecBase4 left, right;
530  left.set(sbs_left_dimensions[0], sbs_left_dimensions[1],
531  sbs_left_dimensions[2], sbs_left_dimensions[3]);
532  right.set(sbs_right_dimensions[0], sbs_right_dimensions[1],
533  sbs_right_dimensions[2], sbs_right_dimensions[3]);
534  set_side_by_side_stereo(side_by_side_stereo, left, right);
535 }
536 
537 /**
538  * Enables side-by-side stereo mode on this particular window. When side-by-
539  * side stereo mode is in effect, DisplayRegions that have the "left" channel
540  * set will render on the part of the window specified by sbs_left_dimensions
541  * (typically the left half: (0, 0.5, 0, 1)), while DisplayRegions that have
542  * the "right" channel set will render on the part of the window specified by
543  * sbs_right_dimensions (typically the right half: (0.5, 1, 0, 1)).
544  *
545  * This is commonly used in a dual-monitor mode, where a window is opened that
546  * spans two monitors, and each monitor represents a different eye.
547  */
549 set_side_by_side_stereo(bool side_by_side_stereo,
550  const LVecBase4 &sbs_left_dimensions,
551  const LVecBase4 &sbs_right_dimensions) {
552  _side_by_side_stereo = side_by_side_stereo;
553  if (_side_by_side_stereo) {
554  _sbs_left_dimensions = sbs_left_dimensions;
555  _sbs_right_dimensions = sbs_right_dimensions;
556  } else {
557  _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
558  _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
559  }
560 }
561 
562 /**
563  * Returns the current setting of the delete flag. When this is true, the
564  * GraphicsOutput will automatically be removed before the beginning of the
565  * next frame by the GraphicsEngine.
566  */
568 get_delete_flag() const {
569  // We only delete the window or buffer automatically when it is no longer
570  // associated with a texture.
571  for (int i = 0; i < (int)_hold_textures.size(); i++) {
572  if (_hold_textures[i].is_valid_pointer()) {
573  return false;
574  }
575  }
576 
577  return _delete_flag;
578 }
579 
580 /**
581  * Adjusts the sorting order of this particular GraphicsOutput, relative to
582  * other GraphicsOutputs.
583  */
584 void GraphicsOutput::
585 set_sort(int sort) {
586  if (_sort != sort) {
587  if (_gsg != nullptr &&
588  _gsg->get_engine() != nullptr) {
589  _gsg->get_engine()->set_window_sort(this, sort);
590  }
591  }
592 }
593 
594 /**
595  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
596  * the window. The range on all parameters is 0..1.
597  *
598  * If is_stereo() is true for this window, and default-stereo-camera is
599  * configured true, this actually makes a StereoDisplayRegion. Call
600  * make_mono_display_region() or make_stereo_display_region() if you want to
601  * insist on one or the other.
602  */
604 make_display_region(const LVecBase4 &dimensions) {
605  if (is_stereo() && default_stereo_camera) {
606  return make_stereo_display_region(dimensions);
607  } else {
608  return make_mono_display_region(dimensions);
609  }
610 }
611 
612 /**
613  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
614  * the window. The range on all parameters is 0..1.
615  *
616  * This generally returns a mono DisplayRegion, even if is_stereo() is true.
617  * However, if side-by-side stereo is enabled, this will return a
618  * StereoDisplayRegion whose two eyes are both set to SC_mono. (This is
619  * necessary because in side-by-side stereo mode, it is necessary to draw even
620  * mono DisplayRegions twice).
621  */
623 make_mono_display_region(const LVecBase4 &dimensions) {
624  if (_side_by_side_stereo) {
626  dr->get_left_eye()->set_stereo_channel(Lens::SC_mono);
627  dr->get_right_eye()->set_stereo_channel(Lens::SC_mono);
628  return dr;
629  }
630 
631  return new DisplayRegion(this, dimensions);
632 }
633 
634 /**
635  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
636  * the window. The range on all parameters is 0..1.
637  *
638  * This always returns a stereo DisplayRegion, even if is_stereo() is false.
639  */
641 make_stereo_display_region(const LVecBase4 &dimensions) {
642  PT(DisplayRegion) left, right;
643 
644  if (_side_by_side_stereo) {
645  // On a side-by-side stereo window, each eye gets the corresponding
646  // dimensions of its own sub-region.
647  PN_stdfloat left_l = _sbs_left_dimensions[0];
648  PN_stdfloat left_b = _sbs_left_dimensions[2];
649  PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
650  PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
651  LVecBase4 left_dimensions(dimensions[0] * left_w + left_l,
652  dimensions[1] * left_w + left_l,
653  dimensions[2] * left_h + left_b,
654  dimensions[3] * left_h + left_b);
655  left = new DisplayRegion(this, left_dimensions);
656 
657  PN_stdfloat right_l = _sbs_right_dimensions[0];
658  PN_stdfloat right_b = _sbs_right_dimensions[2];
659  PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
660  PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
661  LVecBase4 right_dimensions(dimensions[0] * right_w + right_l,
662  dimensions[1] * right_w + right_l,
663  dimensions[2] * right_h + right_b,
664  dimensions[3] * right_h + right_b);
665  right = new DisplayRegion(this, right_dimensions);
666 
667  if (_swap_eyes) {
668  DisplayRegion *t = left;
669  left = right;
670  right = t;
671  }
672 
673  } else {
674  // Not a side-by-side stereo window; thus, both the left and right eyes
675  // are the same region: the region specified.
676  left = new DisplayRegion(this, dimensions);
677  right = new DisplayRegion(this, dimensions);
678 
679  // In this case, we assume that the two eyes will share the same depth
680  // buffer, which means the right eye should clear the depth buffer by
681  // default.
682  if (get_clear_depth_active()) {
683  right->set_clear_depth_active(true);
684  }
685  if (get_clear_stencil_active()) {
686  right->set_clear_stencil_active(true);
687  }
688  }
689 
690  PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, dimensions,
691  left, right);
692 
693  return stereo;
694 }
695 
696 /**
697  * Removes the indicated DisplayRegion from the window, and destructs it if
698  * there are no other references.
699  *
700  * Returns true if the DisplayRegion is found and removed, false if it was not
701  * a part of the window.
702  */
704 remove_display_region(DisplayRegion *display_region) {
705  LightMutexHolder holder(_lock);
706 
707  nassertr(display_region != _overlay_display_region, false);
708 
709  if (display_region->is_stereo()) {
710  StereoDisplayRegion *sdr;
711  DCAST_INTO_R(sdr, display_region, false);
712  do_remove_display_region(sdr->get_left_eye());
713  do_remove_display_region(sdr->get_right_eye());
714  }
715 
716  return do_remove_display_region(display_region);
717 }
718 
719 /**
720  * Removes all display regions from the window, except the default one that is
721  * created with the window.
722  */
725  LightMutexHolder holder(_lock);
726 
727  TotalDisplayRegions::iterator dri;
728  for (dri = _total_display_regions.begin();
729  dri != _total_display_regions.end();
730  ++dri) {
731  DisplayRegion *display_region = (*dri);
732  if (display_region != _overlay_display_region) {
733  // Let's aggressively clean up the display region too.
734  display_region->cleanup();
735  display_region->_window = nullptr;
736  }
737  }
738  _total_display_regions.clear();
739  _total_display_regions.push_back(_overlay_display_region);
740 
741  OPEN_ITERATE_ALL_STAGES(_cycler) {
742  CDStageWriter cdata(_cycler, pipeline_stage);
743  cdata->_active_display_regions_stale = true;
744  }
745  CLOSE_ITERATE_ALL_STAGES(_cycler);
746 }
747 
748 /**
749  * Replaces the special "overlay" DisplayRegion that is created for each
750  * window or buffer. See get_overlay_display_region(). This must be a new
751  * DisplayRegion that has already been created for this window, for instance
752  * via a call to make_mono_display_region(). You are responsible for ensuring
753  * that the new DisplayRegion covers the entire window. The previous overlay
754  * display region is not automatically removed; you must explicitly call
755  * remove_display_region() on it after replacing it with this method, if you
756  * wish it to be removed.
757  *
758  * Normally, there is no reason to change the overlay DisplayRegion, so this
759  * method should be used only in very unusual circumstances.
760  */
762 set_overlay_display_region(DisplayRegion *display_region) {
763  nassertv(display_region->get_window() == this);
764  _overlay_display_region = display_region;
765 }
766 
767 /**
768  * Returns the number of DisplayRegions that have been created within the
769  * window, active or otherwise.
770  */
772 get_num_display_regions() const {
773  LightMutexHolder holder(_lock);
774  return _total_display_regions.size();
775 }
776 
777 /**
778  * Returns the nth DisplayRegion of those that have been created within the
779  * window. This may return NULL if n is out of bounds; particularly likely if
780  * the number of display regions has changed since the last call to
781  * get_num_display_regions().
782  */
783 PT(DisplayRegion) GraphicsOutput::
784 get_display_region(int n) const {
785  determine_display_regions();
786  PT(DisplayRegion) result;
787  {
788  LightMutexHolder holder(_lock);
789  if (n >= 0 && n < (int)_total_display_regions.size()) {
790  result = _total_display_regions[n];
791  } else {
792  result = nullptr;
793  }
794  }
795  return result;
796 }
797 
798 /**
799  * Returns the number of active DisplayRegions that have been created within
800  * the window.
801  */
802 int GraphicsOutput::
803 get_num_active_display_regions() const {
804  determine_display_regions();
805  CDReader cdata(_cycler);
806  return cdata->_active_display_regions.size();
807 }
808 
809 /**
810  * Returns the nth active DisplayRegion of those that have been created within
811  * the window. This may return NULL if n is out of bounds; particularly
812  * likely if the number of display regions has changed since the last call to
813  * get_num_active_display_regions().
814  */
815 PT(DisplayRegion) GraphicsOutput::
816 get_active_display_region(int n) const {
817  determine_display_regions();
818 
819  CDReader cdata(_cycler);
820  if (n >= 0 && n < (int)cdata->_active_display_regions.size()) {
821  return cdata->_active_display_regions[n];
822  }
823  return nullptr;
824 }
825 
826 /**
827  * Creates and returns an offscreen buffer for rendering into, the result of
828  * which will be a texture suitable for applying to geometry within the scene
829  * rendered into this window.
830  *
831  * If tex is not NULL, it is the texture that will be set up for rendering
832  * into; otherwise, a new Texture object will be created. In either case, the
833  * target texture can be retrieved from the return value with
834  * buffer->get_texture() (assuming the return value is not NULL).
835  *
836  * If to_ram is true, the buffer will be set up to download its contents to
837  * the system RAM memory associated with the Texture object, instead of
838  * keeping it strictly within texture memory; this is much slower, but it
839  * allows using the texture with any GSG.
840  *
841  * This will attempt to be smart about maximizing render performance while
842  * minimizing framebuffer waste. It might return a GraphicsBuffer set to
843  * render directly into a texture, if possible; or it might return a
844  * ParasiteBuffer that renders into this window. The return value is NULL if
845  * the buffer could not be created for some reason.
846  *
847  * When you are done using the buffer, you should remove it with a call to
848  * GraphicsEngine::remove_window().
849  */
850 GraphicsOutput *GraphicsOutput::
851 make_texture_buffer(const string &name, int x_size, int y_size,
852  Texture *tex, bool to_ram, FrameBufferProperties *fbp) {
853 
854  FrameBufferProperties props;
855  props.set_rgb_color(1);
856  props.set_color_bits(1);
857  props.set_alpha_bits(1);
858  props.set_depth_bits(1);
859 
860  if (fbp == nullptr) {
861  fbp = &props;
862  }
863 
864  int flags = GraphicsPipe::BF_refuse_window;
865  if (textures_power_2 != ATS_none) {
866  flags |= GraphicsPipe::BF_size_power_2;
867  }
868  if (tex != nullptr &&
869  tex->get_texture_type() == Texture::TT_cube_map) {
870  flags |= GraphicsPipe::BF_size_square;
871  }
872 
873  GraphicsOutput *buffer = get_gsg()->get_engine()->
874  make_output(get_gsg()->get_pipe(),
875  name, get_child_sort(),
876  *fbp, WindowProperties::size(x_size, y_size),
877  flags, get_gsg(), get_host());
878 
879  if (buffer != nullptr) {
880  if (buffer->get_gsg() == nullptr ||
881  buffer->get_gsg()->get_prepared_objects() != get_gsg()->get_prepared_objects()) {
882  // If the newly-created buffer doesn't share texture objects with the
883  // current GSG, then we will have to force the texture copy to go
884  // through RAM.
885  to_ram = true;
886  }
887 
888  buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_bind_or_copy);
889  return buffer;
890  }
891 
892  return nullptr;
893 }
894 
895 /**
896  * This is similar to make_texture_buffer() in that it allocates a separate
897  * buffer suitable for rendering to a texture that can be assigned to geometry
898  * in this window, but in this case, the buffer is set up to render the six
899  * faces of a cube map.
900  *
901  * The buffer is automatically set up with six display regions and six
902  * cameras, each of which are assigned the indicated draw_mask and parented to
903  * the given camera_rig node (which you should then put in your scene to
904  * render the cube map from the appropriate point of view).
905  *
906  * You may take the texture associated with the buffer and apply it to
907  * geometry, particularly with TexGenAttrib::M_world_cube_map also in effect,
908  * to apply a reflection of everything seen by the camera rig.
909  */
911 make_cube_map(const string &name, int size, NodePath &camera_rig,
912  DrawMask camera_mask, bool to_ram, FrameBufferProperties *fbp) {
913  if (!to_ram) {
914  // Check the limits imposed by the GSG. (However, if we're rendering the
915  // texture to RAM only, these limits may be irrelevant.)
917  int max_dimension = gsg->get_max_cube_map_dimension();
918  if (max_dimension == 0 || !gsg->get_supports_cube_map()) {
919  // The GSG doesn't support cube mapping; too bad for you.
920  display_cat.warning()
921  << "Cannot make dynamic cube map; GSG does not support cube maps.\n";
922  return nullptr;
923  }
924  if (max_dimension > 0) {
925  size = std::min(max_dimension, size);
926  }
927  }
928 
929  // Usually, we want the whole camera_rig to keep itself unrotated with
930  // respect to the world coordinate space, so the user can apply
931  // TexGenAttrib::M_world_cube_map to the objects on which the cube map
932  // texture is applied. If for some reason the user doesn't want this
933  // behavior, he can take this effect off again.
934  camera_rig.node()->set_effect(CompassEffect::make(NodePath()));
935 
936  PT(Texture) tex = new Texture(name);
937  tex->setup_cube_map();
938  tex->set_wrap_u(SamplerState::WM_clamp);
939  tex->set_wrap_v(SamplerState::WM_clamp);
940  GraphicsOutput *buffer;
941 
942  buffer = make_texture_buffer(name, size, size, tex, to_ram, fbp);
943 
944  // We don't need to clear the overall buffer; instead, we'll clear each
945  // display region.
946  buffer->set_clear_color_active(false);
947  buffer->set_clear_depth_active(false);
948  buffer->set_clear_stencil_active(false);
949 
950  PT(Lens) lens = new PerspectiveLens(90, 90);
951 
952  for (int i = 0; i < 6; i++) {
953  PT(Camera) camera = new Camera(cube_faces[i]._name);
954  camera->set_lens(lens);
955  camera->set_camera_mask(camera_mask);
956  NodePath camera_np = camera_rig.attach_new_node(camera);
957  camera_np.look_at(cube_faces[i]._look_at, cube_faces[i]._up);
958 
959  DisplayRegion *dr;
960  dr = buffer->make_display_region();
961 
962  dr->set_target_tex_page(i);
963  dr->copy_clear_settings(*this);
964  dr->set_camera(camera_np);
965  }
966 
967  return buffer;
968 }
969 
970 /**
971  * Returns a PandaNode containing a square polygon. The dimensions are
972  * (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of
973  * this GraphicsOutput is aligned properly to the polygon. The GraphicsOutput
974  * promises to surgically update the Geom inside the PandaNode if necessary to
975  * maintain this invariant.
976  *
977  * Each invocation of this function returns a freshly- allocated PandaNode.
978  * You can therefore safely modify the RenderAttribs of the PandaNode. The
979  * PandaNode is initially textured with the texture of this GraphicOutput.
980  */
983  if (_texture_card == nullptr) {
984  PT(GeomVertexData) vdata = create_texture_card_vdata(get_x_size(), get_y_size());
985  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
986  strip->set_shade_model(Geom::SM_uniform);
987  strip->add_next_vertices(4);
988  strip->close_primitive();
989  PT(Geom) geom = new Geom(vdata);
990  geom->add_primitive(strip);
991  _texture_card = new GeomNode("texture card");
992  _texture_card->add_geom(geom);
993  }
994 
995  NodePath path("texture card");
996  path.node()->add_child(_texture_card);
997 
998  // The texture card, by default, is textured with the first render-to-
999  // texture output texture. Depth and stencil textures are ignored. The
1000  // user can freely alter the card's texture attrib.
1001  CDReader cdata(_cycler);
1002  RenderTextures::const_iterator ri;
1003  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1004  Texture *texture = (*ri)._texture;
1005  if ((texture->get_format() != Texture::F_depth_stencil)) {
1006  path.set_texture(texture, 0);
1007  break;
1008  }
1009  }
1010 
1011  return path;
1012 }
1013 
1014 /**
1015  * Will attempt to use the depth buffer of the input graphics_output. The
1016  * buffer sizes must be exactly the same.
1017  */
1019 share_depth_buffer(GraphicsOutput *graphics_output) {
1020  return false;
1021 }
1022 
1023 /**
1024  * Discontinue sharing the depth buffer.
1025  */
1028 }
1029 
1030 /**
1031  * Returns true if this particular GraphicsOutput can render directly into a
1032  * texture, or false if it must always copy-to-texture at the end of each
1033  * frame to achieve this effect.
1034  */
1035 bool GraphicsOutput::
1037  return false;
1038 }
1039 
1040 /**
1041  * Returns true if a frame has been rendered and needs to be flipped, false
1042  * otherwise.
1043  */
1045 flip_ready() const {
1046  return _flip_ready;
1047 }
1048 
1049 /**
1050  * This is normally called only from within make_texture_buffer(). When
1051  * called on a ParasiteBuffer, it returns the host of that buffer; but when
1052  * called on some other buffer, it returns the buffer itself.
1053  */
1055 get_host() {
1056  return this;
1057 }
1058 
1059 /**
1060  * This is called by the GraphicsEngine to request that the window (or
1061  * whatever) open itself or, in general, make itself valid, at the next call
1062  * to process_events().
1063  */
1065 request_open() {
1066 }
1067 
1068 /**
1069  * This is called by the GraphicsEngine to request that the window (or
1070  * whatever) close itself or, in general, make itself invalid, at the next
1071  * call to process_events(). By that time we promise the gsg pointer will be
1072  * cleared.
1073  */
1075 request_close() {
1076 }
1077 
1078 /**
1079  * This is called by the GraphicsEngine to insist that the output be closed
1080  * immediately. This is only called from the window thread.
1081  */
1083 set_close_now() {
1084 }
1085 
1086 /**
1087  * Resets the window framebuffer from its derived children. Does nothing
1088  * here.
1089  */
1091 reset_window(bool swapchain) {
1092  display_cat.info()
1093  << "Resetting " << get_type() << "\n";
1094 }
1095 
1096 /**
1097  * Sets the window's _pipe pointer to NULL; this is generally called only as a
1098  * precursor to deleting the window.
1099  */
1101 clear_pipe() {
1102  _pipe = nullptr;
1103 }
1104 
1105 /**
1106  * Changes the x_size and y_size, then recalculates structures that depend on
1107  * size. The recalculation currently includes: - compute_pixels on all the
1108  * graphics regions. - updating the texture card, if one is present.
1109  */
1111 set_size_and_recalc(int x, int y) {
1112  _size.set(x, y);
1113  _has_size = true;
1114 
1115  _is_nonzero_size = (x > 0 && y > 0);
1116 
1117  int fb_x_size = get_fb_x_size();
1118  int fb_y_size = get_fb_y_size();
1119 
1120  TotalDisplayRegions::iterator dri;
1121  for (dri = _total_display_regions.begin();
1122  dri != _total_display_regions.end();
1123  ++dri) {
1124  (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
1125  }
1126 
1127  if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {
1128  _texture_card->modify_geom(0)->set_vertex_data(create_texture_card_vdata(x, y));
1129  }
1130 }
1131 
1132 /**
1133  * Clears the entire framebuffer before rendering, according to the settings
1134  * of get_color_clear_active() and get_depth_clear_active() (inherited from
1135  * DrawableRegion).
1136  *
1137  * This function is called only within the draw thread.
1138  */
1140 clear(Thread *current_thread) {
1141  if (is_any_clear_active()) {
1142  if (display_cat.is_spam()) {
1143  display_cat.spam()
1144  << "clear(): " << get_type() << " "
1145  << get_name() << " " << (void *)this << "\n";
1146  }
1147 
1148  nassertv(_gsg != nullptr);
1149 
1150  DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread);
1151  _gsg->prepare_display_region(&dr_reader);
1152  _gsg->clear(this);
1153  }
1154 }
1155 
1156 /**
1157  * This function will be called within the draw thread before beginning
1158  * rendering for a given frame. It should do whatever setup is required, and
1159  * return true if the frame should be rendered, or false if it should be
1160  * skipped.
1161  */
1163 begin_frame(FrameMode mode, Thread *current_thread) {
1164  return false;
1165 }
1166 
1167 /**
1168  * This function will be called within the draw thread after rendering is
1169  * completed for a given frame. It should do whatever finalization is
1170  * required.
1171  */
1173 end_frame(FrameMode mode, Thread *current_thread) {
1174 }
1175 
1176 /**
1177  * Called by the GraphicsEngine when the window is about to change to another
1178  * DisplayRegion. This exists mainly to provide a callback for switching the
1179  * cube map face, if we are rendering to the different faces of a cube map.
1180  */
1183  int new_target_tex_page = new_dr->get_target_tex_page();
1184 
1185  if (new_target_tex_page != -1 && new_target_tex_page != _target_tex_page) {
1186 
1187  if (new_target_tex_page == -1) {
1188  new_target_tex_page = 0;
1189  }
1190  int old_target_tex_page = _target_tex_page;
1191  DisplayRegion *old_page_dr = _prev_page_dr;
1192  _target_tex_page = new_target_tex_page;
1193  _prev_page_dr = new_dr->get_object();
1194 
1195  CDReader cdata(_cycler);
1196  RenderTextures::const_iterator ri;
1197  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1198  RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1199  RenderTexturePlane plane = (*ri)._plane;
1200  Texture *texture = (*ri)._texture;
1201  if (rtm_mode != RTM_none) {
1202  if (rtm_mode == RTM_bind_or_copy || rtm_mode == RTM_bind_layered) {
1203  // In render-to-texture mode, switch the rendering backend to the
1204  // new page, so that the subsequent frame will be rendered to the
1205  // correct page.
1206  select_target_tex_page(_target_tex_page);
1207 
1208  } else if (old_target_tex_page != -1) {
1209  // In copy-to-texture mode, copy the just-rendered framebuffer to
1210  // the old texture page.
1211 
1212  nassertv(old_page_dr != nullptr);
1213  if (display_cat.is_debug()) {
1214  display_cat.debug()
1215  << "Copying texture for " << get_name() << " at scene change.\n";
1216  display_cat.debug()
1217  << "target_tex_page = " << old_target_tex_page << "\n";
1218  }
1219  RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1220  get_fb_properties());
1221 
1222  if (plane == RTP_color && _fb_properties.is_stereo()) {
1223  // We've got two texture views to copy.
1224  RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1225  RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1226 
1227  if (rtm_mode == RTM_copy_ram) {
1228  _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1229  old_page_dr, left);
1230  _gsg->framebuffer_copy_to_ram(texture, 1, old_target_tex_page,
1231  old_page_dr, right);
1232  } else {
1233  _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1234  old_page_dr, left);
1235  _gsg->framebuffer_copy_to_texture(texture, 1, old_target_tex_page,
1236  old_page_dr, right);
1237  }
1238  } else {
1239  if (rtm_mode == RTM_copy_ram) {
1240  _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1241  old_page_dr, buffer);
1242  } else {
1243  _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1244  old_page_dr, buffer);
1245  }
1246  }
1247  }
1248  }
1249  }
1250  }
1251 }
1252 
1253 /**
1254  * Called internally when the window is in render-to-a-texture mode and we are
1255  * in the process of rendering the six faces of a cube map, or any other
1256  * multi-page texture. This should do whatever needs to be done to switch the
1257  * buffer to the indicated page.
1258  */
1261 }
1262 
1263 /**
1264  * This function will be called within the draw thread after end_frame() has
1265  * been called on all windows, to initiate the exchange of the front and back
1266  * buffers.
1267  *
1268  * This should instruct the window to prepare for the flip at the next video
1269  * sync, but it should not wait.
1270  *
1271  * We have the two separate functions, begin_flip() and end_flip(), to make it
1272  * easier to flip all of the windows at the same time.
1273  */
1275 begin_flip() {
1276 }
1277 
1278 /**
1279  * This function will be called within the draw thread after end_frame() has
1280  * been called on all windows, to initiate the exchange of the front and back
1281  * buffers.
1282  *
1283  * This should instruct the window to prepare for the flip when it is command
1284  * but not actually flip
1285  *
1286  */
1288 ready_flip() {
1289 }
1290 
1291 /**
1292  * This function will be called within the draw thread after begin_flip() has
1293  * been called on all windows, to finish the exchange of the front and back
1294  * buffers.
1295  *
1296  * This should cause the window to wait for the flip, if necessary.
1297  */
1299 end_flip() {
1300  _flip_ready = false;
1301 }
1302 
1303 /**
1304  * Do whatever processing in the window thread is appropriate for this output
1305  * object each frame.
1306  *
1307  * This function is called only within the window thread.
1308  */
1310 process_events() {
1311 }
1312 
1313 /**
1314  * Called internally when the pixel factor changes.
1315  */
1316 void GraphicsOutput::
1317 pixel_factor_changed() {
1318  if (_has_size) {
1320  }
1321 }
1322 
1323 /**
1324  * Set the delete flag, and do the usual cleanup activities associated with
1325  * that.
1326  */
1327 void GraphicsOutput::
1328 prepare_for_deletion() {
1329  CDWriter cdata(_cycler, true);
1330  cdata->_active = false;
1331 
1332  // If we were rendering directly to texture, we can't delete the buffer
1333  // until all the textures are gone too.
1334  RenderTextures::iterator ri;
1335  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1336  if ((*ri)._rtm_mode == RTM_bind_or_copy || (*ri)._rtm_mode == RTM_bind_layered) {
1337  _hold_textures.push_back((*ri)._texture);
1338  }
1339  }
1340  cdata->_textures.clear();
1341 
1342  _delete_flag = true;
1343 
1344  // We have to be sure to remove all of the display regions immediately, so
1345  // that circular reference counts can be cleared up (each display region
1346  // keeps a pointer to a CullResult, which can hold all sorts of pointers).
1348 }
1349 
1350 /**
1351  * If any textures are marked RTM_bind_or_copy, change them to
1352  * RTM_copy_texture. This does not change textures that are set to
1353  * RTM_bind_layered, as layered framebuffers aren't supported with
1354  * RTM_copy_texture.
1355  */
1356 void GraphicsOutput::
1357 promote_to_copy_texture() {
1358  CDLockedReader cdata(_cycler);
1359  RenderTextures::const_iterator ri;
1360 
1361  bool any_bind = false;
1362  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1363  if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1364  any_bind = true;
1365  break;
1366  }
1367  }
1368  if (any_bind) {
1369  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
1370  RenderTextures::iterator ri;
1371  for (ri = cdataw->_textures.begin(); ri != cdataw->_textures.end(); ++ri) {
1372  if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1373  (*ri)._rtm_mode = RTM_copy_texture;
1374  }
1375  }
1376  }
1377 }
1378 
1379 /**
1380  * For all textures marked RTM_copy_texture, RTM_copy_ram,
1381  * RTM_triggered_copy_texture, or RTM_triggered_copy_ram, do the necessary
1382  * copies.
1383  *
1384  * Returns true if all copies are successful, false otherwise.
1385  */
1386 bool GraphicsOutput::
1387 copy_to_textures() {
1388  bool okflag = true;
1389 
1390  CDReader cdata(_cycler);
1391  RenderTextures::const_iterator ri;
1392  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1393  RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1394  if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
1395  continue;
1396  }
1397 
1398  Texture *texture = (*ri)._texture;
1399  PStatTimer timer(_copy_texture_pcollector);
1400 
1401  if ((rtm_mode == RTM_copy_texture)||
1402  (rtm_mode == RTM_copy_ram)||
1403  ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
1404  ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
1405  if (display_cat.is_debug()) {
1406  display_cat.debug()
1407  << "Copying texture for " << get_name() << " at frame end.\n";
1408  display_cat.debug()
1409  << "target_tex_page = " << _target_tex_page << "\n";
1410  }
1411  RenderTexturePlane plane = (*ri)._plane;
1413  if (plane == RTP_color) {
1414  buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1415  get_fb_properties());
1416  }
1417 
1418  bool copied = false;
1419  DisplayRegion *dr = _overlay_display_region;
1420  if (_prev_page_dr != nullptr) {
1421  dr = _prev_page_dr;
1422  }
1423 
1424  if (plane == RTP_color && _fb_properties.is_stereo()) {
1425  // We've got two texture views to copy.
1426  RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1427  RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1428 
1429  if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1430  copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1431  dr, left);
1432  copied = _gsg->framebuffer_copy_to_ram(texture, 1, _target_tex_page,
1433  dr, right) && copied;
1434  } else {
1435  copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1436  dr, left);
1437  copied = _gsg->framebuffer_copy_to_texture(texture, 1, _target_tex_page,
1438  dr, right) && copied;
1439  }
1440  } else {
1441  if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1442  copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1443  dr, buffer);
1444  } else {
1445  copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1446  dr, buffer);
1447  }
1448  }
1449  if (!copied) {
1450  okflag = false;
1451  }
1452  }
1453  }
1454  if (_trigger_copy != nullptr) {
1455  _trigger_copy->set_result(nullptr);
1456  _trigger_copy = nullptr;
1457  }
1458 
1459  return okflag;
1460 }
1461 
1462 /**
1463  * Generates a GeomVertexData for a texture card.
1464  */
1465 PT(GeomVertexData) GraphicsOutput::
1466 create_texture_card_vdata(int x, int y) {
1467  PN_stdfloat xhi = 1.0;
1468  PN_stdfloat yhi = 1.0;
1469 
1470  if (Texture::get_textures_power_2() != ATS_none) {
1471  int xru = Texture::up_to_power_2(x);
1472  int yru = Texture::up_to_power_2(y);
1473  xhi = (x * 1.0f) / xru;
1474  yhi = (y * 1.0f) / yru;
1475  }
1476 
1478 
1479  PT(GeomVertexData) vdata = new GeomVertexData
1480  ("card", format, Geom::UH_static);
1481 
1482  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
1483  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
1484  GeomVertexWriter normal(vdata, InternalName::get_normal());
1485 
1486  vertex.add_data3(LVertex::rfu(-1.0f, 0.0f, 1.0f));
1487  vertex.add_data3(LVertex::rfu(-1.0f, 0.0f, -1.0f));
1488  vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, 1.0f));
1489  vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, -1.0f));
1490 
1491  texcoord.add_data2( 0.0f, yhi);
1492  texcoord.add_data2( 0.0f, 0.0f);
1493  texcoord.add_data2( xhi, yhi);
1494  texcoord.add_data2( xhi, 0.0f);
1495 
1496  normal.add_data3(LVector3::back());
1497  normal.add_data3(LVector3::back());
1498  normal.add_data3(LVector3::back());
1499  normal.add_data3(LVector3::back());
1500 
1501  return vdata;
1502 }
1503 
1504 /**
1505  * Called by the DisplayRegion constructor to add the new DisplayRegion to the
1506  * list.
1507  */
1508 DisplayRegion *GraphicsOutput::
1509 add_display_region(DisplayRegion *display_region) {
1510  LightMutexHolder holder(_lock);
1511  CDWriter cdata(_cycler, true);
1512  cdata->_active_display_regions_stale = true;
1513 
1514  _total_display_regions.push_back(display_region);
1515 
1516  return display_region;
1517 }
1518 
1519 /**
1520  * Internal implementation of remove_display_region. Assumes the lock is
1521  * already held.
1522  */
1523 bool GraphicsOutput::
1524 do_remove_display_region(DisplayRegion *display_region) {
1525  nassertr(display_region != _overlay_display_region, false);
1526 
1527  PT(DisplayRegion) drp = display_region;
1528  TotalDisplayRegions::iterator dri =
1529  find(_total_display_regions.begin(), _total_display_regions.end(), drp);
1530  if (dri != _total_display_regions.end()) {
1531  // Let's aggressively clean up the display region too.
1532  display_region->cleanup();
1533  display_region->_window = nullptr;
1534  _total_display_regions.erase(dri);
1535 
1536  OPEN_ITERATE_ALL_STAGES(_cycler) {
1537  CDStageWriter cdata(_cycler, pipeline_stage);
1538  cdata->_active_display_regions_stale = true;
1539  }
1540  CLOSE_ITERATE_ALL_STAGES(_cycler);
1541  return true;
1542  }
1543 
1544  return false;
1545 }
1546 
1547 /**
1548  * Re-sorts the list of active DisplayRegions within the window.
1549  */
1550 void GraphicsOutput::
1551 do_determine_display_regions(GraphicsOutput::CData *cdata) {
1552  cdata->_active_display_regions_stale = false;
1553 
1554  cdata->_active_display_regions.clear();
1555  cdata->_active_display_regions.reserve(_total_display_regions.size());
1556 
1557  int index = 0;
1558  TotalDisplayRegions::const_iterator dri;
1559  for (dri = _total_display_regions.begin();
1560  dri != _total_display_regions.end();
1561  ++dri) {
1562  DisplayRegion *display_region = (*dri);
1563  if (display_region->is_active()) {
1564  cdata->_active_display_regions.push_back(display_region);
1565  display_region->set_active_index(index);
1566  ++index;
1567  } else {
1568  display_region->set_active_index(-1);
1569  }
1570  }
1571 
1572  std::stable_sort(cdata->_active_display_regions.begin(),
1573  cdata->_active_display_regions.end(),
1575 }
1576 
1577 /**
1578  * Parses one of the keywords in the red-blue-stereo-colors Config.prc
1579  * variable, and returns the corresponding bitmask.
1580  *
1581  * These bitmask values are taken from ColorWriteAttrib.
1582  */
1583 unsigned int GraphicsOutput::
1584 parse_color_mask(const string &word) {
1585  unsigned int result = 0;
1586  vector_string components;
1587  tokenize(word, components, "|");
1588 
1589  vector_string::const_iterator ci;
1590  for (ci = components.begin(); ci != components.end(); ++ci) {
1591  string w = downcase(*ci);
1592  if (w == "red" || w == "r") {
1593  result |= 0x001;
1594 
1595  } else if (w == "green" || w == "g") {
1596  result |= 0x002;
1597 
1598  } else if (w == "blue" || w == "b") {
1599  result |= 0x004;
1600 
1601  } else if (w == "yellow" || w == "y") {
1602  result |= 0x003;
1603 
1604  } else if (w == "magenta" || w == "m") {
1605  result |= 0x005;
1606 
1607  } else if (w == "cyan" || w == "c") {
1608  result |= 0x006;
1609 
1610  } else if (w == "alpha" || w == "a") {
1611  result |= 0x008;
1612 
1613  } else if (w == "off") {
1614 
1615  } else {
1616  display_cat.warning()
1617  << "Invalid color in red-blue-stereo-colors: " << (*ci) << "\n";
1618  }
1619  }
1620 
1621  return result;
1622 }
1623 
1624 /**
1625  *
1626  */
1627 GraphicsOutput::CData::
1628 CData() {
1629  // The default is *not* active, so the entire pipeline stage is initially
1630  // populated with inactive outputs. Pipeline stage 0 is set to active in
1631  // the constructor.
1632  _active = false;
1633  _one_shot_frame = -1;
1634  _active_display_regions_stale = false;
1635 }
1636 
1637 /**
1638  *
1639  */
1640 GraphicsOutput::CData::
1641 CData(const GraphicsOutput::CData &copy) :
1642  _textures(copy._textures),
1643  _active(copy._active),
1644  _one_shot_frame(copy._one_shot_frame),
1645  _active_display_regions(copy._active_display_regions),
1646  _active_display_regions_stale(copy._active_display_regions_stale)
1647 {
1648 }
1649 
1650 /**
1651  *
1652  */
1653 CycleData *GraphicsOutput::CData::
1654 make_copy() const {
1655  return new CData(*this);
1656 }
1657 
1658 /**
1659  *
1660  */
1661 std::ostream &
1662 operator << (std::ostream &out, GraphicsOutput::FrameMode fm) {
1663  switch (fm) {
1664  case GraphicsOutput::FM_render:
1665  return out << "render";
1666  case GraphicsOutput::FM_parasite:
1667  return out << "parasite";
1668  case GraphicsOutput::FM_refresh:
1669  return out << "refresh";
1670  }
1671 
1672  return out << "(**invalid GraphicsOutput::FrameMode(" << (int)fm << ")**)";
1673 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
Definition: clockObject.h:94
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
const LColor & get_value() const
Returns the variable's value.
std::string get_word(size_t n) const
Returns the variable's nth value.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
int get_target_tex_page() const
Returns the target page number associated with this particular DisplayRegion, or -1 if it is not asso...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
is_stereo
Returns true if this is a StereoDisplayRegion, false otherwise.
Definition: displayRegion.h:90
set_camera
Sets the camera that is associated with this DisplayRegion.
Definition: displayRegion.h:94
set_target_tex_page
This is a special parameter that is only used when rendering the faces of a cube map or multipage and...
is_active
Returns the active flag associated with the DisplayRegion.
Definition: displayRegion.h:98
get_window
Returns the GraphicsOutput that this DisplayRegion is ultimately associated with, or NULL if no windo...
Definition: displayRegion.h:88
void cleanup()
Cleans up some pointers associated with the DisplayRegion to help reduce the chance of memory leaks d...
int get_draw_buffer_type() const
Returns the RenderBuffer into which the GSG should issue draw commands.
void set_clear_stencil_active(bool clear_stencil_active)
Toggles the flag that indicates whether the stencil buffer should be cleared every frame.
void set_clear_color_active(bool clear_color_active)
Toggles the flag that indicates whether the color buffer should be cleared every frame.
static int get_renderbuffer_type(int plane)
Returns the RenderBuffer::Type that corresponds to a RenderTexturePlane.
bool get_clear_depth_active() const
Returns the current setting of the flag that indicates whether the depth buffer should be cleared eve...
virtual bool is_any_clear_active() const
Returns true if any of the clear types (so far there are just color or depth) have been set active,...
void copy_clear_settings(const DrawableRegion &copy)
Copies only the clear settings from the other drawable region.
bool get_clear_stencil_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
void set_clear_depth_active(bool clear_depth_active)
Toggles the flag that indicates whether the depth buffer should be cleared every frame.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
bool setup_color_texture(Texture *tex) const
Sets the texture up for render-to-texture matching these framebuffer properties.
bool setup_depth_texture(Texture *tex) const
Sets the texture up for render-to-texture matching these framebuffer properties.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
Defines a series of triangle strips.
Definition: geomTristrips.h:23
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
static const GeomVertexFormat * get_v3n3t2()
Returns a standard vertex format with a 2-component texture coordinate pair, a 3-component normal,...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row.
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row.
A container for geometry primitives.
Definition: geom.h:54
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
virtual void ready_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
virtual void process_events()
Do whatever processing in the window thread is appropriate for this output object each frame.
void clear_render_textures()
If the GraphicsOutput is currently rendering to a texture, then all textures are dissociated from the...
int get_fb_x_size() const
Returns the internal width of the window or buffer.
set_inverted
Changes the current setting of the inverted flag.
get_pipe
Returns the GraphicsPipe that this window is associated with.
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
void add_render_texture(Texture *tex, RenderTextureMode mode, RenderTexturePlane bitplane=RTP_COUNT)
Creates a new Texture object, suitable for rendering the contents of this buffer into,...
set_active
Sets the active flag associated with the GraphicsOutput.
void set_side_by_side_stereo(bool side_by_side_stereo)
Enables side-by-side stereo mode on this particular window.
is_active
Returns true if the window is ready to be rendered into, false otherwise.
get_gsg
Returns the GSG that is associated with this window.
virtual void clear_pipe()
Sets the window's _pipe pointer to NULL; this is generally called only as a precursor to deleting the...
void remove_all_display_regions()
Removes all display regions from the window, except the default one that is created with the window.
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
virtual bool share_depth_buffer(GraphicsOutput *graphics_output)
Will attempt to use the depth buffer of the input graphics_output.
set_one_shot
Changes the current setting of the one-shot flag.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
bool get_delete_flag() const
Returns the current setting of the delete flag.
void change_scenes(DisplayRegionPipelineReader *new_dr)
Called by the GraphicsEngine when the window is about to change to another DisplayRegion.
void setup_render_texture(Texture *tex, bool allow_bind, bool to_ram)
This is a deprecated interface that made sense back when GraphicsOutputs could only render into one t...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
StereoDisplayRegion * make_stereo_display_region()
Creates a new DisplayRegion that covers the entire window.
DisplayRegion * make_mono_display_region()
Creates a new DisplayRegion that covers the entire window.
virtual void unshare_depth_buffer()
Discontinue sharing the depth buffer.
get_supports_render_texture
Returns true if this particular GraphicsOutput can render directly into a texture,...
NodePath get_texture_card()
Returns a PandaNode containing a square polygon.
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
get_num_display_regions
Returns the number of DisplayRegions that have been created within the window, active or otherwise.
get_child_sort
Returns the sort value of future offscreen buffers created by make_texture_sort().
get_one_shot
Returns the current setting of the one-shot flag.
int get_fb_y_size() const
Returns the internal height of the window or buffer.
DisplayRegion * make_display_region()
Creates a new DisplayRegion that covers the entire window.
virtual void reset_window(bool swapchain)
Resets the window framebuffer from its derived children.
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
void set_size_and_recalc(int x, int y)
Changes the x_size and y_size, then recalculates structures that depend on size.
bool remove_display_region(DisplayRegion *display_region)
Removes the indicated DisplayRegion from the window, and destructs it if there are no other reference...
bool is_valid() const
Returns true if the output is fully created and ready for rendering, false otherwise.
virtual void request_close()
This is called by the GraphicsEngine to request that the window (or whatever) close itself or,...
int get_y_size() const
Returns the visible height of the window or buffer, if it is known.
get_name
Returns the name that was passed to the GraphicsOutput constructor.
void set_overlay_display_region(DisplayRegion *display_region)
Replaces the special "overlay" DisplayRegion that is created for each window or buffer.
virtual void select_target_tex_page(int page)
Called internally when the window is in render-to-a-texture mode and we are in the process of renderi...
GraphicsOutput * make_cube_map(const std::string &name, int size, NodePath &camera_rig, DrawMask camera_mask=PandaNode::get_all_camera_mask(), bool to_ram=false, FrameBufferProperties *fbp=nullptr)
This is similar to make_texture_buffer() in that it allocates a separate buffer suitable for renderin...
bool is_stereo() const
Returns Returns true if this window can render stereo DisplayRegions, either through red-blue stereo ...
virtual void request_open()
This is called by the GraphicsEngine to request that the window (or whatever) open itself or,...
int get_x_size() const
Returns the visible width of the window or buffer, if it is known.
virtual bool flip_ready() const
Returns true if a frame has been rendered and needs to be flipped, false otherwise.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
set_sort
Adjusts the sorting order of this particular GraphicsOutput, relative to other GraphicsOutputs.
virtual void set_close_now()
This is called by the GraphicsEngine to insist that the output be closed immediately.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Encapsulates all the communication with a particular instance of a given rendering backend.
get_max_cube_map_dimension
Returns the largest possible texture size in any one dimension for a cube map texture,...
get_supports_cube_map
Returns true if this GSG can render cube map textures.
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
Definition: indirectLess.h:25
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
Similar to MutexHolder, but for a light mutex.
static void update_type(ReferenceCount *ptr, TypeHandle type)
Associates the indicated type with the given pointer.
Definition: memoryUsage.I:55
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
void look_at(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the transform on this NodePath so that it rotates to face the indicated point in space.
Definition: nodePath.I:789
void set_texture(Texture *tex, int priority=0)
Adds the indicated texture to the list of textures that will be rendered on the default texture stage...
Definition: nodePath.cxx:2934
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:596
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: pandaNode.cxx:999
A perspective-type lens: a normal camera.
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
Definition: renderBuffer.h:27
This is a special DisplayRegion wrapper that actually includes a pair of DisplayRegions internally: t...
get_right_eye
Returns a pointer to the right DisplayRegion managed by this stereo object.
get_left_eye
Returns a pointer to the left DisplayRegion managed by this stereo object.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
set_compression
Requests that this particular Texture be compressed when it is loaded into texture memory.
Definition: texture.h:413
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition: texture.h:370
set_match_framebuffer_format
Sets the special flag that, if true, indicates to the GSG that the Texture's format should be chosen ...
Definition: texture.h:589
set_wrap_u
This setting determines what happens when the texture is sampled with a U value outside the range 0....
Definition: texture.h:378
set_num_views
Sets the number of "views" within a texture.
Definition: texture.h:354
get_num_views
Returns the number of "views" in the texture.
Definition: texture.h:354
get_texture_type
Returns the overall interpretation of the texture.
Definition: texture.h:365
set_format
Changes the format value for the texture components.
Definition: texture.h:370
set_render_to_texture
Sets a flag on the texture that indicates whether the texture is intended to be used as a direct-rend...
Definition: texture.h:417
void setup_cube_map()
Sets the texture as an empty cube map texture with no dimensions.
Definition: texture.I:155
get_z_size
Returns the depth of the texture image in texels.
Definition: texture.h:350
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:2008
static AutoTextureScale get_textures_power_2()
This flag returns ATS_none, ATS_up, or ATS_down and controls the scaling of textures in general.
Definition: texture.I:1863
void clear_ram_image()
Discards the current system-RAM image.
Definition: texture.I:1439
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well.
Definition: texture.cxx:1932
set_wrap_v
This setting determines what happens when the texture is sampled with a V value outside the range 0....
Definition: texture.h:382
set_component_type
Changes the data value for the texture components.
Definition: texture.h:374
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A container for the various kinds of properties we might ask to have on a graphics window before we o...
static WindowProperties size(const LVecBase2i &size)
Returns a WindowProperties structure with only the size specified.
has_size
Returns true if the window size has been specified, false otherwise.
get_size
Returns size in pixels of the useful part of the window, not including decorations.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(DisplayRegion) GraphicsOutput
Returns the nth DisplayRegion of those that have been created within the window.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.