Panda3D
 All Classes Functions Variables Enumerations
tinyGraphicsStateGuardian.cxx
1 // Filename: tinyGraphicsStateGuardian.cxx
2 // Created by: drose (24Apr08)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "tinyGraphicsStateGuardian.h"
16 #include "tinyGeomMunger.h"
17 #include "tinyTextureContext.h"
18 #include "config_tinydisplay.h"
19 #include "pStatTimer.h"
20 #include "geomVertexReader.h"
21 #include "ambientLight.h"
22 #include "pointLight.h"
23 #include "directionalLight.h"
24 #include "spotlight.h"
25 #include "depthWriteAttrib.h"
26 #include "depthOffsetAttrib.h"
27 #include "colorWriteAttrib.h"
28 #include "alphaTestAttrib.h"
29 #include "depthTestAttrib.h"
30 #include "shadeModelAttrib.h"
31 #include "cullFaceAttrib.h"
32 #include "rescaleNormalAttrib.h"
33 #include "materialAttrib.h"
34 #include "lightAttrib.h"
35 #include "scissorAttrib.h"
36 #include "bitMask.h"
37 #include "samplerState.h"
38 #include "zgl.h"
39 #include "zmath.h"
40 #include "ztriangle_table.h"
41 #include "store_pixel_table.h"
42 #include "graphicsEngine.h"
43 
44 TypeHandle TinyGraphicsStateGuardian::_type_handle;
45 
46 PStatCollector TinyGraphicsStateGuardian::_vertices_immediate_pcollector("Vertices:Immediate mode");
47 PStatCollector TinyGraphicsStateGuardian::_draw_transform_pcollector("Draw:Transform");
48 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_untextured_pcollector("Pixels:White untextured");
49 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_untextured_pcollector("Pixels:Flat untextured");
50 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_untextured_pcollector("Pixels:Smooth untextured");
51 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_textured_pcollector("Pixels:White textured");
52 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_textured_pcollector("Pixels:Flat textured");
53 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_textured_pcollector("Pixels:Smooth textured");
54 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_perspective_pcollector("Pixels:White perspective");
55 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_perspective_pcollector("Pixels:Flat perspective");
56 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_perspective_pcollector("Pixels:Smooth perspective");
57 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_multitex2_pcollector("Pixels:Smooth multitex 2");
58 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_multitex3_pcollector("Pixels:Smooth multitex 3");
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: TinyGraphicsStateGuardian::Constructor
62 // Access: Public
63 // Description:
64 ////////////////////////////////////////////////////////////////////
65 TinyGraphicsStateGuardian::
66 TinyGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
67  TinyGraphicsStateGuardian *share_with) :
68  GraphicsStateGuardian(CS_yup_right, engine, pipe)
69 {
70  _current_frame_buffer = NULL;
71  _aux_frame_buffer = NULL;
72  _c = NULL;
73  _vertices = NULL;
74  _vertices_size = 0;
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: TinyGraphicsStateGuardian::Destructor
79 // Access: Public
80 // Description:
81 ////////////////////////////////////////////////////////////////////
82 TinyGraphicsStateGuardian::
83 ~TinyGraphicsStateGuardian() {
84 }
85 
86 ////////////////////////////////////////////////////////////////////
87 // Function: TinyGraphicsStateGuardian::reset
88 // Access: Public, Virtual
89 // Description: Resets all internal state as if the gsg were newly
90 // created.
91 ////////////////////////////////////////////////////////////////////
93 reset() {
94  free_pointers();
96 
97  // Build _inv_state_mask as a mask of 1's where we don't care, and
98  // 0's where we do care, about the state.
99  _inv_state_mask.clear_bit(ColorAttrib::get_class_slot());
100  _inv_state_mask.clear_bit(ColorScaleAttrib::get_class_slot());
101  _inv_state_mask.clear_bit(CullFaceAttrib::get_class_slot());
102  _inv_state_mask.clear_bit(DepthOffsetAttrib::get_class_slot());
103  _inv_state_mask.clear_bit(RenderModeAttrib::get_class_slot());
104  _inv_state_mask.clear_bit(RescaleNormalAttrib::get_class_slot());
105  _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
106  _inv_state_mask.clear_bit(MaterialAttrib::get_class_slot());
107  _inv_state_mask.clear_bit(LightAttrib::get_class_slot());
108  _inv_state_mask.clear_bit(ScissorAttrib::get_class_slot());
109 
110  if (_c != (GLContext *)NULL) {
111  glClose(_c);
112  _c = NULL;
113  }
114 
115  _c = (GLContext *)gl_zalloc(sizeof(GLContext));
116  glInit(_c, _current_frame_buffer);
117 
118  _c->draw_triangle_front = gl_draw_triangle_fill;
119  _c->draw_triangle_back = gl_draw_triangle_fill;
120 
121  _supported_geom_rendering =
122  Geom::GR_point |
123  Geom::GR_indexed_other |
124  Geom::GR_triangle_strip |
125  Geom::GR_flat_last_vertex;
126 
127  _max_texture_dimension = (1 << ZB_POINT_ST_FRAC_BITS);
128  _max_texture_stages = MAX_TEXTURE_STAGES;
129  _max_lights = MAX_LIGHTS;
130 
131  _color_scale_via_lighting = false;
132  _alpha_scale_via_texture = false;
133  _runtime_color_scale = true;
134 
135  _color_material_flags = 0;
136  _texturing_state = 0;
137  _texfilter_state = 0;
138  _texture_replace = false;
139  _filled_flat = false;
140  _auto_rescale_normal = false;
141 
142  // Now that the GSG has been initialized, make it available for
143  // optimizations.
144  add_gsg(this);
145 }
146 
147 ////////////////////////////////////////////////////////////////////
148 // Function: TinyGraphicsStateGuardian::free_pointers
149 // Access: Protected, Virtual
150 // Description: Frees some memory that was explicitly allocated
151 // within the glgsg.
152 ////////////////////////////////////////////////////////////////////
155  if (_aux_frame_buffer != (ZBuffer *)NULL) {
156  ZB_close(_aux_frame_buffer);
157  _aux_frame_buffer = NULL;
158  }
159 
160  if (_vertices != (GLVertex *)NULL) {
161  PANDA_FREE_ARRAY(_vertices);
162  _vertices = NULL;
163  }
164  _vertices_size = 0;
165 }
166 
167 ////////////////////////////////////////////////////////////////////
168 // Function: TinyGraphicsStateGuardian::close_gsg
169 // Access: Protected, Virtual
170 // Description: This is called by the associated GraphicsWindow when
171 // close_window() is called. It should null out the
172 // _win pointer and possibly free any open resources
173 // associated with the GSG.
174 ////////////////////////////////////////////////////////////////////
177  GraphicsStateGuardian::close_gsg();
178 
179  if (_c != (GLContext *)NULL) {
180  glClose(_c);
181  _c = NULL;
182  }
183 }
184 
185 ////////////////////////////////////////////////////////////////////
186 // Function: TinyGraphicsStateGuardian::depth_offset_decals
187 // Access: Public, Virtual
188 // Description: Returns true if this GSG can implement decals using a
189 // DepthOffsetAttrib, or false if that is unreliable
190 // and the three-step rendering process should be used
191 // instead.
192 ////////////////////////////////////////////////////////////////////
195  return false;
196 }
197 
198 ////////////////////////////////////////////////////////////////////
199 // Function: TinyGraphicsStateGuardian::make_geom_munger
200 // Access: Public, Virtual
201 // Description: Creates a new GeomMunger object to munge vertices
202 // appropriate to this GSG for the indicated state.
203 ////////////////////////////////////////////////////////////////////
205 make_geom_munger(const RenderState *state, Thread *current_thread) {
206  PT(TinyGeomMunger) munger = new TinyGeomMunger(this, state);
207  return GeomMunger::register_munger(munger, current_thread);
208 }
209 
210 ////////////////////////////////////////////////////////////////////
211 // Function: TinyGraphicsStateGuardian::clear
212 // Access: Public
213 // Description: Clears the framebuffer within the current
214 // DisplayRegion, according to the flags indicated by
215 // the given DrawableRegion object.
216 //
217 // This does not set the DisplayRegion first. You
218 // should call prepare_display_region() to specify the
219 // region you wish the clear operation to apply to.
220 ////////////////////////////////////////////////////////////////////
222 clear(DrawableRegion *clearable) {
223  PStatTimer timer(_clear_pcollector);
224 
225  if ((!clearable->get_clear_color_active())&&
226  (!clearable->get_clear_depth_active())&&
227  (!clearable->get_clear_stencil_active())) {
228  return;
229  }
230 
231  set_state_and_transform(RenderState::make_empty(), _internal_transform);
232 
233  bool clear_color = false;
234  PIXEL color = 0;
235  if (clearable->get_clear_color_active()) {
236  LColor v = clearable->get_clear_color();
237  v = v.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
238 
239  if (_current_properties->get_srgb_color()) {
240  color = SRGBA_TO_PIXEL(
241  (v[0] * 0xffff), (v[1] * 0xffff),
242  (v[2] * 0xffff), (v[3] * 0xffff));
243  } else {
244  color = RGBA_TO_PIXEL(
245  (v[0] * 0xffff), (v[1] * 0xffff),
246  (v[2] * 0xffff), (v[3] * 0xffff));
247  }
248  clear_color = true;
249  }
250 
251  bool clear_z = false;
252  int z = 0;
253  if (clearable->get_clear_depth_active()) {
254  // We ignore the specified depth clear value, since we don't
255  // support alternate depth compare functions anyway.
256  clear_z = true;
257  }
258 
259  ZB_clear_viewport(_c->zb, clear_z, z, clear_color, color,
260  _c->viewport.xmin, _c->viewport.ymin,
261  _c->viewport.xsize, _c->viewport.ysize);
262 }
263 
264 ////////////////////////////////////////////////////////////////////
265 // Function: TinyGraphicsStateGuardian::prepare_display_region
266 // Access: Public, Virtual
267 // Description: Prepare a display region for rendering (set up
268 // scissor region and viewport)
269 ////////////////////////////////////////////////////////////////////
272  nassertv(dr != (DisplayRegionPipelineReader *)NULL);
274 
275  int xmin, ymin, xsize, ysize;
276  dr->get_region_pixels_i(xmin, ymin, xsize, ysize);
277 
278  PN_stdfloat pixel_factor = _current_display_region->get_pixel_factor();
279  if (pixel_factor != 1.0) {
280  // Render into an aux buffer, and zoom it up into the main
281  // frame buffer later.
282  xmin = 0;
283  ymin = 0;
284  xsize = int(xsize * pixel_factor);
285  ysize = int(ysize * pixel_factor);
286  if (_aux_frame_buffer == (ZBuffer *)NULL) {
287  _aux_frame_buffer = ZB_open(xsize, ysize, ZB_MODE_RGBA, 0, 0, 0, 0);
288  } else if (_aux_frame_buffer->xsize < xsize || _aux_frame_buffer->ysize < ysize) {
289  ZB_resize(_aux_frame_buffer, NULL,
290  max(_aux_frame_buffer->xsize, xsize),
291  max(_aux_frame_buffer->ysize, ysize));
292  }
293  _c->zb = _aux_frame_buffer;
294 
295  } else {
296  // Render directly into the main frame buffer.
297  _c->zb = _current_frame_buffer;
298  }
299 
300  _c->viewport.xmin = xmin;
301  _c->viewport.ymin = ymin;
302  _c->viewport.xsize = xsize;
303  _c->viewport.ysize = ysize;
304  set_scissor(0.0f, 1.0f, 0.0f, 1.0f);
305 
306  nassertv(xmin >= 0 && xmin < _c->zb->xsize &&
307  ymin >= 0 && ymin < _c->zb->ysize &&
308  xmin + xsize >= 0 && xmin + xsize <= _c->zb->xsize &&
309  ymin + ysize >= 0 && ymin + ysize <= _c->zb->ysize);
310 }
311 
312 ////////////////////////////////////////////////////////////////////
313 // Function: TinyGraphicsStateGuardian::calc_projection_mat
314 // Access: Public, Virtual
315 // Description: Given a lens, calculates the appropriate projection
316 // matrix for use with this gsg. Note that the
317 // projection matrix depends a lot upon the coordinate
318 // system of the rendering API.
319 //
320 // The return value is a TransformState if the lens is
321 // acceptable, NULL if it is not.
322 ////////////////////////////////////////////////////////////////////
323 CPT(TransformState) TinyGraphicsStateGuardian::
324 calc_projection_mat(const Lens *lens) {
325  if (lens == (Lens *)NULL) {
326  return NULL;
327  }
328 
329  if (!lens->is_linear()) {
330  return NULL;
331  }
332 
333  // The projection matrix must always be right-handed Y-up, even if
334  // our coordinate system of choice is otherwise, because certain GL
335  // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of
336  // a coordinate system. Sigh. In order to implement a Z-up (or
337  // other arbitrary) coordinate system, we'll use a Y-up projection
338  // matrix, and store the conversion to our coordinate system of
339  // choice in the modelview matrix.
340 
341  LMatrix4 result =
342  LMatrix4::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
343  lens->get_projection_mat(_current_stereo_channel);
344 
345  if (_scene_setup->get_inverted()) {
346  // If the scene is supposed to be inverted, then invert the
347  // projection matrix.
348  result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
349  }
350 
351  return TransformState::make_mat(result);
352 }
353 
354 ////////////////////////////////////////////////////////////////////
355 // Function: TinyGraphicsStateGuardian::prepare_lens
356 // Access: Public, Virtual
357 // Description: Makes the current lens (whichever lens was most
358 // recently specified with set_scene()) active, so
359 // that it will transform future rendered geometry.
360 // Normally this is only called from the draw process,
361 // and usually it is called by set_scene().
362 //
363 // The return value is true if the lens is acceptable,
364 // false if it is not.
365 ////////////////////////////////////////////////////////////////////
368  _transform_stale = true;
369  return true;
370 }
371 
372 ////////////////////////////////////////////////////////////////////
373 // Function: GraphicsStateGuardian::begin_frame
374 // Access: Public, Virtual
375 // Description: Called before each frame is rendered, to allow the
376 // GSG a chance to do any internal cleanup before
377 // beginning the frame.
378 //
379 // The return value is true if successful (in which case
380 // the frame will be drawn and end_frame() will be
381 // called later), or false if unsuccessful (in which
382 // case nothing will be drawn and end_frame() will not
383 // be called).
384 ////////////////////////////////////////////////////////////////////
386 begin_frame(Thread *current_thread) {
387  if (!GraphicsStateGuardian::begin_frame(current_thread)) {
388  return false;
389  }
390 
391  _c->zb = _current_frame_buffer;
392 
393 #ifdef DO_PSTATS
394  _vertices_immediate_pcollector.clear_level();
395 
396  _pixel_count_white_untextured_pcollector.clear_level();
397  _pixel_count_flat_untextured_pcollector.clear_level();
398  _pixel_count_smooth_untextured_pcollector.clear_level();
399  _pixel_count_white_textured_pcollector.clear_level();
400  _pixel_count_flat_textured_pcollector.clear_level();
401  _pixel_count_smooth_textured_pcollector.clear_level();
402  _pixel_count_white_perspective_pcollector.clear_level();
403  _pixel_count_flat_perspective_pcollector.clear_level();
404  _pixel_count_smooth_perspective_pcollector.clear_level();
405  _pixel_count_smooth_multitex2_pcollector.clear_level();
406  _pixel_count_smooth_multitex3_pcollector.clear_level();
407 #endif
408 
409  return true;
410 }
411 
412 ////////////////////////////////////////////////////////////////////
413 // Function: GraphicsStateGuardian::begin_scene
414 // Access: Public, Virtual
415 // Description: Called between begin_frame() and end_frame() to mark
416 // the beginning of drawing commands for a "scene"
417 // (usually a particular DisplayRegion) within a frame.
418 // All 3-D drawing commands, except the clear operation,
419 // must be enclosed within begin_scene() .. end_scene().
420 //
421 // The return value is true if successful (in which case
422 // the scene will be drawn and end_scene() will be
423 // called later), or false if unsuccessful (in which
424 // case nothing will be drawn and end_scene() will not
425 // be called).
426 ////////////////////////////////////////////////////////////////////
430 }
431 
432 ////////////////////////////////////////////////////////////////////
433 // Function: TinyGraphicsStateGuardian::end_scene
434 // Access: Protected, Virtual
435 // Description: Called between begin_frame() and end_frame() to mark
436 // the end of drawing commands for a "scene" (usually a
437 // particular DisplayRegion) within a frame. All 3-D
438 // drawing commands, except the clear operation, must be
439 // enclosed within begin_scene() .. end_scene().
440 ////////////////////////////////////////////////////////////////////
443  if (_c->zb == _aux_frame_buffer) {
444  // Copy the aux frame buffer into the main scene now, zooming it
445  // up to the appropriate size.
446  int xmin, ymin, xsize, ysize;
447  _current_display_region->get_region_pixels_i(xmin, ymin, xsize, ysize);
448  PN_stdfloat pixel_factor = _current_display_region->get_pixel_factor();
449 
450  int fb_xsize = int(xsize * pixel_factor);
451  int fb_ysize = int(ysize * pixel_factor);
452 
453  ZB_zoomFrameBuffer(_current_frame_buffer, xmin, ymin, xsize, ysize,
454  _aux_frame_buffer, 0, 0, fb_xsize, fb_ysize);
455  _c->zb = _current_frame_buffer;
456  }
457 
458  // Clear the lighting state.
459  clear_light_state();
460  _plights.clear();
461  _dlights.clear();
462  _slights.clear();
463 
465 }
466 
467 ////////////////////////////////////////////////////////////////////
468 // Function: TinyGraphicsStateGuardian::end_frame
469 // Access: Public, Virtual
470 // Description: Called after each frame is rendered, to allow the
471 // GSG a chance to do any internal cleanup after
472 // rendering the frame, and before the window flips.
473 ////////////////////////////////////////////////////////////////////
475 end_frame(Thread *current_thread) {
476  GraphicsStateGuardian::end_frame(current_thread);
477 
478 #ifndef NDEBUG
479  static ConfigVariableBool td_show_zbuffer
480  ("td-show-zbuffer", false,
481  PRC_DESC("Set this true to draw the ZBuffer instead of the visible buffer, when rendering with tinydisplay. This is useful to aid debugging the ZBuffer"));
482  if (td_show_zbuffer) {
483  PIXEL *tp = _current_frame_buffer->pbuf;
484  ZPOINT *tz = _current_frame_buffer->zbuf;
485  for (int yi = 0; yi < _current_frame_buffer->ysize; ++yi) {
486  for (int xi = 0; xi < _current_frame_buffer->xsize; ++xi) {
487  (*tp) = (int)(*tz);
488  ++tz;
489  ++tp;
490  }
491  }
492  }
493 #endif // NDEBUG
494 
495 #ifdef DO_PSTATS
496  // Flush any PCollectors specific to this kind of GSG.
497  _vertices_immediate_pcollector.flush_level();
498 
499  _pixel_count_white_untextured_pcollector.flush_level();
500  _pixel_count_flat_untextured_pcollector.flush_level();
501  _pixel_count_smooth_untextured_pcollector.flush_level();
502  _pixel_count_white_textured_pcollector.flush_level();
503  _pixel_count_flat_textured_pcollector.flush_level();
504  _pixel_count_smooth_textured_pcollector.flush_level();
505  _pixel_count_white_perspective_pcollector.flush_level();
506  _pixel_count_flat_perspective_pcollector.flush_level();
507  _pixel_count_smooth_perspective_pcollector.flush_level();
508  _pixel_count_smooth_multitex2_pcollector.flush_level();
509  _pixel_count_smooth_multitex3_pcollector.flush_level();
510 #endif // DO_PSTATS
511 }
512 
513 
514 ////////////////////////////////////////////////////////////////////
515 // Function: TinyGraphicsStateGuardian::begin_draw_primitives
516 // Access: Public, Virtual
517 // Description: Called before a sequence of draw_primitive()
518 // functions are called, this should prepare the vertex
519 // data for rendering. It returns true if the vertices
520 // are ok, false to abort this group of primitives.
521 ////////////////////////////////////////////////////////////////////
524  const GeomMunger *munger,
525  const GeomVertexDataPipelineReader *data_reader,
526  bool force) {
527 #ifndef NDEBUG
528  if (tinydisplay_cat.is_spam()) {
529  tinydisplay_cat.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
530  }
531 #endif // NDEBUG
532 
533  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
534  return false;
535  }
536  nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
537 
538  PStatTimer timer(_draw_transform_pcollector);
539 
540  // Set up the proper transform.
541  if (_data_reader->is_vertex_transformed()) {
542  // If the vertex data claims to be already transformed into clip
543  // coordinates, wipe out the current projection and modelview
544  // matrix (so we don't attempt to transform it again).
545  const TransformState *ident = TransformState::make_identity();
546  load_matrix(&_c->matrix_model_view, ident);
547  load_matrix(&_c->matrix_projection, _scissor_mat);
548  load_matrix(&_c->matrix_model_view_inv, ident);
549  load_matrix(&_c->matrix_model_projection, _scissor_mat);
550  _c->matrix_model_projection_no_w_transform = 1;
551  _transform_stale = true;
552 
553  } else if (_transform_stale) {
554  // Load the actual transform.
555 
556  CPT(TransformState) scissor_proj_mat = _scissor_mat->compose(_projection_mat);
557 
558  if (_c->lighting_enabled) {
559  // With the lighting equation, we need to keep the modelview and
560  // projection matrices separate.
561 
562  load_matrix(&_c->matrix_model_view, _internal_transform);
563  load_matrix(&_c->matrix_projection, scissor_proj_mat);
564 
565  /* precompute inverse modelview */
566  M4 tmp;
567  gl_M4_Inv(&tmp, &_c->matrix_model_view);
568  gl_M4_Transpose(&_c->matrix_model_view_inv, &tmp);
569 
570  }
571 
572  // Compose the modelview and projection matrices.
573  load_matrix(&_c->matrix_model_projection,
574  scissor_proj_mat->compose(_internal_transform));
575 
576  /* test to accelerate computation */
577  _c->matrix_model_projection_no_w_transform = 0;
578  PN_stdfloat *m = &_c->matrix_model_projection.m[0][0];
579  if (m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0) {
580  _c->matrix_model_projection_no_w_transform = 1;
581  }
582  _transform_stale = false;
583  }
584 
585  // Figure out the subset of vertices we will be using in this
586  // operation.
587  int num_vertices = data_reader->get_num_rows();
588  _min_vertex = num_vertices;
589  _max_vertex = 0;
590  int num_prims = geom_reader->get_num_primitives();
591  int i;
592  for (i = 0; i < num_prims; ++i) {
593  CPT(GeomPrimitive) prim = geom_reader->get_primitive(i);
594  int nv = prim->get_min_vertex();
595  _min_vertex = min(_min_vertex, nv);
596  int xv = prim->get_max_vertex();
597  _max_vertex = max(_max_vertex, xv);
598  }
599  if (_min_vertex > _max_vertex) {
600  return false;
601  }
602 
603  // Now copy all of those vertices into our working table,
604  // transforming into screen space them as we go.
605  int num_used_vertices = _max_vertex - _min_vertex + 1;
606  if (_vertices_size < num_used_vertices) {
607  if (_vertices_size == 0) {
608  _vertices_size = 1;
609  }
610  while (_vertices_size < num_used_vertices) {
611  _vertices_size *= 2;
612  }
613  if (_vertices != (GLVertex *)NULL) {
614  PANDA_FREE_ARRAY(_vertices);
615  }
616  _vertices = (GLVertex *)PANDA_MALLOC_ARRAY(_vertices_size * sizeof(GLVertex));
617  }
618 
619  GeomVertexReader rcolor, rnormal;
620 
621  // We now support up to 3-stage multitexturing.
622  GenTexcoordFunc *texgen_func[MAX_TEXTURE_STAGES];
623  TexCoordData tcdata[MAX_TEXTURE_STAGES];
624 
625  const TexGenAttrib *target_tex_gen = DCAST(TexGenAttrib, _target_rs->get_attrib_def(TexGenAttrib::get_class_slot()));
626  const TexMatrixAttrib *target_tex_matrix = DCAST(TexMatrixAttrib, _target_rs->get_attrib_def(TexMatrixAttrib::get_class_slot()));
627 
628  int max_stage_index = _target_texture->get_num_on_ff_stages();
629  for (int si = 0; si < max_stage_index; ++si) {
630  TextureStage *stage = _target_texture->get_on_ff_stage(si);
631 
632  switch (target_tex_gen->get_mode(stage)) {
633  case TexGenAttrib::M_eye_sphere_map:
634  tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_normal(),
635  force);
636  tcdata[si]._r2 = GeomVertexReader(data_reader, InternalName::get_vertex(),
637  force);
638  texgen_func[si] = &texgen_sphere_map;
639  tcdata[si]._mat = _internal_transform->get_mat();
640  break;
641 
642  case TexGenAttrib::M_eye_position:
643  tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_vertex(),
644  force);
645  texgen_func[si] = &texgen_texmat;
646  {
647  CPT(TransformState) eye_transform =
648  _cs_transform->invert_compose(_internal_transform);
649  tcdata[si]._mat = eye_transform->get_mat();
650  }
651  if (target_tex_matrix->has_stage(stage)) {
652  tcdata[si]._mat = tcdata[si]._mat * target_tex_matrix->get_mat(stage);
653  }
654  break;
655 
656  case TexGenAttrib::M_world_position:
657  tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_vertex(),
658  force);
659  texgen_func[si] = &texgen_texmat;
660  {
661  CPT(TransformState) render_transform =
662  _cs_transform->compose(_scene_setup->get_world_transform());
663  CPT(TransformState) world_inv_transform =
664  render_transform->invert_compose(_internal_transform);
665  tcdata[si]._mat = world_inv_transform->get_mat();
666  }
667  if (target_tex_matrix->has_stage(stage)) {
668  tcdata[si]._mat = tcdata[si]._mat * target_tex_matrix->get_mat(stage);
669  }
670  break;
671 
672  default:
673  // Fall through: use the standard texture coordinates.
674  tcdata[si]._r1 = GeomVertexReader(data_reader, stage->get_texcoord_name(),
675  force);
676  texgen_func[si] = &texgen_simple;
677  if (target_tex_matrix->has_stage(stage)) {
678  texgen_func[si] = &texgen_texmat;
679  tcdata[si]._mat = target_tex_matrix->get_mat(stage);
680  }
681 
682  break;
683  }
684  tcdata[si]._r1.set_row_unsafe(_min_vertex);
685  tcdata[si]._r2.set_row_unsafe(_min_vertex);
686  if (!tcdata[si]._r1.has_column()) {
687  texgen_func[si] = &texgen_null;
688  }
689  }
690 
691  bool needs_color = false;
692  if (_vertex_colors_enabled) {
693  rcolor = GeomVertexReader(data_reader, InternalName::get_color(), force);
694  rcolor.set_row_unsafe(_min_vertex);
695  needs_color = rcolor.has_column();
696  }
697 
698  if (!needs_color) {
699  const LColor &d = _scene_graph_color;
700  const LColor &s = _current_color_scale;
701  _c->current_color.v[0] = max(d[0] * s[0], (PN_stdfloat)0);
702  _c->current_color.v[1] = max(d[1] * s[1], (PN_stdfloat)0);
703  _c->current_color.v[2] = max(d[2] * s[2], (PN_stdfloat)0);
704  _c->current_color.v[3] = max(d[3] * s[3], (PN_stdfloat)0);
705  }
706 
707  bool needs_normal = false;
708  if (_c->lighting_enabled) {
709  rnormal = GeomVertexReader(data_reader, InternalName::get_normal(), force);
710  rnormal.set_row_unsafe(_min_vertex);
711  needs_normal = rnormal.has_column();
712  }
713 
714  GeomVertexReader rvertex(data_reader, InternalName::get_vertex(), force);
715  rvertex.set_row_unsafe(_min_vertex);
716 
717  if (!rvertex.has_column()) {
718  // Whoops, guess the vertex data isn't resident.
719  return false;
720  }
721 
722  if (!needs_color && _color_material_flags) {
723  if (_color_material_flags & CMF_ambient) {
724  _c->materials[0].ambient = _c->current_color;
725  _c->materials[1].ambient = _c->current_color;
726  }
727  if (_color_material_flags & CMF_diffuse) {
728  _c->materials[0].diffuse = _c->current_color;
729  _c->materials[1].diffuse = _c->current_color;
730  }
731  }
732 
733  if (_texturing_state != 0 && _texture_replace) {
734  // We don't need the vertex color or lighting calculation after
735  // all, since the current texture will just hide all of that.
736  needs_color = false;
737  needs_normal = false;
738  }
739 
740  bool lighting_enabled = (needs_normal && _c->lighting_enabled);
741 
742  for (i = 0; i < num_used_vertices; ++i) {
743  GLVertex *v = &_vertices[i];
744  const LVecBase4 &d = rvertex.get_data4();
745 
746  v->coord.v[0] = d[0];
747  v->coord.v[1] = d[1];
748  v->coord.v[2] = d[2];
749  v->coord.v[3] = d[3];
750 
751  // Texture coordinates.
752  for (int si = 0; si < max_stage_index; ++si) {
753  LTexCoord d;
754  (*texgen_func[si])(v->tex_coord[si], tcdata[si]);
755  }
756 
757  if (needs_color) {
758  const LColor &d = rcolor.get_data4();
759  const LColor &s = _current_color_scale;
760  _c->current_color.v[0] = max(d[0] * s[0], (PN_stdfloat)0);
761  _c->current_color.v[1] = max(d[1] * s[1], (PN_stdfloat)0);
762  _c->current_color.v[2] = max(d[2] * s[2], (PN_stdfloat)0);
763  _c->current_color.v[3] = max(d[3] * s[3], (PN_stdfloat)0);
764 
765  if (_color_material_flags) {
766  if (_color_material_flags & CMF_ambient) {
767  _c->materials[0].ambient = _c->current_color;
768  _c->materials[1].ambient = _c->current_color;
769  }
770  if (_color_material_flags & CMF_diffuse) {
771  _c->materials[0].diffuse = _c->current_color;
772  _c->materials[1].diffuse = _c->current_color;
773  }
774  }
775  }
776 
777  v->color = _c->current_color;
778 
779  if (lighting_enabled) {
780  const LVecBase3 &d = rnormal.get_data3();
781  _c->current_normal.v[0] = d[0];
782  _c->current_normal.v[1] = d[1];
783  _c->current_normal.v[2] = d[2];
784  _c->current_normal.v[3] = 0.0f;
785 
786  gl_vertex_transform(_c, v);
787  gl_shade_vertex(_c, v);
788 
789  } else {
790  gl_vertex_transform(_c, v);
791  }
792 
793  if (v->clip_code == 0) {
794  gl_transform_to_viewport(_c, v);
795  }
796 
797  v->edge_flag = 1;
798  }
799 
800  // Set up the appropriate function callback for filling triangles,
801  // according to the current state.
802 
803  bool srgb_blend = _current_properties->get_srgb_color();
804 
805  int depth_write_state = 0; // zon
806  const DepthWriteAttrib *target_depth_write = DCAST(DepthWriteAttrib, _target_rs->get_attrib_def(DepthWriteAttrib::get_class_slot()));
807  if (target_depth_write->get_mode() != DepthWriteAttrib::M_on) {
808  depth_write_state = 1; // zoff
809  }
810 
811  int color_write_state = 0; // cstore
812 
813  const ColorWriteAttrib *target_color_write = DCAST(ColorWriteAttrib, _target_rs->get_attrib_def(ColorWriteAttrib::get_class_slot()));
814  unsigned int color_channels =
815  target_color_write->get_channels() & _color_write_mask;
816 
817  if (color_channels == ColorWriteAttrib::C_all) {
818  if (srgb_blend) {
819  color_write_state = 4; // csstore
820  } else {
821  color_write_state = 0; // cstore
822  }
823  } else {
824  // Implement a color mask.
825  int op_a = get_color_blend_op(ColorBlendAttrib::O_one);
826  int op_b = get_color_blend_op(ColorBlendAttrib::O_zero);
827 
828  if (srgb_blend) {
829  _c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
830  } else {
831  _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
832  }
833  color_write_state = 2; // cgeneral
834  }
835 
836  const TransparencyAttrib *target_transparency = DCAST(TransparencyAttrib, _target_rs->get_attrib_def(TransparencyAttrib::get_class_slot()));
837  switch (target_transparency->get_mode()) {
838  case TransparencyAttrib::M_alpha:
839  case TransparencyAttrib::M_dual:
840  if (color_channels == ColorWriteAttrib::C_all) {
841  if (srgb_blend) {
842  color_write_state = 5; // csblend
843  } else {
844  color_write_state = 1; // cblend
845  }
846  } else {
847  // Implement a color mask, with alpha blending.
848  int op_a = get_color_blend_op(ColorBlendAttrib::O_incoming_alpha);
849  int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha);
850 
851  if (srgb_blend) {
852  _c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
853  } else {
854  _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
855  }
856  color_write_state = 2; // cgeneral
857  }
858  break;
859 
860  default:
861  break;
862  }
863 
864  const ColorBlendAttrib *target_color_blend = DCAST(ColorBlendAttrib, _target_rs->get_attrib_def(ColorBlendAttrib::get_class_slot()));
865  if (target_color_blend->get_mode() == ColorBlendAttrib::M_add) {
866  // If we have a color blend set that we can support, it overrides
867  // the transparency set.
868  LColor c = target_color_blend->get_color();
869  _c->zb->blend_r = (int)(c[0] * ZB_POINT_RED_MAX);
870  _c->zb->blend_g = (int)(c[1] * ZB_POINT_GREEN_MAX);
871  _c->zb->blend_b = (int)(c[2] * ZB_POINT_BLUE_MAX);
872  _c->zb->blend_a = (int)(c[3] * ZB_POINT_ALPHA_MAX);
873 
874  int op_a = get_color_blend_op(target_color_blend->get_operand_a());
875  int op_b = get_color_blend_op(target_color_blend->get_operand_b());
876 
877  if (srgb_blend) {
878  _c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
879  } else {
880  _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
881  }
882  color_write_state = 2; // cgeneral
883  }
884 
885  if (color_channels == ColorWriteAttrib::C_off) {
886  color_write_state = 3; // coff
887  }
888 
889  int alpha_test_state = 0; // anone
890  const AlphaTestAttrib *target_alpha_test = DCAST(AlphaTestAttrib, _target_rs->get_attrib_def(AlphaTestAttrib::get_class_slot()));
891  switch (target_alpha_test->get_mode()) {
892  case AlphaTestAttrib::M_none:
893  case AlphaTestAttrib::M_never:
894  case AlphaTestAttrib::M_always:
895  case AlphaTestAttrib::M_equal:
896  case AlphaTestAttrib::M_not_equal:
897  alpha_test_state = 0; // anone
898  break;
899 
900  case AlphaTestAttrib::M_less:
901  case AlphaTestAttrib::M_less_equal:
902  alpha_test_state = 1; // aless
903  _c->zb->reference_alpha = (int)(target_alpha_test->get_reference_alpha() * ZB_POINT_ALPHA_MAX);
904  break;
905 
906  case AlphaTestAttrib::M_greater:
907  case AlphaTestAttrib::M_greater_equal:
908  alpha_test_state = 2; // amore
909  _c->zb->reference_alpha = (int)(target_alpha_test->get_reference_alpha() * ZB_POINT_ALPHA_MAX);
910  break;
911  }
912 
913  int depth_test_state = 1; // zless
914  _c->depth_test = 1; // set this for ZB_line
915  const DepthTestAttrib *target_depth_test = DCAST(DepthTestAttrib, _target_rs->get_attrib_def(DepthTestAttrib::get_class_slot()));
916  if (target_depth_test->get_mode() == DepthTestAttrib::M_none) {
917  depth_test_state = 0; // zless
918  _c->depth_test = 0;
919  }
920 
921  const ShadeModelAttrib *target_shade_model = DCAST(ShadeModelAttrib, _target_rs->get_attrib_def(ShadeModelAttrib::get_class_slot()));
922  ShadeModelAttrib::Mode shade_model = target_shade_model->get_mode();
923  if (!needs_normal && !needs_color) {
924  // With no per-vertex lighting, and no per-vertex colors, we might
925  // as well use the flat shading model.
926  shade_model = ShadeModelAttrib::M_flat;
927  }
928  int shade_model_state = 2; // smooth
929  _c->smooth_shade_model = true;
930 
931  if (shade_model == ShadeModelAttrib::M_flat) {
932  _c->smooth_shade_model = false;
933  shade_model_state = 1; // flat
934  if (_c->current_color.v[0] == 1.0f &&
935  _c->current_color.v[1] == 1.0f &&
936  _c->current_color.v[2] == 1.0f &&
937  _c->current_color.v[3] == 1.0f) {
938  shade_model_state = 0; // white
939  }
940  }
941 
942  int texturing_state = _texturing_state;
943  int texfilter_state = 0; // tnearest
944  if (texturing_state > 0) {
945  texfilter_state = _texfilter_state;
946 
947  if (texturing_state < 3 &&
948  (_c->matrix_model_projection_no_w_transform || _filled_flat)) {
949  // Don't bother with the perspective-correct algorithm if we're
950  // under an orthonormal lens, e.g. render2d; or if
951  // RenderMode::M_filled_flat is in effect.
952  texturing_state = 1; // textured (not perspective correct)
953  }
954 
955  if (_texture_replace) {
956  // If we're completely replacing the underlying color, then it
957  // doesn't matter what the color is.
958  shade_model_state = 0;
959  }
960  }
961 
962  _c->zb_fill_tri = fill_tri_funcs[depth_write_state][color_write_state][alpha_test_state][depth_test_state][texfilter_state][shade_model_state][texturing_state];
963 
964 #ifdef DO_PSTATS
965  pixel_count_white_untextured = 0;
966  pixel_count_flat_untextured = 0;
967  pixel_count_smooth_untextured = 0;
968  pixel_count_white_textured = 0;
969  pixel_count_flat_textured = 0;
970  pixel_count_smooth_textured = 0;
971  pixel_count_white_perspective = 0;
972  pixel_count_flat_perspective = 0;
973  pixel_count_smooth_perspective = 0;
974  pixel_count_smooth_multitex2 = 0;
975  pixel_count_smooth_multitex3 = 0;
976 #endif // DO_PSTATS
977 
978  return true;
979 }
980 
981 ////////////////////////////////////////////////////////////////////
982 // Function: TinyGraphicsStateGuardian::draw_triangles
983 // Access: Public, Virtual
984 // Description: Draws a series of disconnected triangles.
985 ////////////////////////////////////////////////////////////////////
987 draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
988  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
989 
990 #ifndef NDEBUG
991  if (tinydisplay_cat.is_spam()) {
992  tinydisplay_cat.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
993  }
994 #endif // NDEBUG
995 
996  int num_vertices = reader->get_num_vertices();
997  _vertices_tri_pcollector.add_level(num_vertices);
998 
999  if (reader->is_indexed()) {
1000  switch (reader->get_index_type()) {
1001  case Geom::NT_uint8:
1002  {
1003  PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
1004  if (index == NULL) {
1005  return false;
1006  }
1007  for (int i = 0; i < num_vertices; i += 3) {
1008  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1009  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1010  GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
1011  gl_draw_triangle(_c, v0, v1, v2);
1012  }
1013  }
1014  break;
1015 
1016  case Geom::NT_uint16:
1017  {
1018  PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
1019  if (index == NULL) {
1020  return false;
1021  }
1022  for (int i = 0; i < num_vertices; i += 3) {
1023  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1024  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1025  GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
1026  gl_draw_triangle(_c, v0, v1, v2);
1027  }
1028  }
1029  break;
1030 
1031  case Geom::NT_uint32:
1032  {
1033  PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
1034  if (index == NULL) {
1035  return false;
1036  }
1037  for (int i = 0; i < num_vertices; i += 3) {
1038  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1039  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1040  GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
1041  gl_draw_triangle(_c, v0, v1, v2);
1042  }
1043  }
1044  break;
1045 
1046  default:
1047  tinydisplay_cat.error()
1048  << "Invalid index type " << reader->get_index_type() << "!\n";
1049  return false;
1050  }
1051 
1052  } else {
1053  int delta = reader->get_first_vertex() - _min_vertex;
1054  for (int vi = 0; vi < num_vertices; vi += 3) {
1055  GLVertex *v0 = &_vertices[vi + delta];
1056  GLVertex *v1 = &_vertices[vi + delta + 1];
1057  GLVertex *v2 = &_vertices[vi + delta + 2];
1058  gl_draw_triangle(_c, v0, v1, v2);
1059  }
1060  }
1061 
1062  return true;
1063 }
1064 
1065 ////////////////////////////////////////////////////////////////////
1066 // Function: TinyGraphicsStateGuardian::draw_tristrips
1067 // Access: Public, Virtual
1068 // Description: Draws a series of triangle strips.
1069 ////////////////////////////////////////////////////////////////////
1071 draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
1072  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
1073 
1074 #ifndef NDEBUG
1075  if (tinydisplay_cat.is_spam()) {
1076  tinydisplay_cat.spam() << "draw_tristrips: " << *(reader->get_object()) << "\n";
1077  }
1078 #endif // NDEBUG
1079 
1080  // Send the individual triangle strips, stepping over the
1081  // degenerate vertices.
1082  CPTA_int ends = reader->get_ends();
1083 
1084  _primitive_batches_tristrip_pcollector.add_level(ends.size());
1085  if (reader->is_indexed()) {
1086  unsigned int start = 0;
1087  for (size_t i = 0; i < ends.size(); i++) {
1088  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1089 
1090  int end = ends[i];
1091  nassertr(end - start >= 3, false);
1092 
1093  switch (reader->get_index_type()) {
1094  case Geom::NT_uint8:
1095  {
1096  PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
1097  if (index == NULL) {
1098  return false;
1099  }
1100  GLVertex *v0 = &_vertices[index[start] - _min_vertex];
1101  GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
1102 
1103  bool reversed = false;
1104  for (int vi = start + 2; vi < end; ++vi) {
1105  GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
1106  if (reversed) {
1107  gl_draw_triangle(_c, v1, v0, v2);
1108  reversed = false;
1109  } else {
1110  gl_draw_triangle(_c, v0, v1, v2);
1111  reversed = true;
1112  }
1113  v0 = v1;
1114  v1 = v2;
1115  }
1116  }
1117  break;
1118 
1119  case Geom::NT_uint16:
1120  {
1121  PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
1122  if (index == NULL) {
1123  return false;
1124  }
1125  GLVertex *v0 = &_vertices[index[start] - _min_vertex];
1126  GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
1127 
1128  bool reversed = false;
1129  for (int vi = start + 2; vi < end; ++vi) {
1130  GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
1131  if (reversed) {
1132  gl_draw_triangle(_c, v1, v0, v2);
1133  reversed = false;
1134  } else {
1135  gl_draw_triangle(_c, v0, v1, v2);
1136  reversed = true;
1137  }
1138  v0 = v1;
1139  v1 = v2;
1140  }
1141  }
1142  break;
1143 
1144  case Geom::NT_uint32:
1145  {
1146  PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
1147  if (index == NULL) {
1148  return false;
1149  }
1150  GLVertex *v0 = &_vertices[index[start] - _min_vertex];
1151  GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
1152 
1153  bool reversed = false;
1154  for (int vi = start + 2; vi < end; ++vi) {
1155  GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
1156  if (reversed) {
1157  gl_draw_triangle(_c, v1, v0, v2);
1158  reversed = false;
1159  } else {
1160  gl_draw_triangle(_c, v0, v1, v2);
1161  reversed = true;
1162  }
1163  v0 = v1;
1164  v1 = v2;
1165  }
1166  }
1167  break;
1168 
1169  default:
1170  tinydisplay_cat.error()
1171  << "Invalid index type " << reader->get_index_type() << "!\n";
1172  return false;
1173  }
1174 
1175  start = ends[i] + 2;
1176  }
1177  } else {
1178  unsigned int start = 0;
1179  int delta = reader->get_first_vertex() - _min_vertex;
1180  for (size_t i = 0; i < ends.size(); i++) {
1181  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1182 
1183  int end = ends[i];
1184  nassertr(end - start >= 3, false);
1185  GLVertex *v0 = &_vertices[start + delta];
1186  GLVertex *v1 = &_vertices[start + delta + 1];
1187 
1188  bool reversed = false;
1189  for (int vi = start + 2; vi < end; ++vi) {
1190  GLVertex *v2 = &_vertices[vi + delta];
1191  if (reversed) {
1192  gl_draw_triangle(_c, v1, v0, v2);
1193  reversed = false;
1194  } else {
1195  gl_draw_triangle(_c, v0, v1, v2);
1196  reversed = true;
1197  }
1198  v0 = v1;
1199  v1 = v2;
1200  }
1201  start = ends[i] + 2;
1202  }
1203  }
1204 
1205  return true;
1206 }
1207 
1208 ////////////////////////////////////////////////////////////////////
1209 // Function: TinyGraphicsStateGuardian::draw_lines
1210 // Access: Public, Virtual
1211 // Description: Draws a series of disconnected line segments.
1212 ////////////////////////////////////////////////////////////////////
1214 draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
1215  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
1216 #ifndef NDEBUG
1217  if (tinydisplay_cat.is_spam()) {
1218  tinydisplay_cat.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
1219  }
1220 #endif // NDEBUG
1221 
1222  int num_vertices = reader->get_num_vertices();
1223  _vertices_other_pcollector.add_level(num_vertices);
1224 
1225  if (reader->is_indexed()) {
1226  switch (reader->get_index_type()) {
1227  case Geom::NT_uint8:
1228  {
1229  PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
1230  if (index == NULL) {
1231  return false;
1232  }
1233  for (int i = 0; i < num_vertices; i += 2) {
1234  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1235  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1236  gl_draw_line(_c, v0, v1);
1237  }
1238  }
1239  break;
1240 
1241  case Geom::NT_uint16:
1242  {
1243  PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
1244  if (index == NULL) {
1245  return false;
1246  }
1247  for (int i = 0; i < num_vertices; i += 2) {
1248  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1249  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1250  gl_draw_line(_c, v0, v1);
1251  }
1252  }
1253  break;
1254 
1255  case Geom::NT_uint32:
1256  {
1257  PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
1258  if (index == NULL) {
1259  return false;
1260  }
1261  for (int i = 0; i < num_vertices; i += 2) {
1262  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1263  GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
1264  gl_draw_line(_c, v0, v1);
1265  }
1266  }
1267  break;
1268 
1269  default:
1270  tinydisplay_cat.error()
1271  << "Invalid index type " << reader->get_index_type() << "!\n";
1272  return false;
1273  }
1274 
1275  } else {
1276  int delta = reader->get_first_vertex() - _min_vertex;
1277  for (int vi = 0; vi < num_vertices; vi += 2) {
1278  GLVertex *v0 = &_vertices[vi + delta];
1279  GLVertex *v1 = &_vertices[vi + delta + 1];
1280  gl_draw_line(_c, v0, v1);
1281  }
1282  }
1283 
1284  return true;
1285 }
1286 
1287 ////////////////////////////////////////////////////////////////////
1288 // Function: TinyGraphicsStateGuardian::draw_points
1289 // Access: Public, Virtual
1290 // Description: Draws a series of disconnected points.
1291 ////////////////////////////////////////////////////////////////////
1293 draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
1294  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
1295 #ifndef NDEBUG
1296  if (tinydisplay_cat.is_spam()) {
1297  tinydisplay_cat.spam() << "draw_points: " << *(reader->get_object()) << "\n";
1298  }
1299 #endif // NDEBUG
1300 
1301  int num_vertices = reader->get_num_vertices();
1302  _vertices_other_pcollector.add_level(num_vertices);
1303 
1304  if (reader->is_indexed()) {
1305  switch (reader->get_index_type()) {
1306  case Geom::NT_uint8:
1307  {
1308  PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
1309  if (index == NULL) {
1310  return false;
1311  }
1312  for (int i = 0; i < num_vertices; ++i) {
1313  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1314  gl_draw_point(_c, v0);
1315  }
1316  }
1317  break;
1318 
1319  case Geom::NT_uint16:
1320  {
1321  PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
1322  if (index == NULL) {
1323  return false;
1324  }
1325  for (int i = 0; i < num_vertices; ++i) {
1326  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1327  gl_draw_point(_c, v0);
1328  }
1329  }
1330  break;
1331 
1332  case Geom::NT_uint32:
1333  {
1334  PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
1335  if (index == NULL) {
1336  return false;
1337  }
1338  for (int i = 0; i < num_vertices; ++i) {
1339  GLVertex *v0 = &_vertices[index[i] - _min_vertex];
1340  gl_draw_point(_c, v0);
1341  }
1342  }
1343  break;
1344 
1345  default:
1346  tinydisplay_cat.error()
1347  << "Invalid index type " << reader->get_index_type() << "!\n";
1348  return false;
1349  }
1350 
1351  } else {
1352  int delta = reader->get_first_vertex() - _min_vertex;
1353  for (int vi = 0; vi < num_vertices; ++vi) {
1354  GLVertex *v0 = &_vertices[vi + delta];
1355  gl_draw_point(_c, v0);
1356  }
1357  }
1358 
1359  return true;
1360 }
1361 
1362 ////////////////////////////////////////////////////////////////////
1363 // Function: TinyGraphicsStateGuardian::end_draw_primitives()
1364 // Access: Public, Virtual
1365 // Description: Called after a sequence of draw_primitive()
1366 // functions are called, this should do whatever cleanup
1367 // is appropriate.
1368 ////////////////////////////////////////////////////////////////////
1371 
1372 #ifdef DO_PSTATS
1373  _pixel_count_white_untextured_pcollector.add_level(pixel_count_white_untextured);
1374  _pixel_count_flat_untextured_pcollector.add_level(pixel_count_flat_untextured);
1375  _pixel_count_smooth_untextured_pcollector.add_level(pixel_count_smooth_untextured);
1376  _pixel_count_white_textured_pcollector.add_level(pixel_count_white_textured);
1377  _pixel_count_flat_textured_pcollector.add_level(pixel_count_flat_textured);
1378  _pixel_count_smooth_textured_pcollector.add_level(pixel_count_smooth_textured);
1379  _pixel_count_white_perspective_pcollector.add_level(pixel_count_white_perspective);
1380  _pixel_count_flat_perspective_pcollector.add_level(pixel_count_flat_perspective);
1381  _pixel_count_smooth_perspective_pcollector.add_level(pixel_count_smooth_perspective);
1382  _pixel_count_smooth_multitex2_pcollector.add_level(pixel_count_smooth_multitex2);
1383  _pixel_count_smooth_multitex3_pcollector.add_level(pixel_count_smooth_multitex3);
1384 #endif // DO_PSTATS
1385 
1387 }
1388 
1389 ////////////////////////////////////////////////////////////////////
1390 // Function: TinyGraphicsStateGuardian::framebuffer_copy_to_texture
1391 // Access: Public, Virtual
1392 // Description: Copy the pixels within the indicated display
1393 // region from the framebuffer into texture memory.
1394 //
1395 // If z > -1, it is the cube map index into which to
1396 // copy.
1397 ////////////////////////////////////////////////////////////////////
1399 framebuffer_copy_to_texture(Texture *tex, int view, int z,
1400  const DisplayRegion *dr,
1401  const RenderBuffer &rb) {
1402  nassertr(tex != NULL && dr != NULL, false);
1403 
1404  int xo, yo, w, h;
1405  dr->get_region_pixels_i(xo, yo, w, h);
1406 
1407  tex->setup_2d_texture(w, h, Texture::T_unsigned_byte, Texture::F_rgba);
1408 
1409  TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
1410  nassertr(tc != (TextureContext *)NULL, false);
1411  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
1412 
1413  GLTexture *gltex = &gtc->_gltex;
1414  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), 1)) {
1415  return false;
1416  }
1417  LColor border_color = tex->get_border_color();
1418  border_color = border_color.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
1419  gltex->border_color.v[0] = border_color[0];
1420  gltex->border_color.v[1] = border_color[1];
1421  gltex->border_color.v[2] = border_color[2];
1422  gltex->border_color.v[3] = border_color[3];
1423 
1424  PIXEL *ip = gltex->levels[0].pixmap + gltex->xsize * gltex->ysize;
1425  PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB;
1426  for (int y = 0; y < gltex->ysize; ++y) {
1427  ip -= gltex->xsize;
1428  memcpy(ip, fo, gltex->xsize * PSZB);
1429  fo += _c->zb->linesize / PSZB;
1430  }
1431 
1432  gtc->update_data_size_bytes(gltex->xsize * gltex->ysize * 4);
1433  gtc->mark_loaded();
1434  gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
1435 
1436  return true;
1437 }
1438 
1439 ////////////////////////////////////////////////////////////////////
1440 // Function: TinyGraphicsStateGuardian::framebuffer_copy_to_ram
1441 // Access: Public, Virtual
1442 // Description: Copy the pixels within the indicated display region
1443 // from the framebuffer into system memory, not texture
1444 // memory. Returns true on success, false on failure.
1445 //
1446 // This completely redefines the ram image of the
1447 // indicated texture.
1448 ////////////////////////////////////////////////////////////////////
1450 framebuffer_copy_to_ram(Texture *tex, int view, int z,
1451  const DisplayRegion *dr,
1452  const RenderBuffer &rb) {
1453  nassertr(tex != NULL && dr != NULL, false);
1454 
1455  int xo, yo, w, h;
1456  dr->get_region_pixels_i(xo, yo, w, h);
1457 
1458  Texture::TextureType texture_type;
1459  int z_size;
1460  if (z >= 0) {
1461  texture_type = Texture::TT_cube_map;
1462  z_size = 6;
1463  } else {
1464  texture_type = Texture::TT_2d_texture;
1465  z_size = 1;
1466  }
1467 
1468  Texture::ComponentType component_type = Texture::T_unsigned_byte;
1469  Texture::Format format = Texture::F_rgba;
1470 
1471  if (tex->get_x_size() != w || tex->get_y_size() != h ||
1472  tex->get_z_size() != z_size ||
1473  tex->get_component_type() != component_type ||
1474  tex->get_format() != format ||
1475  tex->get_texture_type() != texture_type) {
1476  // Re-setup the texture; its properties have changed.
1477  tex->setup_texture(texture_type, w, h, z_size,
1478  component_type, format);
1479  }
1480 
1481  nassertr(z < tex->get_z_size(), false);
1482 
1483  unsigned char *image_ptr = tex->modify_ram_image();
1484  size_t image_size = tex->get_ram_image_size();
1485  if (z >= 0 || view > 0) {
1486  image_size = tex->get_expected_ram_page_size();
1487  if (z >= 0) {
1488  image_ptr += z * image_size;
1489  }
1490  if (view > 0) {
1491  image_ptr += (view * tex->get_z_size()) * image_size;
1492  }
1493  }
1494 
1495  PIXEL *ip = (PIXEL *)(image_ptr + image_size);
1496  PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB;
1497  for (int y = 0; y < h; ++y) {
1498  ip -= w;
1499 #ifndef WORDS_BIGENDIAN
1500  // On a little-endian machine, we can copy the whole row at a time.
1501  memcpy(ip, fo, w * PSZB);
1502 #else
1503  // On a big-endian machine, we have to reverse the color-component order.
1504  const char *source = (const char *)fo;
1505  const char *stop = (const char *)fo + w * PSZB;
1506  char *dest = (char *)ip;
1507  while (source < stop) {
1508  char b = source[0];
1509  char g = source[1];
1510  char r = source[2];
1511  char a = source[3];
1512  dest[0] = a;
1513  dest[1] = r;
1514  dest[2] = g;
1515  dest[3] = b;
1516  dest += 4;
1517  source += 4;
1518  }
1519 #endif
1520  fo += _c->zb->linesize / PSZB;
1521  }
1522 
1523  return true;
1524 }
1525 
1526 ////////////////////////////////////////////////////////////////////
1527 // Function: TinyGraphicsStateGuardian::set_state_and_transform
1528 // Access: Public, Virtual
1529 // Description: Simultaneously resets the render state and the
1530 // transform state.
1531 //
1532 // This transform specified is the "internal" net
1533 // transform, already converted into the GSG's internal
1534 // coordinate space by composing it to
1535 // get_cs_transform(). (Previously, this used to be the
1536 // "external" net transform, with the assumption that
1537 // that GSG would convert it internally, but that is no
1538 // longer the case.)
1539 //
1540 // Special case: if (state==NULL), then the target
1541 // state is already stored in _target.
1542 ////////////////////////////////////////////////////////////////////
1545  const TransformState *transform) {
1546 #ifndef NDEBUG
1547  if (tinydisplay_cat.is_spam()) {
1548  tinydisplay_cat.spam()
1549  << "Setting GSG state to " << (void *)target << ":\n";
1550  target->write(tinydisplay_cat.spam(false), 2);
1551  transform->write(tinydisplay_cat.spam(false), 2);
1552  }
1553 #endif
1554 
1555  _state_pcollector.add_level(1);
1556  PStatTimer timer1(_draw_set_state_pcollector);
1557 
1558  if (transform != _internal_transform) {
1559  PStatTimer timer(_draw_set_state_transform_pcollector);
1560  _state_pcollector.add_level(1);
1561  _internal_transform = transform;
1562  do_issue_transform();
1563  }
1564 
1565  if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) {
1566  return;
1567  }
1568  _target_rs = target;
1569 
1570  int color_slot = ColorAttrib::get_class_slot();
1571  int color_scale_slot = ColorScaleAttrib::get_class_slot();
1572  if (_target_rs->get_attrib(color_slot) != _state_rs->get_attrib(color_slot) ||
1573  _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) ||
1574  !_state_mask.get_bit(color_slot) ||
1575  !_state_mask.get_bit(color_scale_slot)) {
1576  PStatTimer timer(_draw_set_state_color_pcollector);
1577  do_issue_color();
1578  do_issue_color_scale();
1579  _state_mask.set_bit(color_slot);
1580  _state_mask.set_bit(color_scale_slot);
1581  }
1582 
1583  int cull_face_slot = CullFaceAttrib::get_class_slot();
1584  if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) ||
1585  !_state_mask.get_bit(cull_face_slot)) {
1586  PStatTimer timer(_draw_set_state_cull_face_pcollector);
1587  do_issue_cull_face();
1588  _state_mask.set_bit(cull_face_slot);
1589  }
1590 
1591  int depth_offset_slot = DepthOffsetAttrib::get_class_slot();
1592  if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) ||
1593  !_state_mask.get_bit(depth_offset_slot)) {
1594  //PStatTimer timer(_draw_set_state_depth_offset_pcollector);
1595  do_issue_depth_offset();
1596  _state_mask.set_bit(depth_offset_slot);
1597  }
1598 
1599  int rescale_normal_slot = RescaleNormalAttrib::get_class_slot();
1600  if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) ||
1601  !_state_mask.get_bit(rescale_normal_slot)) {
1602  PStatTimer timer(_draw_set_state_rescale_normal_pcollector);
1603  do_issue_rescale_normal();
1604  _state_mask.set_bit(rescale_normal_slot);
1605  }
1606 
1607  int render_mode_slot = RenderModeAttrib::get_class_slot();
1608  if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) ||
1609  !_state_mask.get_bit(render_mode_slot)) {
1610  PStatTimer timer(_draw_set_state_render_mode_pcollector);
1611  do_issue_render_mode();
1612  _state_mask.set_bit(render_mode_slot);
1613  }
1614 
1615  int texture_slot = TextureAttrib::get_class_slot();
1616  if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
1617  !_state_mask.get_bit(texture_slot)) {
1618  PStatTimer timer(_draw_set_state_texture_pcollector);
1619  determine_target_texture();
1620  do_issue_texture();
1621  _state_mask.set_bit(texture_slot);
1622  }
1623 
1624  int material_slot = MaterialAttrib::get_class_slot();
1625  if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) ||
1626  !_state_mask.get_bit(material_slot)) {
1627  PStatTimer timer(_draw_set_state_material_pcollector);
1628  do_issue_material();
1629  _state_mask.set_bit(material_slot);
1630  }
1631 
1632  int light_slot = LightAttrib::get_class_slot();
1633  if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) ||
1634  !_state_mask.get_bit(light_slot)) {
1635  PStatTimer timer(_draw_set_state_light_pcollector);
1636  do_issue_light();
1637  _state_mask.set_bit(light_slot);
1638  }
1639 
1640  int scissor_slot = ScissorAttrib::get_class_slot();
1641  if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) ||
1642  !_state_mask.get_bit(scissor_slot)) {
1643  PStatTimer timer(_draw_set_state_scissor_pcollector);
1644  do_issue_scissor();
1645  _state_mask.set_bit(scissor_slot);
1646  }
1647 
1648  _state_rs = _target_rs;
1649 }
1650 
1651 ////////////////////////////////////////////////////////////////////
1652 // Function: TinyGraphicsStateGuardian::prepare_texture
1653 // Access: Public, Virtual
1654 // Description: Creates whatever structures the GSG requires to
1655 // represent the texture internally, and returns a
1656 // newly-allocated TextureContext object with this data.
1657 // It is the responsibility of the calling function to
1658 // later call release_texture() with this same pointer
1659 // (which will also delete the pointer).
1660 //
1661 // This function should not be called directly to
1662 // prepare a texture. Instead, call Texture::prepare().
1663 ////////////////////////////////////////////////////////////////////
1665 prepare_texture(Texture *tex, int view) {
1666  switch (tex->get_texture_type()) {
1667  case Texture::TT_1d_texture:
1668  case Texture::TT_2d_texture:
1669  // These are supported.
1670  break;
1671 
1672  default:
1673  // Anything else is not supported.
1674  tinydisplay_cat.info()
1675  << "Not loading texture " << tex->get_name() << ": "
1676  << tex->get_texture_type() << "\n";
1677  return NULL;
1678  }
1679 
1680  // Even though the texture might be compressed now, it might have an
1681  // available uncompressed version that we can load. So don't reject
1682  // it out-of-hand just because it's compressed.
1683  /*
1684  if (tex->get_ram_image_compression() != Texture::CM_off) {
1685  tinydisplay_cat.info()
1686  << "Not loading texture " << tex->get_name() << ": "
1687  << tex->get_ram_image_compression() << "\n";
1688  return NULL;
1689  }
1690  */
1691 
1692  TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex, view);
1693 
1694  return gtc;
1695 }
1696 
1697 ////////////////////////////////////////////////////////////////////
1698 // Function: TinyGraphicsStateGuardian::update_texture
1699 // Access: Public, Virtual
1700 // Description: Ensures that the current Texture data is refreshed
1701 // onto the GSG. This means updating the texture
1702 // properties and/or re-uploading the texture image, if
1703 // necessary. This should only be called within the
1704 // draw thread.
1705 //
1706 // If force is true, this function will not return until
1707 // the texture has been fully uploaded. If force is
1708 // false, the function may choose to upload a simple
1709 // version of the texture instead, if the texture is not
1710 // fully resident (and if get_incomplete_render() is
1711 // true).
1712 ////////////////////////////////////////////////////////////////////
1714 update_texture(TextureContext *tc, bool force) {
1715  apply_texture(tc);
1716 
1717  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
1718 
1719  GLTexture *gltex = &gtc->_gltex;
1720 
1721  if (gtc->was_image_modified() || gltex->num_levels == 0) {
1722  // If the texture image was modified, reload the texture.
1723  Texture *tex = gtc->get_texture();
1724  bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
1725  if (!okflag) {
1726  tinydisplay_cat.error()
1727  << "Could not load " << *tex << "\n";
1728  return false;
1729  }
1730  }
1731  gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
1732 
1733  return true;
1734 }
1735 
1736 ////////////////////////////////////////////////////////////////////
1737 // Function: TinyGraphicsStateGuardian::update_texture
1738 // Access: Public
1739 // Description: Ensures that the current Texture data is refreshed
1740 // onto the GSG. This means updating the texture
1741 // properties and/or re-uploading the texture image, if
1742 // necessary. This should only be called within the
1743 // draw thread.
1744 //
1745 // If force is true, this function will not return until
1746 // the texture has been fully uploaded. If force is
1747 // false, the function may choose to upload a simple
1748 // version of the texture instead, if the texture is not
1749 // fully resident (and if get_incomplete_render() is
1750 // true).
1751 ////////////////////////////////////////////////////////////////////
1753 update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmaps) {
1754  if (!update_texture(tc, force)) {
1755  return false;
1756  }
1757 
1758  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
1759  GLTexture *gltex = &gtc->_gltex;
1760 
1761  if (uses_mipmaps && gltex->num_levels <= 1) {
1762  // We don't have mipmaps, yet we are sampling with mipmaps.
1763  Texture *tex = gtc->get_texture();
1764  bool okflag = upload_texture(gtc, force, true);
1765  if (!okflag) {
1766  tinydisplay_cat.error()
1767  << "Could not load " << *tex << "\n";
1768  return false;
1769  }
1770  }
1771 
1772  _c->current_textures[stage_index] = gltex;
1773 
1774  ZTextureDef *texture_def = &_c->zb->current_textures[stage_index];
1775  texture_def->levels = gltex->levels;
1776  texture_def->s_max = gltex->s_max;
1777  texture_def->t_max = gltex->t_max;
1778 
1779  const V4 &bc = gltex->border_color;
1780  int r = (int)(bc.v[0] * (ZB_POINT_RED_MAX - ZB_POINT_RED_MIN)
1781  + ZB_POINT_RED_MIN);
1782  int g = (int)(bc.v[1] * (ZB_POINT_GREEN_MAX - ZB_POINT_GREEN_MIN)
1783  + ZB_POINT_GREEN_MIN);
1784  int b = (int)(bc.v[2] * (ZB_POINT_BLUE_MAX - ZB_POINT_BLUE_MIN)
1785  + ZB_POINT_BLUE_MIN);
1786  int a = (int)(bc.v[3] * (ZB_POINT_ALPHA_MAX - ZB_POINT_ALPHA_MIN)
1787  + ZB_POINT_ALPHA_MIN);
1788  texture_def->border_color = RGBA_TO_PIXEL(r, g, b, a);
1789 
1790  return true;
1791 }
1792 
1793 ////////////////////////////////////////////////////////////////////
1794 // Function: TinyGraphicsStateGuardian::release_texture
1795 // Access: Public, Virtual
1796 // Description: Frees the GL resources previously allocated for the
1797 // texture. This function should never be called
1798 // directly; instead, call Texture::release() (or simply
1799 // let the Texture destruct).
1800 ////////////////////////////////////////////////////////////////////
1803  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
1804 
1805  _texturing_state = 0; // just in case
1806 
1807  GLTexture *gltex = &gtc->_gltex;
1808  if (gltex->allocated_buffer != NULL) {
1809  nassertv(gltex->num_levels != 0);
1810  TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount);
1811  PANDA_FREE_ARRAY(gltex->allocated_buffer);
1812  gltex->allocated_buffer = NULL;
1813  gltex->total_bytecount = 0;
1814  gltex->num_levels = 0;
1815  } else {
1816  nassertv(gltex->num_levels == 0);
1817  }
1818 
1819  gtc->dequeue_lru();
1820 
1821  delete gtc;
1822 }
1823 
1824 ////////////////////////////////////////////////////////////////////
1825 // Function: TinyGraphicsStateGuardian::do_issue_light
1826 // Access: Protected, Virtual
1827 // Description:
1828 ////////////////////////////////////////////////////////////////////
1831  // Initialize the current ambient light total and newly enabled
1832  // light list
1833  LColor cur_ambient_light(0.0f, 0.0f, 0.0f, 0.0f);
1834 
1835  int num_enabled = 0;
1836  int num_on_lights = 0;
1837 
1838  const LightAttrib *target_light = DCAST(LightAttrib, _target_rs->get_attrib_def(LightAttrib::get_class_slot()));
1839  if (display_cat.is_spam()) {
1840  display_cat.spam()
1841  << "do_issue_light: " << target_light << "\n";
1842  }
1843 
1844  // First, release all of the previously-assigned lights.
1845  clear_light_state();
1846 
1847  // Now, assign new lights.
1848  if (target_light != (LightAttrib *)NULL) {
1849  CPT(LightAttrib) new_light = target_light->filter_to_max(_max_lights);
1850  if (display_cat.is_spam()) {
1851  new_light->write(display_cat.spam(false), 2);
1852  }
1853 
1854  num_on_lights = new_light->get_num_on_lights();
1855  for (int li = 0; li < num_on_lights; li++) {
1856  NodePath light = new_light->get_on_light(li);
1857  nassertv(!light.is_empty());
1858  Light *light_obj = light.node()->as_light();
1859  nassertv(light_obj != (Light *)NULL);
1860 
1861  _lighting_enabled = true;
1862  _c->lighting_enabled = true;
1863 
1864  if (light_obj->get_type() == AmbientLight::get_class_type()) {
1865  // Accumulate all of the ambient lights together into one.
1866  cur_ambient_light += light_obj->get_color();
1867 
1868  } else {
1869  // Other kinds of lights each get their own GLLight object.
1870  light_obj->bind(this, light, num_enabled);
1871  num_enabled++;
1872 
1873  // Handle the diffuse color here, since all lights have this
1874  // property.
1875  GLLight *gl_light = _c->first_light;
1876  nassertv(gl_light != NULL);
1877  const LColor &diffuse = light_obj->get_color();
1878  gl_light->diffuse.v[0] = diffuse[0];
1879  gl_light->diffuse.v[1] = diffuse[1];
1880  gl_light->diffuse.v[2] = diffuse[2];
1881  gl_light->diffuse.v[3] = diffuse[3];
1882  }
1883  }
1884  }
1885 
1886  _c->ambient_light_model.v[0] = cur_ambient_light[0];
1887  _c->ambient_light_model.v[1] = cur_ambient_light[1];
1888  _c->ambient_light_model.v[2] = cur_ambient_light[2];
1889  _c->ambient_light_model.v[3] = cur_ambient_light[3];
1890 
1891  // Changing the lighting state means we need to reapply the
1892  // transform in begin_draw_primitives().
1893  _transform_stale = true;
1894 }
1895 
1896 ////////////////////////////////////////////////////////////////////
1897 // Function: TinyGraphicsStateGuardian::bind_light
1898 // Access: Public, Virtual
1899 // Description: Called the first time a particular light has been
1900 // bound to a given id within a frame, this should set
1901 // up the associated hardware light with the light's
1902 // properties.
1903 ////////////////////////////////////////////////////////////////////
1905 bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
1906  pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
1907  GLLight *gl_light = &(*lookup.first).second;
1908  if (lookup.second) {
1909  // It's a brand new light. Define it.
1910  memset(gl_light, 0, sizeof(GLLight));
1911 
1912  const LColor &specular = light_obj->get_specular_color();
1913  gl_light->specular.v[0] = specular[0];
1914  gl_light->specular.v[1] = specular[1];
1915  gl_light->specular.v[2] = specular[2];
1916  gl_light->specular.v[3] = specular[3];
1917 
1918  // Position needs to specify x, y, z, and w
1919  // w == 1 implies non-infinite position
1920  CPT(TransformState) render_transform =
1921  _cs_transform->compose(_scene_setup->get_world_transform());
1922 
1923  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
1924  CPT(TransformState) net_transform = render_transform->compose(transform);
1925 
1926  LPoint3 pos = light_obj->get_point() * net_transform->get_mat();
1927  gl_light->position.v[0] = pos[0];
1928  gl_light->position.v[1] = pos[1];
1929  gl_light->position.v[2] = pos[2];
1930  gl_light->position.v[3] = 1.0f;
1931 
1932  // Exponent == 0 implies uniform light distribution
1933  gl_light->spot_exponent = 0.0f;
1934 
1935  // Cutoff == 180 means uniform point light source
1936  gl_light->spot_cutoff = 180.0f;
1937 
1938  const LVecBase3 &att = light_obj->get_attenuation();
1939  gl_light->attenuation[0] = att[0];
1940  gl_light->attenuation[1] = att[1];
1941  gl_light->attenuation[2] = att[2];
1942  }
1943 
1944  nassertv(gl_light->next == NULL);
1945 
1946  // Add it to the linked list of active lights.
1947  gl_light->next = _c->first_light;
1948  _c->first_light = gl_light;
1949 }
1950 
1951 ////////////////////////////////////////////////////////////////////
1952 // Function: TinyGraphicsStateGuardian::bind_light
1953 // Access: Public, Virtual
1954 // Description: Called the first time a particular light has been
1955 // bound to a given id within a frame, this should set
1956 // up the associated hardware light with the light's
1957 // properties.
1958 ////////////////////////////////////////////////////////////////////
1960 bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
1961  pair<Lights::iterator, bool> lookup = _dlights.insert(Lights::value_type(light, GLLight()));
1962  GLLight *gl_light = &(*lookup.first).second;
1963  if (lookup.second) {
1964  // It's a brand new light. Define it.
1965  memset(gl_light, 0, sizeof(GLLight));
1966 
1967  const LColor &specular = light_obj->get_specular_color();
1968  gl_light->specular.v[0] = specular[0];
1969  gl_light->specular.v[1] = specular[1];
1970  gl_light->specular.v[2] = specular[2];
1971  gl_light->specular.v[3] = specular[3];
1972 
1973  // Position needs to specify x, y, z, and w
1974  // w == 0 implies light is at infinity
1975  CPT(TransformState) render_transform =
1976  _cs_transform->compose(_scene_setup->get_world_transform());
1977 
1978  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
1979  CPT(TransformState) net_transform = render_transform->compose(transform);
1980 
1981  LVector3 dir = light_obj->get_direction() * net_transform->get_mat();
1982  dir.normalize();
1983  gl_light->position.v[0] = -dir[0];
1984  gl_light->position.v[1] = -dir[1];
1985  gl_light->position.v[2] = -dir[2];
1986  gl_light->position.v[3] = 0.0f;
1987 
1988  gl_light->norm_position.v[0] = -dir[0];
1989  gl_light->norm_position.v[1] = -dir[1];
1990  gl_light->norm_position.v[2] = -dir[2];
1991  gl_V3_Norm(&gl_light->norm_position);
1992 
1993  // Exponent == 0 implies uniform light distribution
1994  gl_light->spot_exponent = 0.0f;
1995 
1996  // Cutoff == 180 means uniform point light source
1997  gl_light->spot_cutoff = 180.0f;
1998 
1999  // Default attenuation values (only spotlight and point light can
2000  // modify these)
2001  gl_light->attenuation[0] = 1.0f;
2002  gl_light->attenuation[1] = 0.0f;
2003  gl_light->attenuation[2] = 0.0f;
2004  }
2005 
2006  nassertv(gl_light->next == NULL);
2007 
2008  // Add it to the linked list of active lights.
2009  gl_light->next = _c->first_light;
2010  _c->first_light = gl_light;
2011 }
2012 
2013 ////////////////////////////////////////////////////////////////////
2014 // Function: TinyGraphicsStateGuardian::bind_light
2015 // Access: Public, Virtual
2016 // Description: Called the first time a particular light has been
2017 // bound to a given id within a frame, this should set
2018 // up the associated hardware light with the light's
2019 // properties.
2020 ////////////////////////////////////////////////////////////////////
2022 bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
2023  pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
2024  GLLight *gl_light = &(*lookup.first).second;
2025  if (lookup.second) {
2026  // It's a brand new light. Define it.
2027  memset(gl_light, 0, sizeof(GLLight));
2028 
2029  const LColor &specular = light_obj->get_specular_color();
2030  gl_light->specular.v[0] = specular[0];
2031  gl_light->specular.v[1] = specular[1];
2032  gl_light->specular.v[2] = specular[2];
2033  gl_light->specular.v[3] = specular[3];
2034 
2035  Lens *lens = light_obj->get_lens();
2036  nassertv(lens != (Lens *)NULL);
2037 
2038  // Position needs to specify x, y, z, and w
2039  // w == 1 implies non-infinite position
2040  CPT(TransformState) render_transform =
2041  _cs_transform->compose(_scene_setup->get_world_transform());
2042 
2043  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
2044  CPT(TransformState) net_transform = render_transform->compose(transform);
2045 
2046  const LMatrix4 &light_mat = net_transform->get_mat();
2047  LPoint3 pos = lens->get_nodal_point() * light_mat;
2048  LVector3 dir = lens->get_view_vector() * light_mat;
2049  dir.normalize();
2050 
2051  gl_light->position.v[0] = pos[0];
2052  gl_light->position.v[1] = pos[1];
2053  gl_light->position.v[2] = pos[2];
2054  gl_light->position.v[3] = 1.0f;
2055 
2056  gl_light->spot_direction.v[0] = dir[0];
2057  gl_light->spot_direction.v[1] = dir[1];
2058  gl_light->spot_direction.v[2] = dir[2];
2059 
2060  gl_light->norm_spot_direction.v[0] = dir[0];
2061  gl_light->norm_spot_direction.v[1] = dir[1];
2062  gl_light->norm_spot_direction.v[2] = dir[2];
2063  gl_V3_Norm(&gl_light->norm_spot_direction);
2064 
2065  gl_light->spot_exponent = light_obj->get_exponent();
2066  gl_light->spot_cutoff = lens->get_hfov() * 0.5f;
2067 
2068  const LVecBase3 &att = light_obj->get_attenuation();
2069  gl_light->attenuation[0] = att[0];
2070  gl_light->attenuation[1] = att[1];
2071  gl_light->attenuation[2] = att[2];
2072  }
2073 
2074  nassertv(gl_light->next == NULL);
2075 
2076  // Add it to the linked list of active lights.
2077  gl_light->next = _c->first_light;
2078  _c->first_light = gl_light;
2079 }
2080 
2081 ////////////////////////////////////////////////////////////////////
2082 // Function: TinyGraphicsStateGuardian::do_issue_transform
2083 // Access: Protected
2084 // Description: Sends the indicated transform matrix to the graphics
2085 // API to be applied to future vertices.
2086 //
2087 // This transform is the internal_transform, already
2088 // converted into the GSG's internal coordinate system.
2089 ////////////////////////////////////////////////////////////////////
2090 void TinyGraphicsStateGuardian::
2091 do_issue_transform() {
2092  _transform_state_pcollector.add_level(1);
2093  _transform_stale = true;
2094 
2095  if (_auto_rescale_normal) {
2096  do_auto_rescale_normal();
2097  }
2098 }
2099 
2100 ////////////////////////////////////////////////////////////////////
2101 // Function: TinyGraphicsStateGuardian::do_issue_render_mode
2102 // Access: Protected
2103 // Description:
2104 ////////////////////////////////////////////////////////////////////
2105 void TinyGraphicsStateGuardian::
2106 do_issue_render_mode() {
2107  const RenderModeAttrib *target_render_mode = DCAST(RenderModeAttrib, _target_rs->get_attrib_def(RenderModeAttrib::get_class_slot()));
2108 
2109  _filled_flat = false;
2110 
2111  switch (target_render_mode->get_mode()) {
2112  case RenderModeAttrib::M_unchanged:
2113  case RenderModeAttrib::M_filled:
2114  _c->draw_triangle_front = gl_draw_triangle_fill;
2115  _c->draw_triangle_back = gl_draw_triangle_fill;
2116  break;
2117 
2118  case RenderModeAttrib::M_filled_flat:
2119  _c->draw_triangle_front = gl_draw_triangle_fill;
2120  _c->draw_triangle_back = gl_draw_triangle_fill;
2121  _filled_flat = true;
2122  break;
2123 
2124  case RenderModeAttrib::M_wireframe:
2125  _c->draw_triangle_front = gl_draw_triangle_line;
2126  _c->draw_triangle_back = gl_draw_triangle_line;
2127  break;
2128 
2129  case RenderModeAttrib::M_point:
2130  _c->draw_triangle_front = gl_draw_triangle_point;
2131  _c->draw_triangle_back = gl_draw_triangle_point;
2132  break;
2133 
2134  default:
2135  tinydisplay_cat.error()
2136  << "Unknown render mode " << (int)target_render_mode->get_mode() << endl;
2137  }
2138 }
2139 
2140 ////////////////////////////////////////////////////////////////////
2141 // Function: TinyGraphicsStateGuardian::do_issue_rescale_normal
2142 // Access: Protected
2143 // Description:
2144 ////////////////////////////////////////////////////////////////////
2145 void TinyGraphicsStateGuardian::
2146 do_issue_rescale_normal() {
2147  const RescaleNormalAttrib *target_rescale_normal = DCAST(RescaleNormalAttrib, _target_rs->get_attrib_def(RescaleNormalAttrib::get_class_slot()));
2148  RescaleNormalAttrib::Mode mode = target_rescale_normal->get_mode();
2149 
2150  _auto_rescale_normal = false;
2151 
2152  switch (mode) {
2153  case RescaleNormalAttrib::M_none:
2154  _c->normalize_enabled = false;
2155  _c->normal_scale = 1.0f;
2156  break;
2157 
2158  case RescaleNormalAttrib::M_normalize:
2159  _c->normalize_enabled = true;
2160  _c->normal_scale = 1.0f;
2161  break;
2162 
2163  case RescaleNormalAttrib::M_rescale:
2164  case RescaleNormalAttrib::M_auto:
2165  _auto_rescale_normal = true;
2166  do_auto_rescale_normal();
2167  break;
2168 
2169  default:
2170  tinydisplay_cat.error()
2171  << "Unknown rescale_normal mode " << (int)mode << endl;
2172  }
2173 }
2174 
2175 ////////////////////////////////////////////////////////////////////
2176 // Function: TinyGraphicsStateGuardian::do_issue_depth_offset
2177 // Access: Protected
2178 // Description:
2179 ////////////////////////////////////////////////////////////////////
2180 void TinyGraphicsStateGuardian::
2181 do_issue_depth_offset() {
2182  const DepthOffsetAttrib *target_depth_offset = DCAST(DepthOffsetAttrib, _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot()));
2183  int offset = target_depth_offset->get_offset();
2184  _c->zbias = offset;
2185 
2186  PN_stdfloat min_value = target_depth_offset->get_min_value();
2187  PN_stdfloat max_value = target_depth_offset->get_max_value();
2188  if (min_value == 0.0f && max_value == 1.0f) {
2189  _c->has_zrange = false;
2190  } else {
2191  _c->has_zrange = true;
2192  _c->zmin = min_value;
2193  _c->zrange = max_value - min_value;
2194  }
2195 }
2196 
2197 ////////////////////////////////////////////////////////////////////
2198 // Function: TinyGraphicsStateGuardian::do_issue_cull_face
2199 // Access: Protected
2200 // Description:
2201 ////////////////////////////////////////////////////////////////////
2202 void TinyGraphicsStateGuardian::
2203 do_issue_cull_face() {
2204  const CullFaceAttrib *target_cull_face = DCAST(CullFaceAttrib, _target_rs->get_attrib_def(CullFaceAttrib::get_class_slot()));
2205  CullFaceAttrib::Mode mode = target_cull_face->get_effective_mode();
2206 
2207  switch (mode) {
2208  case CullFaceAttrib::M_cull_none:
2209  _c->cull_face_enabled = false;
2210  break;
2211  case CullFaceAttrib::M_cull_clockwise:
2212  _c->cull_face_enabled = true;
2213  _c->cull_clockwise = true;
2214  break;
2215  case CullFaceAttrib::M_cull_counter_clockwise:
2216  _c->cull_face_enabled = true;
2217  _c->cull_clockwise = false;
2218  break;
2219  default:
2220  tinydisplay_cat.error()
2221  << "invalid cull face mode " << (int)mode << endl;
2222  break;
2223  }
2224 }
2225 
2226 ////////////////////////////////////////////////////////////////////
2227 // Function: TinyGraphicsStateGuardian::do_issue_material
2228 // Access: Protected
2229 // Description:
2230 ////////////////////////////////////////////////////////////////////
2231 void TinyGraphicsStateGuardian::
2232 do_issue_material() {
2233  static Material empty;
2234 
2235  const MaterialAttrib *target_material = DCAST(MaterialAttrib, _target_rs->get_attrib_def(MaterialAttrib::get_class_slot()));
2236 
2237  const Material *material;
2238  if (target_material == (MaterialAttrib *)NULL ||
2239  target_material->is_off()) {
2240  material = &empty;
2241  } else {
2242  material = target_material->get_material();
2243  }
2244 
2245  // Apply the material parameters to the front face.
2246  setup_material(&_c->materials[0], material);
2247 
2248  if (material->get_twoside()) {
2249  // Also apply the material parameters to the back face.
2250  setup_material(&_c->materials[1], material);
2251  }
2252 
2253  _c->local_light_model = material->get_local();
2254  _c->light_model_two_side = material->get_twoside();
2255 }
2256 
2257 ////////////////////////////////////////////////////////////////////
2258 // Function: TinyGraphicsStateGuardian::do_issue_texture
2259 // Access: Protected
2260 // Description:
2261 ////////////////////////////////////////////////////////////////////
2262 void TinyGraphicsStateGuardian::
2263 do_issue_texture() {
2264  _texturing_state = 0; // untextured
2265  _c->num_textures_enabled = 0;
2266 
2267  int num_stages = _target_texture->get_num_on_ff_stages();
2268  if (num_stages == 0) {
2269  // No texturing.
2270  return;
2271  }
2272  nassertv(num_stages <= MAX_TEXTURE_STAGES);
2273 
2274  bool all_replace = true;
2275  bool all_nearest = true;
2276  bool all_mipmap_nearest = true;
2277  bool any_mipmap = false;
2278  bool needs_general = false;
2279  Texture::QualityLevel best_quality_level = Texture::QL_default;
2280 
2281  for (int si = 0; si < num_stages; ++si) {
2282  TextureStage *stage = _target_texture->get_on_ff_stage(si);
2283  Texture *texture = _target_texture->get_on_texture(stage);
2284  nassertv(texture != (Texture *)NULL);
2285 
2286  int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
2287  TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
2288  if (tc == (TextureContext *)NULL) {
2289  // Something wrong with this texture; skip it.
2290  return;
2291  }
2292 
2293  // Get the sampler state that we are supposed to use.
2294  const SamplerState &sampler = _target_texture->get_on_sampler(stage);
2295 
2296  // Then, turn on the current texture mode.
2297  if (!update_texture(tc, false, si, sampler.uses_mipmaps())) {
2298  return;
2299  }
2300 
2301  // M_replace means M_replace; anything else is treated the same as
2302  // M_modulate.
2303  if (stage->get_mode() != TextureStage::M_replace) {
2304  all_replace = false;
2305  }
2306 
2307  Texture::QualityLevel quality_level = _texture_quality_override;
2308  if (quality_level == Texture::QL_default) {
2309  quality_level = texture->get_quality_level();
2310  }
2311  if (quality_level == Texture::QL_default) {
2312  quality_level = texture_quality_level;
2313  }
2314 
2315  best_quality_level = max(best_quality_level, quality_level);
2316 
2317  ZTextureDef *texture_def = &_c->zb->current_textures[si];
2318 
2319  // Fill in the filter func pointers. These may not actually get
2320  // called, if we decide below we can inline the filters.
2321  SamplerState::FilterType minfilter = sampler.get_minfilter();
2322  SamplerState::FilterType magfilter = sampler.get_magfilter();
2323 
2324  if (td_ignore_mipmaps && SamplerState::is_mipmap(minfilter)) {
2325  // Downgrade mipmaps.
2326  if (minfilter == SamplerState::FT_nearest_mipmap_nearest) {
2327  minfilter = SamplerState::FT_nearest;
2328  } else {
2329  minfilter = SamplerState::FT_linear;
2330  }
2331  }
2332 
2333  // Depending on this particular texture's quality level, we may
2334  // downgrade the requested filters.
2335  if (quality_level == Texture::QL_fastest) {
2336  minfilter = SamplerState::FT_nearest;
2337  magfilter = SamplerState::FT_nearest;
2338 
2339  } else if (quality_level == Texture::QL_normal) {
2340  if (SamplerState::is_mipmap(minfilter)) {
2341  minfilter = SamplerState::FT_nearest_mipmap_nearest;
2342  } else {
2343  minfilter = SamplerState::FT_nearest;
2344  }
2345  magfilter = SamplerState::FT_nearest;
2346 
2347  } else if (quality_level == Texture::QL_best) {
2348  minfilter = sampler.get_effective_minfilter();
2349  magfilter = sampler.get_effective_magfilter();
2350  }
2351 
2352  texture_def->tex_minfilter_func = get_tex_filter_func(minfilter);
2353  texture_def->tex_magfilter_func = get_tex_filter_func(magfilter);
2354 
2355  SamplerState::WrapMode wrap_u = sampler.get_wrap_u();
2356  SamplerState::WrapMode wrap_v = sampler.get_wrap_v();
2357  if (td_ignore_clamp) {
2358  wrap_u = SamplerState::WM_repeat;
2359  wrap_v = SamplerState::WM_repeat;
2360  }
2361 
2362  if (wrap_u != SamplerState::WM_repeat || wrap_v != SamplerState::WM_repeat) {
2363  // We have some nonstandard wrap mode. This will force the use
2364  // of the general texfilter mode.
2365  needs_general = true;
2366 
2367  // We need another level of indirection to implement the
2368  // different texcoord wrap modes. This means we will be using
2369  // the _impl function pointers, which are called by the toplevel
2370  // function.
2371 
2372  texture_def->tex_minfilter_func_impl = texture_def->tex_minfilter_func;
2373  texture_def->tex_magfilter_func_impl = texture_def->tex_magfilter_func;
2374 
2375  // Now assign the toplevel function pointer to do the
2376  // appropriate texture coordinate wrapping/clamping.
2377  texture_def->tex_minfilter_func = apply_wrap_general_minfilter;
2378  texture_def->tex_magfilter_func = apply_wrap_general_magfilter;
2379 
2380  texture_def->tex_wrap_u_func = get_tex_wrap_func(wrap_u);
2381  texture_def->tex_wrap_v_func = get_tex_wrap_func(wrap_v);
2382 
2383  // The following special cases are handled inline, rather than
2384  // relying on the above wrap function pointers.
2385  if (wrap_u && SamplerState::WM_border_color && wrap_v == SamplerState::WM_border_color) {
2386  texture_def->tex_minfilter_func = apply_wrap_border_color_minfilter;
2387  texture_def->tex_magfilter_func = apply_wrap_border_color_magfilter;
2388  } else if (wrap_u && SamplerState::WM_clamp && wrap_v == SamplerState::WM_clamp) {
2389  texture_def->tex_minfilter_func = apply_wrap_clamp_minfilter;
2390  texture_def->tex_magfilter_func = apply_wrap_clamp_magfilter;
2391  }
2392  }
2393 
2394  if (minfilter != SamplerState::FT_nearest || magfilter != SamplerState::FT_nearest) {
2395  all_nearest = false;
2396  }
2397 
2398  if (minfilter != SamplerState::FT_nearest_mipmap_nearest ||
2399  magfilter != SamplerState::FT_nearest) {
2400  all_mipmap_nearest = false;
2401  }
2402 
2403  if (SamplerState::is_mipmap(minfilter)) {
2404  any_mipmap = true;
2405  }
2406  }
2407 
2408  // Set a few state cache values.
2409  _c->num_textures_enabled = num_stages;
2410  _texture_replace = all_replace;
2411 
2412  _texturing_state = 2; // perspective (perspective-correct texturing)
2413  if (num_stages >= 3) {
2414  _texturing_state = 4; // multitex3
2415  } else if (num_stages == 2) {
2416  _texturing_state = 3; // multitex2
2417  } else if (!td_perspective_textures) {
2418  _texturing_state = 1; // textured (not perspective correct)
2419  }
2420 
2421  if (best_quality_level == Texture::QL_best) {
2422  // This is the most generic texture filter. Slow, but pretty.
2423  _texfilter_state = 2; // tgeneral
2424 
2425  if (!needs_general) {
2426  if (all_nearest) {
2427  // This case is inlined.
2428  _texfilter_state = 0; // tnearest
2429  } else if (all_mipmap_nearest) {
2430  // So is this case.
2431  _texfilter_state = 1; // tmipmap
2432  }
2433  }
2434 
2435  } else if (best_quality_level == Texture::QL_fastest) {
2436  // This is the cheapest texture filter. We disallow mipmaps and
2437  // perspective correctness.
2438  _texfilter_state = 0; // tnearest
2439  _texturing_state = 1; // textured (not perspective correct, no multitexture)
2440 
2441  } else {
2442  // This is the default texture filter. We use nearest sampling if
2443  // there are no mipmaps, and mipmap_nearest if there are any
2444  // mipmaps--these are the two inlined filters.
2445  _texfilter_state = 0; // tnearest
2446  if (any_mipmap) {
2447  _texfilter_state = 1; // tmipmap
2448  }
2449 
2450  if (needs_general) {
2451  // To support nonstandard texcoord wrapping etc, we need to
2452  // force the general texfilter mode.
2453  _texfilter_state = 2; // tgeneral
2454  }
2455  }
2456 }
2457 
2458 ////////////////////////////////////////////////////////////////////
2459 // Function: TinyGraphicsStateGuardian::do_issue_scissor
2460 // Access: Protected
2461 // Description:
2462 ////////////////////////////////////////////////////////////////////
2463 void TinyGraphicsStateGuardian::
2464 do_issue_scissor() {
2465  const ScissorAttrib *target_scissor = DCAST(ScissorAttrib, _target_rs->get_attrib_def(ScissorAttrib::get_class_slot()));
2466  const LVecBase4 &frame = target_scissor->get_frame();
2467  set_scissor(frame[0], frame[1], frame[2], frame[3]);
2468 }
2469 
2470 ////////////////////////////////////////////////////////////////////
2471 // Function: TinyGraphicsStateGuardian::set_scissor
2472 // Access: Private
2473 // Description: Sets up the scissor region, as a set of coordinates
2474 // relative to the current viewport.
2475 ////////////////////////////////////////////////////////////////////
2476 void TinyGraphicsStateGuardian::
2477 set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
2478  _c->scissor.left = left;
2479  _c->scissor.right = right;
2480  _c->scissor.bottom = bottom;
2481  _c->scissor.top = top;
2482  gl_eval_viewport(_c);
2483 
2484  PN_stdfloat xsize = right - left;
2485  PN_stdfloat ysize = top - bottom;
2486  PN_stdfloat xcenter = (left + right) - 1.0f;
2487  PN_stdfloat ycenter = (bottom + top) - 1.0f;
2488  if (xsize == 0.0f || ysize == 0.0f) {
2489  // If the scissor region is zero, nothing will be drawn anyway, so
2490  // don't worry about it.
2491  _scissor_mat = TransformState::make_identity();
2492  } else {
2493  _scissor_mat = TransformState::make_scale(LVecBase3(1.0f / xsize, 1.0f / ysize, 1.0f))->compose(TransformState::make_pos(LPoint3(-xcenter, -ycenter, 0.0f)));
2494  }
2495 }
2496 
2497 ////////////////////////////////////////////////////////////////////
2498 // Function: TinyGraphicsStateGuardian::apply_texture
2499 // Access: Protected
2500 // Description: Updates the graphics state with the current
2501 // information for this texture, and makes it the
2502 // current texture available for rendering.
2503 ////////////////////////////////////////////////////////////////////
2504 bool TinyGraphicsStateGuardian::
2505 apply_texture(TextureContext *tc) {
2506  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
2507 
2508  gtc->set_active(true);
2509  return true;
2510 }
2511 
2512 ////////////////////////////////////////////////////////////////////
2513 // Function: TinyGraphicsStateGuardian::upload_texture
2514 // Access: Protected
2515 // Description: Uploads the texture image to the graphics state.
2516 //
2517 // The return value is true if successful, or false if
2518 // the texture has no image.
2519 ////////////////////////////////////////////////////////////////////
2520 bool TinyGraphicsStateGuardian::
2521 upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) {
2522  Texture *tex = gtc->get_texture();
2523 
2524  if (_effective_incomplete_render && !force) {
2525  if (!tex->has_ram_image() && tex->might_have_ram_image() &&
2526  tex->has_simple_ram_image() &&
2527  !_loader.is_null()) {
2528  // If we don't have the texture data right now, go get it, but in
2529  // the meantime load a temporary simple image in its place.
2530  async_reload_texture(gtc);
2531  if (!tex->has_ram_image()) {
2532  if (gtc->was_simple_image_modified()) {
2533  return upload_simple_texture(gtc);
2534  }
2535  return true;
2536  }
2537  }
2538  }
2539 
2540  PStatTimer timer(_load_texture_pcollector);
2541  CPTA_uchar src_image = tex->get_uncompressed_ram_image();
2542  if (src_image.is_null()) {
2543  return false;
2544  }
2545 
2546 #ifdef DO_PSTATS
2547  _data_transferred_pcollector.add_level(tex->get_ram_image_size());
2548 #endif
2549  GLTexture *gltex = &gtc->_gltex;
2550 
2551  int num_levels = 1;
2552  if (uses_mipmaps) {
2553  num_levels = tex->get_expected_num_mipmap_levels();
2554  if (tex->get_num_ram_mipmap_images() < num_levels) {
2556  }
2557  }
2558 
2559  if (tinydisplay_cat.is_debug()) {
2560  tinydisplay_cat.debug()
2561  << "loading texture " << tex->get_name() << ", "
2562  << tex->get_x_size() << " x " << tex->get_y_size() << ", mipmaps = "
2563  << num_levels << ", uses_mipmaps = " << uses_mipmaps << "\n";
2564  }
2565 
2566  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), num_levels)) {
2567  return false;
2568  }
2569  LColor border_color = tex->get_border_color();
2570  border_color = border_color.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
2571  gltex->border_color.v[0] = border_color[0];
2572  gltex->border_color.v[1] = border_color[1];
2573  gltex->border_color.v[2] = border_color[2];
2574  gltex->border_color.v[3] = border_color[3];
2575 
2576  int bytecount = 0;
2577  int xsize = gltex->xsize;
2578  int ysize = gltex->ysize;
2579 
2580  for (int level = 0; level < gltex->num_levels; ++level) {
2581  ZTextureLevel *dest = &gltex->levels[level];
2582 
2583  switch (tex->get_format()) {
2584  case Texture::F_rgb:
2585  case Texture::F_rgb5:
2586  case Texture::F_rgb8:
2587  case Texture::F_rgb12:
2588  case Texture::F_rgb332:
2589  copy_rgb_image(dest, xsize, ysize, gtc, level);
2590  break;
2591 
2592  case Texture::F_rgba:
2593  case Texture::F_rgbm:
2594  case Texture::F_rgba4:
2595  case Texture::F_rgba5:
2596  case Texture::F_rgba8:
2597  case Texture::F_rgba12:
2598  case Texture::F_rgba16:
2599  case Texture::F_rgba32:
2600  copy_rgba_image(dest, xsize, ysize, gtc, level);
2601  break;
2602 
2603  case Texture::F_luminance:
2604  copy_lum_image(dest, xsize, ysize, gtc, level);
2605  break;
2606 
2607  case Texture::F_red:
2608  copy_one_channel_image(dest, xsize, ysize, gtc, level, 0);
2609  break;
2610 
2611  case Texture::F_green:
2612  copy_one_channel_image(dest, xsize, ysize, gtc, level, 1);
2613  break;
2614 
2615  case Texture::F_blue:
2616  copy_one_channel_image(dest, xsize, ysize, gtc, level, 2);
2617  break;
2618 
2619  case Texture::F_alpha:
2620  copy_alpha_image(dest, xsize, ysize, gtc, level);
2621  break;
2622 
2623  case Texture::F_luminance_alphamask:
2624  case Texture::F_luminance_alpha:
2625  copy_la_image(dest, xsize, ysize, gtc, level);
2626  break;
2627 
2628  default:
2629  tinydisplay_cat.error()
2630  << "Unsupported texture format "
2631  << tex->get_format() << "!\n";
2632  return false;
2633  }
2634 
2635  bytecount += xsize * ysize * 4;
2636  xsize = max(xsize >> 1, 1);
2637  ysize = max(ysize >> 1, 1);
2638  }
2639 
2640  gtc->update_data_size_bytes(bytecount);
2641 
2642  get_engine()->texture_uploaded(tex);
2643  gtc->mark_loaded();
2644 
2645  return true;
2646 }
2647 
2648 ////////////////////////////////////////////////////////////////////
2649 // Function: TinyGraphicsStateGuardian::upload_simple_texture
2650 // Access: Protected
2651 // Description: This is used as a standin for upload_texture
2652 // when the texture in question is unavailable (e.g. it
2653 // hasn't yet been loaded from disk). Until the texture
2654 // image itself becomes available, we will render the
2655 // texture's "simple" image--a sharply reduced version
2656 // of the same texture.
2657 ////////////////////////////////////////////////////////////////////
2658 bool TinyGraphicsStateGuardian::
2659 upload_simple_texture(TinyTextureContext *gtc) {
2660  PStatTimer timer(_load_texture_pcollector);
2661  Texture *tex = gtc->get_texture();
2662  nassertr(tex != (Texture *)NULL, false);
2663 
2664  const unsigned char *image_ptr = tex->get_simple_ram_image();
2665  if (image_ptr == (const unsigned char *)NULL) {
2666  return false;
2667  }
2668 
2669  size_t image_size = tex->get_simple_ram_image_size();
2670  int width = tex->get_simple_x_size();
2671  int height = tex->get_simple_y_size();
2672 
2673 #ifdef DO_PSTATS
2674  _data_transferred_pcollector.add_level(image_size);
2675 #endif
2676  GLTexture *gltex = &gtc->_gltex;
2677 
2678  if (tinydisplay_cat.is_debug()) {
2679  tinydisplay_cat.debug()
2680  << "loading simple image for " << tex->get_name() << "\n";
2681  }
2682 
2683  if (!setup_gltex(gltex, width, height, 1)) {
2684  return false;
2685  }
2686  LColor border_color = tex->get_border_color();
2687  border_color = border_color.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
2688  gltex->border_color.v[0] = border_color[0];
2689  gltex->border_color.v[1] = border_color[1];
2690  gltex->border_color.v[2] = border_color[2];
2691  gltex->border_color.v[3] = border_color[3];
2692 
2693  ZTextureLevel *dest = &gltex->levels[0];
2694  memcpy(dest->pixmap, image_ptr, image_size);
2695 
2696  gtc->mark_simple_loaded();
2697 
2698  return true;
2699 }
2700 
2701 ////////////////////////////////////////////////////////////////////
2702 // Function: TinyGraphicsStateGuardian::setup_gltex
2703 // Access: Private
2704 // Description: Sets the GLTexture size, bits, and masks
2705 // appropriately, and allocates space for a pixmap.
2706 // Does not fill the pixmap contents. Returns true if
2707 // the texture is a valid size, false otherwise.
2708 ////////////////////////////////////////////////////////////////////
2709 bool TinyGraphicsStateGuardian::
2710 setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
2711  int s_bits = get_tex_shift(x_size);
2712  int t_bits = get_tex_shift(y_size);
2713 
2714  if (s_bits < 0 || t_bits < 0) {
2715  tinydisplay_cat.error()
2716  << "Texture size " << x_size << 'x' << y_size
2717  << " unsupported: dimensions must be power of two"
2718  << " and smaller than " << _max_texture_dimension << '\n';
2719  return false;
2720  }
2721 
2722  num_levels = min(num_levels, MAX_MIPMAP_LEVELS);
2723 
2724  gltex->xsize = x_size;
2725  gltex->ysize = y_size;
2726 
2727  gltex->s_max = 1 << (s_bits + ZB_POINT_ST_FRAC_BITS);
2728  gltex->t_max = 1 << (t_bits + ZB_POINT_ST_FRAC_BITS);
2729 
2730  gltex->num_levels = num_levels;
2731 
2732  // We allocate one big buffer, large enough to include all the
2733  // mipmap levels, and index into that buffer for each level. This
2734  // cuts down on the number of individual alloc calls we have to make
2735  // for each texture.
2736  int total_bytecount = 0;
2737 
2738  // Count up the total bytes required for all mipmap levels.
2739  {
2740  int x = x_size;
2741  int y = y_size;
2742  for (int level = 0; level < num_levels; ++level) {
2743  int bytecount = x * y * 4;
2744  total_bytecount += bytecount;
2745  x = max((x >> 1), 1);
2746  y = max((y >> 1), 1);
2747  }
2748  }
2749 
2750  if (gltex->total_bytecount != total_bytecount) {
2751  if (gltex->allocated_buffer != NULL) {
2752  PANDA_FREE_ARRAY(gltex->allocated_buffer);
2753  TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount);
2754  }
2755  gltex->allocated_buffer = PANDA_MALLOC_ARRAY(total_bytecount);
2756  gltex->total_bytecount = total_bytecount;
2757  TinyTextureContext::get_class_type().inc_memory_usage(TypeHandle::MC_array, total_bytecount);
2758  }
2759 
2760  char *next_buffer = (char *)gltex->allocated_buffer;
2761  char *end_of_buffer = next_buffer + total_bytecount;
2762 
2763  int level = 0;
2764  ZTextureLevel *dest = NULL;
2765  while (level < num_levels) {
2766  dest = &gltex->levels[level];
2767  int bytecount = x_size * y_size * 4;
2768  dest->pixmap = (PIXEL *)next_buffer;
2769  next_buffer += bytecount;
2770  nassertr(next_buffer <= end_of_buffer, false);
2771 
2772  dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
2773  dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
2774  dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level);
2775  dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level);
2776 
2777  x_size = max((x_size >> 1), 1);
2778  y_size = max((y_size >> 1), 1);
2779  s_bits = max(s_bits - 1, 0);
2780  t_bits = max(t_bits - 1, 0);
2781 
2782  ++level;
2783  }
2784 
2785  // Fill out the remaining mipmap arrays with copies of the last
2786  // level, so we don't have to be concerned with running off the end
2787  // of this array while scanning out triangles.
2788  while (level < MAX_MIPMAP_LEVELS) {
2789  gltex->levels[level] = *dest;
2790  ++level;
2791  }
2792 
2793  return true;
2794 }
2795 
2796 ////////////////////////////////////////////////////////////////////
2797 // Function: TinyGraphicsStateGuardian::get_tex_shift
2798 // Access: Private
2799 // Description: Calculates the bit shift count, such that (1 << shift)
2800 // == size. Returns -1 if the size is not a power of 2
2801 // or is larger than our largest allowable size.
2802 ////////////////////////////////////////////////////////////////////
2803 int TinyGraphicsStateGuardian::
2804 get_tex_shift(int orig_size) {
2805  if ((orig_size & (orig_size - 1)) != 0) {
2806  // Not a power of 2.
2807  return -1;
2808  }
2809  if (orig_size > _max_texture_dimension) {
2810  return -1;
2811  }
2812 
2813  return count_bits_in_word((unsigned int)orig_size - 1);
2814 }
2815 
2816 ////////////////////////////////////////////////////////////////////
2817 // Function: TinyGraphicsStateGuardian::copy_lum_image
2818 // Access: Private, Static
2819 // Description: Copies and scales the one-channel luminance image
2820 // from the texture into the indicated ZTexture pixmap.
2821 ////////////////////////////////////////////////////////////////////
2822 void TinyGraphicsStateGuardian::
2823 copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2824  Texture *tex = gtc->get_texture();
2825  nassertv(tex->get_num_components() == 1);
2826  nassertv(tex->get_expected_mipmap_x_size(level) == xsize &&
2827  tex->get_expected_mipmap_y_size(level) == ysize);
2828 
2829  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2830  nassertv(!src_image.is_null());
2831  const unsigned char *src = src_image.p();
2832  size_t view_size = tex->get_ram_mipmap_view_size(level);
2833  src += view_size * gtc->get_view();
2834 
2835  // Component width, and offset to the high-order byte.
2836  int cw = tex->get_component_width();
2837 #ifdef WORDS_BIGENDIAN
2838  // Big-endian: the high-order byte is always first.
2839  static const int co = 0;
2840 #else
2841  // Little-endian: the high-order byte is last.
2842  int co = cw - 1;
2843 #endif
2844 
2845  unsigned int *dpix = (unsigned int *)dest->pixmap;
2846  nassertv(dpix != NULL);
2847  const unsigned char *spix = src;
2848  int pixel_count = xsize * ysize;
2849  while (pixel_count-- > 0) {
2850  *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], 0xff);
2851  ++dpix;
2852  spix += cw;
2853  }
2854 }
2855 
2856 ////////////////////////////////////////////////////////////////////
2857 // Function: TinyGraphicsStateGuardian::copy_alpha_image
2858 // Access: Private, Static
2859 // Description: Copies and scales the one-channel alpha image
2860 // from the texture into the indicated ZTexture pixmap.
2861 ////////////////////////////////////////////////////////////////////
2862 void TinyGraphicsStateGuardian::
2863 copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2864  Texture *tex = gtc->get_texture();
2865  nassertv(tex->get_num_components() == 1);
2866 
2867  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2868  nassertv(!src_image.is_null());
2869  const unsigned char *src = src_image.p();
2870  size_t view_size = tex->get_ram_mipmap_view_size(level);
2871  src += view_size * gtc->get_view();
2872 
2873  // Component width, and offset to the high-order byte.
2874  int cw = tex->get_component_width();
2875 #ifdef WORDS_BIGENDIAN
2876  // Big-endian: the high-order byte is always first.
2877  static const int co = 0;
2878 #else
2879  // Little-endian: the high-order byte is last.
2880  int co = cw - 1;
2881 #endif
2882 
2883  unsigned int *dpix = (unsigned int *)dest->pixmap;
2884  nassertv(dpix != NULL);
2885  const unsigned char *spix = src;
2886  int pixel_count = xsize * ysize;
2887  while (pixel_count-- > 0) {
2888  *dpix = RGBA8_TO_PIXEL(0xff, 0xff, 0xff, spix[co]);
2889  ++dpix;
2890  spix += cw;
2891  }
2892 }
2893 
2894 ////////////////////////////////////////////////////////////////////
2895 // Function: TinyGraphicsStateGuardian::copy_one_channel_image
2896 // Access: Private, Static
2897 // Description: Copies and scales the one-channel image (with a
2898 // single channel, e.g. red, green, or blue) from
2899 // the texture into the indicated ZTexture pixmap.
2900 ////////////////////////////////////////////////////////////////////
2901 void TinyGraphicsStateGuardian::
2902 copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel) {
2903  Texture *tex = gtc->get_texture();
2904  nassertv(tex->get_num_components() == 1);
2905 
2906  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2907  nassertv(!src_image.is_null());
2908  const unsigned char *src = src_image.p();
2909  size_t view_size = tex->get_ram_mipmap_view_size(level);
2910  src += view_size * gtc->get_view();
2911 
2912  // Component width, and offset to the high-order byte.
2913  int cw = tex->get_component_width();
2914 #ifdef WORDS_BIGENDIAN
2915  // Big-endian: the high-order byte is always first.
2916  static const int co = 0;
2917 #else
2918  // Little-endian: the high-order byte is last.
2919  int co = cw - 1;
2920 #endif
2921 
2922  unsigned int *dpix = (unsigned int *)dest->pixmap;
2923  nassertv(dpix != NULL);
2924  const unsigned char *spix = src;
2925  int pixel_count = xsize * ysize;
2926 
2927  switch (channel) {
2928  case 0:
2929  while (pixel_count-- > 0) {
2930  *dpix = RGBA8_TO_PIXEL(spix[co], 0, 0, 0xff);
2931  ++dpix;
2932  spix += cw;
2933  }
2934  break;
2935 
2936  case 1:
2937  while (pixel_count-- > 0) {
2938  *dpix = RGBA8_TO_PIXEL(0, spix[co], 0, 0xff);
2939  ++dpix;
2940  spix += cw;
2941  }
2942  break;
2943 
2944  case 2:
2945  while (pixel_count-- > 0) {
2946  *dpix = RGBA8_TO_PIXEL(0, 0, spix[co], 0xff);
2947  ++dpix;
2948  spix += cw;
2949  }
2950  break;
2951 
2952  case 3:
2953  while (pixel_count-- > 0) {
2954  *dpix = RGBA8_TO_PIXEL(0, 0, 0, spix[co]);
2955  ++dpix;
2956  spix += cw;
2957  }
2958  break;
2959  }
2960 }
2961 
2962 ////////////////////////////////////////////////////////////////////
2963 // Function: TinyGraphicsStateGuardian::copy_la_image
2964 // Access: Private, Static
2965 // Description: Copies and scales the two-channel luminance-alpha
2966 // image from the texture into the indicated ZTexture
2967 // pixmap.
2968 ////////////////////////////////////////////////////////////////////
2969 void TinyGraphicsStateGuardian::
2970 copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2971  Texture *tex = gtc->get_texture();
2972  nassertv(tex->get_num_components() == 2);
2973 
2974  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2975  nassertv(!src_image.is_null());
2976  const unsigned char *src = src_image.p();
2977  size_t view_size = tex->get_ram_mipmap_view_size(level);
2978  src += view_size * gtc->get_view();
2979 
2980  // Component width, and offset to the high-order byte.
2981  int cw = tex->get_component_width();
2982 #ifdef WORDS_BIGENDIAN
2983  // Big-endian: the high-order byte is always first.
2984  static const int co = 0;
2985 #else
2986  // Little-endian: the high-order byte is last.
2987  int co = cw - 1;
2988 #endif
2989 
2990  unsigned int *dpix = (unsigned int *)dest->pixmap;
2991  nassertv(dpix != NULL);
2992  const unsigned char *spix = src;
2993  int pixel_count = xsize * ysize;
2994  int inc = 2 * cw;
2995  while (pixel_count-- > 0) {
2996  *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], spix[cw + co]);
2997  ++dpix;
2998  spix += inc;
2999  }
3000 }
3001 
3002 ////////////////////////////////////////////////////////////////////
3003 // Function: TinyGraphicsStateGuardian::copy_rgb_image
3004 // Access: Private, Static
3005 // Description: Copies and scales the three-channel RGB image from
3006 // the texture into the indicated ZTexture pixmap.
3007 ////////////////////////////////////////////////////////////////////
3008 void TinyGraphicsStateGuardian::
3009 copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
3010  Texture *tex = gtc->get_texture();
3011  nassertv(tex->get_num_components() == 3);
3012 
3013  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
3014  nassertv(!src_image.is_null());
3015  const unsigned char *src = src_image.p();
3016  size_t view_size = tex->get_ram_mipmap_view_size(level);
3017  src += view_size * gtc->get_view();
3018 
3019  // Component width, and offset to the high-order byte.
3020  int cw = tex->get_component_width();
3021 #ifdef WORDS_BIGENDIAN
3022  // Big-endian: the high-order byte is always first.
3023  static const int co = 0;
3024 #else
3025  // Little-endian: the high-order byte is last.
3026  int co = cw - 1;
3027 #endif
3028 
3029  unsigned int *dpix = (unsigned int *)dest->pixmap;
3030  nassertv(dpix != NULL);
3031  const unsigned char *spix = src;
3032  int pixel_count = xsize * ysize;
3033  int inc = 3 * cw;
3034  while (pixel_count-- > 0) {
3035  *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], 0xff);
3036  ++dpix;
3037  spix += inc;
3038  }
3039 }
3040 
3041 ////////////////////////////////////////////////////////////////////
3042 // Function: TinyGraphicsStateGuardian::copy_rgba_image
3043 // Access: Private, Static
3044 // Description: Copies and scales the four-channel RGBA image from
3045 // the texture into the indicated ZTexture pixmap.
3046 ////////////////////////////////////////////////////////////////////
3047 void TinyGraphicsStateGuardian::
3048 copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
3049  Texture *tex = gtc->get_texture();
3050  nassertv(tex->get_num_components() == 4);
3051 
3052  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
3053  nassertv(!src_image.is_null());
3054  const unsigned char *src = src_image.p();
3055  size_t view_size = tex->get_ram_mipmap_view_size(level);
3056  src += view_size * gtc->get_view();
3057 
3058  // Component width, and offset to the high-order byte.
3059  int cw = tex->get_component_width();
3060 #ifdef WORDS_BIGENDIAN
3061  // Big-endian: the high-order byte is always first.
3062  static const int co = 0;
3063 #else
3064  // Little-endian: the high-order byte is last.
3065  int co = cw - 1;
3066 #endif
3067 
3068  unsigned int *dpix = (unsigned int *)dest->pixmap;
3069  nassertv(dpix != NULL);
3070  const unsigned char *spix = src;
3071  int pixel_count = xsize * ysize;
3072  int inc = 4 * cw;
3073  while (pixel_count-- > 0) {
3074  *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], spix[cw + cw + cw + co]);
3075  ++dpix;
3076  spix += inc;
3077  }
3078 }
3079 
3080 ////////////////////////////////////////////////////////////////////
3081 // Function: TinyGraphicsStateGuardian::setup_material
3082 // Access: Private
3083 // Description: Applies the desired parametesr to the indicated
3084 // GLMaterial object.
3085 ////////////////////////////////////////////////////////////////////
3086 void TinyGraphicsStateGuardian::
3087 setup_material(GLMaterial *gl_material, const Material *material) {
3088  const LColor &specular = material->get_specular();
3089  gl_material->specular.v[0] = specular[0];
3090  gl_material->specular.v[1] = specular[1];
3091  gl_material->specular.v[2] = specular[2];
3092  gl_material->specular.v[3] = specular[3];
3093 
3094  const LColor &emission = material->get_emission();
3095  gl_material->emission.v[0] = emission[0];
3096  gl_material->emission.v[1] = emission[1];
3097  gl_material->emission.v[2] = emission[2];
3098  gl_material->emission.v[3] = emission[3];
3099 
3100  gl_material->shininess = material->get_shininess();
3101  gl_material->shininess_i = (int)((material->get_shininess() / 128.0f) * SPECULAR_BUFFER_RESOLUTION);
3102 
3103  _color_material_flags = CMF_ambient | CMF_diffuse;
3104 
3105  if (material->has_ambient()) {
3106  const LColor &ambient = material->get_ambient();
3107  gl_material->ambient.v[0] = ambient[0];
3108  gl_material->ambient.v[1] = ambient[1];
3109  gl_material->ambient.v[2] = ambient[2];
3110  gl_material->ambient.v[3] = ambient[3];
3111 
3112  _color_material_flags &= ~CMF_ambient;
3113  }
3114 
3115  if (material->has_diffuse()) {
3116  const LColor &diffuse = material->get_diffuse();
3117  gl_material->diffuse.v[0] = diffuse[0];
3118  gl_material->diffuse.v[1] = diffuse[1];
3119  gl_material->diffuse.v[2] = diffuse[2];
3120  gl_material->diffuse.v[3] = diffuse[3];
3121 
3122  _color_material_flags &= ~CMF_diffuse;
3123  }
3124 }
3125 
3126 ////////////////////////////////////////////////////////////////////
3127 // Function: TinyGraphicsStateGuardian::do_auto_rescale_normal
3128 // Access: Protected
3129 // Description: Sets the state to either rescale or normalize the
3130 // normals according to the current transform.
3131 ////////////////////////////////////////////////////////////////////
3132 void TinyGraphicsStateGuardian::
3133 do_auto_rescale_normal() {
3134  if (_internal_transform->has_uniform_scale()) {
3135  // There's a uniform scale; rescale the normals uniformly.
3136  _c->normalize_enabled = false;
3137  _c->normal_scale = _internal_transform->get_uniform_scale();
3138 
3139  } else {
3140  // If there's a non-uniform scale, normalize everything.
3141  _c->normalize_enabled = true;
3142  _c->normal_scale = 1.0f;
3143  }
3144 }
3145 
3146 ////////////////////////////////////////////////////////////////////
3147 // Function: TinyGraphicsStateGuardian::load_matrix
3148 // Access: Private, Static
3149 // Description: Copies the Panda matrix stored in the indicated
3150 // TransformState object into the indicated TinyGL
3151 // matrix.
3152 ////////////////////////////////////////////////////////////////////
3153 void TinyGraphicsStateGuardian::
3154 load_matrix(M4 *matrix, const TransformState *transform) {
3155  const LMatrix4 &pm = transform->get_mat();
3156  for (int i = 0; i < 4; ++i) {
3157  matrix->m[0][i] = pm.get_cell(i, 0);
3158  matrix->m[1][i] = pm.get_cell(i, 1);
3159  matrix->m[2][i] = pm.get_cell(i, 2);
3160  matrix->m[3][i] = pm.get_cell(i, 3);
3161  }
3162 }
3163 
3164 ////////////////////////////////////////////////////////////////////
3165 // Function: TinyGraphicsStateGuardian::get_color_blend_op
3166 // Access: Private, Static
3167 // Description: Returns the integer element of store_pixel_funcs (as
3168 // defined by store_pixel.py) that corresponds to the
3169 // indicated ColorBlendAttrib operand code.
3170 ////////////////////////////////////////////////////////////////////
3171 int TinyGraphicsStateGuardian::
3172 get_color_blend_op(ColorBlendAttrib::Operand operand) {
3173  switch (operand) {
3174  case ColorBlendAttrib::O_zero:
3175  return 0;
3176  case ColorBlendAttrib::O_one:
3177  return 1;
3178  case ColorBlendAttrib::O_incoming_color:
3179  return 2;
3180  case ColorBlendAttrib::O_one_minus_incoming_color:
3181  return 3;
3182  case ColorBlendAttrib::O_fbuffer_color:
3183  return 4;
3184  case ColorBlendAttrib::O_one_minus_fbuffer_color:
3185  return 5;
3186  case ColorBlendAttrib::O_incoming_alpha:
3187  return 6;
3188  case ColorBlendAttrib::O_one_minus_incoming_alpha:
3189  return 7;
3190  case ColorBlendAttrib::O_fbuffer_alpha:
3191  return 8;
3192  case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
3193  return 9;
3194  case ColorBlendAttrib::O_constant_color:
3195  return 10;
3196  case ColorBlendAttrib::O_one_minus_constant_color:
3197  return 11;
3198  case ColorBlendAttrib::O_constant_alpha:
3199  return 12;
3200  case ColorBlendAttrib::O_one_minus_constant_alpha:
3201  return 13;
3202 
3203  case ColorBlendAttrib::O_incoming_color_saturate:
3204  return 1;
3205 
3206  case ColorBlendAttrib::O_color_scale:
3207  return 10;
3208  case ColorBlendAttrib::O_one_minus_color_scale:
3209  return 11;
3210  case ColorBlendAttrib::O_alpha_scale:
3211  return 12;
3212  case ColorBlendAttrib::O_one_minus_alpha_scale:
3213  return 13;
3214  }
3215  return 0;
3216 }
3217 
3218 ////////////////////////////////////////////////////////////////////
3219 // Function: TinyGraphicsStateGuardian::get_tex_filter_func
3220 // Access: Private, Static
3221 // Description: Returns the pointer to the appropriate filter
3222 // function according to the texture's filter type.
3223 ////////////////////////////////////////////////////////////////////
3224 ZB_lookupTextureFunc TinyGraphicsStateGuardian::
3225 get_tex_filter_func(SamplerState::FilterType filter) {
3226  switch (filter) {
3227  case SamplerState::FT_nearest:
3228  return &lookup_texture_nearest;
3229 
3230  case SamplerState::FT_linear:
3231  return &lookup_texture_bilinear;
3232 
3233  case SamplerState::FT_nearest_mipmap_nearest:
3234  return &lookup_texture_mipmap_nearest;
3235 
3236  case SamplerState::FT_nearest_mipmap_linear:
3237  return &lookup_texture_mipmap_linear;
3238 
3239  case SamplerState::FT_linear_mipmap_nearest:
3240  return &lookup_texture_mipmap_bilinear;
3241 
3242  case SamplerState::FT_linear_mipmap_linear:
3243  return &lookup_texture_mipmap_trilinear;
3244 
3245  default:
3246  return &lookup_texture_nearest;
3247  }
3248 }
3249 
3250 ////////////////////////////////////////////////////////////////////
3251 // Function: TinyGraphicsStateGuardian::get_tex_wrap_func
3252 // Access: Private, Static
3253 // Description: Returns the pointer to the appropriate wrap
3254 // function according to the texture's wrap mode.
3255 ////////////////////////////////////////////////////////////////////
3256 ZB_texWrapFunc TinyGraphicsStateGuardian::
3257 get_tex_wrap_func(SamplerState::WrapMode wrap_mode) {
3258  switch (wrap_mode) {
3259  case SamplerState::WM_clamp:
3260  case SamplerState::WM_border_color: // border_color is handled later.
3261  return &texcoord_clamp;
3262 
3263  case SamplerState::WM_repeat:
3264  case SamplerState::WM_invalid:
3265  return &texcoord_repeat;
3266 
3267  case SamplerState::WM_mirror:
3268  return &texcoord_mirror;
3269 
3270  case SamplerState::WM_mirror_once:
3271  return &texcoord_mirror_once;
3272  }
3273 
3274  return &texcoord_repeat;
3275 }
3276 
3277 ////////////////////////////////////////////////////////////////////
3278 // Function: TinyGraphicsStateGuardian::texgen_null
3279 // Access: Private, Static
3280 // Description: Generates invalid texture coordinates. Used when
3281 // texture coordinate params are invalid or unsupported.
3282 ////////////////////////////////////////////////////////////////////
3283 void TinyGraphicsStateGuardian::
3284 texgen_null(V2 &result, TinyGraphicsStateGuardian::TexCoordData &) {
3285  result.v[0] = 0.0;
3286  result.v[1] = 0.0;
3287 }
3288 
3289 ////////////////////////////////////////////////////////////////////
3290 // Function: TinyGraphicsStateGuardian::texgen_simple
3291 // Access: Private, Static
3292 // Description: Extracts a simple 2-d texture coordinate pair from
3293 // the vertex data, without applying any texture matrix.
3294 ////////////////////////////////////////////////////////////////////
3295 void TinyGraphicsStateGuardian::
3296 texgen_simple(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3297  // No need to transform, so just extract as two-component.
3298  const LVecBase2 &d = tcdata._r1.get_data2();
3299  result.v[0] = d[0];
3300  result.v[1] = d[1];
3301 }
3302 
3303 ////////////////////////////////////////////////////////////////////
3304 // Function: TinyGraphicsStateGuardian::texgen_simple
3305 // Access: Private, Static
3306 // Description: Extracts a simple 2-d texture coordinate pair from
3307 // the vertex data, and then applies a texture matrix.
3308 ////////////////////////////////////////////////////////////////////
3309 void TinyGraphicsStateGuardian::
3310 texgen_texmat(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3311  // Transform texcoords as a four-component vector for most generality.
3312  LVecBase4 d = tcdata._r1.get_data4() * tcdata._mat;
3313  result.v[0] = d[0] / d[3];
3314  result.v[1] = d[1] / d[3];
3315 }
3316 
3317 ////////////////////////////////////////////////////////////////////
3318 // Function: TinyGraphicsStateGuardian::texgen_sphere_map
3319 // Access: Private, Static
3320 // Description: Computes appropriate sphere map texture coordinates
3321 // based on the eye normal coordinates.
3322 ////////////////////////////////////////////////////////////////////
3323 void TinyGraphicsStateGuardian::
3324 texgen_sphere_map(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3325  // Get the normal and point in eye coordinates.
3326  LVector3 n = tcdata._mat.xform_vec(tcdata._r1.get_data3());
3327  LVector3 u = tcdata._mat.xform_point(tcdata._r2.get_data3());
3328 
3329  // Normalize the vectors.
3330  n.normalize();
3331  u.normalize();
3332 
3333  // Compute the reflection vector.
3334  LVector3 r = u - n * dot(n, u) * 2.0f;
3335 
3336  // compute the denominator, m.
3337  PN_stdfloat m = 2.0f * csqrt(r[0] * r[0] + r[1] * r[1] + (r[2] + 1.0f) * (r[2] + 1.0f));
3338 
3339  // Now we can compute the s and t coordinates.
3340  result.v[0] = r[0] / m + 0.5f;
3341  result.v[1] = r[1] / m + 0.5f;
3342 
3343  /*
3344  cerr << "n = " << n << " u = " << u << "\n"
3345  << " r = " << r << "\n"
3346  << " m = " << m << ", result = " << result.v[0] << " " << result.v[1]
3347  << "\n";
3348  */
3349 }
Format get_format() const
Returns the format of the texture, which represents both the semantic meaning of the texels and...
Definition: texture.I:872
A light shining from infinitely far away in a particular direction, like sunlight.
virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected points.
Mode get_mode() const
Returns the colorBlend mode.
virtual Light * as_light()
Cross-casts the node to a Light pointer, if it is one of the four kinds of Light nodes, or returns NULL if it is not.
Definition: pandaNode.cxx:2528
FilterType get_effective_magfilter() const
Returns the filter mode of the texture for magnification, with special treatment for FT_default...
The abstract interface to all kinds of lights.
Definition: light.h:42
virtual void end_frame(Thread *current_thread)
Called after each frame is rendered, to allow the GSG a chance to do any internal cleanup after rende...
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
virtual void end_draw_primitives()
Called after a sequence of draw_primitive() functions are called, this should do whatever cleanup is ...
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
Mode get_mode() const
Return the mode of this stage.
Definition: textureStage.I:206
int get_simple_x_size() const
Returns the width of the &quot;simple&quot; image in texels.
Definition: texture.I:2030
virtual bool update_texture(TextureContext *tc, bool force)
Ensures that the current Texture data is refreshed onto the GSG.
void get_region_pixels_i(int &xo, int &yo, int &w, int &h) const
Similar to get_region_pixels(), but returns the upper left corner, and the pixel numbers are numbered...
void setup_2d_texture()
Sets the texture as an empty 2-d texture with no dimensions.
Definition: texture.I:108
const LColor & get_ambient() const
Returns the ambient color setting, if it has been set.
Definition: material.I:82
This is a base class for GraphicsWindow (actually, GraphicsOutput) and DisplayRegion, both of which are conceptually rectangular regions into which drawing commands may be issued.
Enables or disables writing to the depth buffer.
virtual void do_issue_light()
This implementation of do_issue_light() assumes we have a limited number of hardware lights available...
static LMatrix4f scale_mat(const LVecBase3f &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
Definition: lmatrix.h:2456
virtual bool depth_offset_decals()
Returns true if this GSG can implement decals using a DepthOffsetAttrib, or false if that is unreliab...
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
bool has_ambient() const
Returns true if the ambient color has been explicitly set for this material, false otherwise...
Definition: material.I:70
Definition: zmath.h:18
Definition: zmath.h:6
Enables or disables writing to the color buffer.
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a &quot;scene&quot; ...
bool is_off() const
Returns true if the MaterialAttrib is an &#39;off&#39; MaterialAttrib, indicating that it should disable the ...
Indicates which, if any, material should be applied to geometry.
Enables or disables writing to the depth buffer.
virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected triangles.
size_t get_ram_image_size() const
Returns the total number of bytes used by the in-memory image, across all pages and views...
Definition: texture.I:1507
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
Definition: zgl.h:141
This is a convenience class to specialize ConfigVariable as a boolean type.
ComponentType get_component_type() const
Returns the numeric interpretation of each component of the texture.
Definition: texture.I:884
virtual void end_draw_primitives()
Called after a sequence of draw_primitive() functions are called, this should do whatever cleanup is ...
const LPoint3 & get_point() const
Returns the point in space at which the light is located.
Definition: pointLight.I:102
virtual bool framebuffer_copy_to_ram(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)
Copy the pixels within the indicated display region from the framebuffer into system memory...
This controls the enabling of transparency.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:925
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:57
CPTA_uchar get_uncompressed_ram_image()
Returns the system-RAM image associated with the texture, in an uncompressed form if at all possible...
Definition: texture.I:1666
const LVector3 & get_view_vector() const
Returns the axis along which the lens is facing.
Definition: lens.cxx:247
WrapMode get_wrap_v() const
Returns the wrap mode of the texture in the V direction.
Definition: samplerState.I:192
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
unsigned int get_channels() const
Returns the mask of color channels that are enabled by this attrib.
void setup_texture(TextureType texture_type, int x_size, int y_size, int z_size, ComponentType component_type, Format format)
Sets the texture to the indicated type and dimensions, presumably in preparation for calling read() o...
Definition: texture.I:62
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:63
Specifies whether flat shading (per-polygon) or smooth shading (per-vertex) is in effect...
This is a special class object that holds all the information returned by a particular GSG to indicat...
PandaCompareFunc get_mode() const
Returns the depth write mode.
virtual void end_frame(Thread *current_thread)
Called after each frame is rendered, to allow the GSG a chance to do any internal cleanup after rende...
Material * get_material() const
If the MaterialAttrib is not an &#39;off&#39; MaterialAttrib, returns the material that is associated...
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
Definition: zmath.h:26
FilterType get_magfilter() const
Returns the filter mode of the texture for magnification.
Definition: samplerState.I:231
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
bool uses_mipmaps() const
Returns true if the minfilter settings on this sampler indicate the use of mipmapping, false otherwise.
Definition: samplerState.I:318
static bool is_mipmap(FilterType type)
Returns true if the indicated filter type requires the use of mipmaps, or false if it does not...
Definition: samplerState.I:329
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
virtual bool prepare_lens()
Makes the current lens (whichever lens was most recently specified with set_scene()) active...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
Texture * get_texture() const
Returns the pointer to the associated Texture object.
virtual void close_gsg()
This is called by the associated GraphicsWindow when close_window() is called.
PN_stdfloat get_shininess() const
Returns the shininess exponent of the material.
Definition: material.I:217
Mode get_mode(TextureStage *stage) const
Returns the generation mode associated with the named texture stage, or M_off if nothing is associate...
Encapsulates the data from a Geom, pre-fetched for one stage of the pipeline.
Definition: geom.h:401
void do_issue_color()
This method is defined in the base class because it is likely that this functionality will be used fo...
static const LMatrix4f & convert_mat(CoordinateSystem from, CoordinateSystem to)
Returns a matrix that transforms from the indicated coordinate system to the indicated coordinate sys...
Definition: lmatrix.cxx:656
PN_stdfloat get_exponent() const FINAL
Returns the exponent that controls the amount of light falloff from the center of the spotlight...
Definition: spotlight.I:50
const LColor & get_emission() const
Returns the emission color setting, if it has been set.
Definition: material.I:193
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping, false otherwise.
Definition: texture.I:1319
void texture_uploaded(Texture *tex)
This method is called by the GraphicsStateGuardian after a texture has been successfully uploaded to ...
PN_stdfloat get_min_value() const
Returns the value for the minimum (closest) depth value to be stored in the buffer, in the range 0
int get_num_ram_mipmap_images() const
Returns the maximum number of mipmap level images available in system memory.
Definition: texture.I:1806
void dequeue_lru()
Removes the page from its AdaptiveLru.
Definition: adaptiveLru.I:171
int get_expected_mipmap_x_size(int n) const
Returns the x_size that the nth mipmap level should have, based on the texture&#39;s size.
Definition: texture.I:1392
Definition: zgl.h:48
PN_stdfloat get_hfov() const
Returns the horizontal component of fov only.
Definition: lens.I:435
const LColor & get_specular_color() const FINAL
Returns the color of specular highlights generated by the light.
void enqueue_lru(AdaptiveLru *lru)
Adds the page to the LRU for the first time, or marks it recently-accessed if it has already been add...
Indicates which faces should be culled based on their vertex ordering.
This specialization on GeomMunger finesses vertices for TinyPanda rendering.
bool was_simple_image_modified() const
Returns true if the texture&#39;s &quot;simple&quot; image has been modified since the last time mark_simple_loaded...
Mode get_mode() const
Returns the depth write mode.
InternalName * get_texcoord_name() const
See set_texcoord_name.
Definition: textureStage.I:148
A lightweight class that represents a single element that may be timed and/or counted via stats...
Mode get_effective_mode() const
Returns the effective culling mode.
Mode get_mode() const
Returns the shade mode.
virtual bool framebuffer_copy_to_texture(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)
Copy the pixels within the indicated display region from the framebuffer into texture memory...
PandaCompareFunc get_mode() const
Returns the alpha write mode.
LColor get_color() const
Returns the constant color associated with the attrib.
PN_stdfloat get_reference_alpha() const
Returns the alpha reference value.
Definition: zgl.h:106
virtual void release_texture(TextureContext *tc)
Frees the GL resources previously allocated for the texture.
int get_z_size() const
Returns the depth of the texture image in texels.
Definition: texture.I:663
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
int get_num_components() const
Returns the number of color components for each texel of the texture image.
Definition: texture.I:835
Definition: zgl.h:123
const LVector3 & get_direction() const
Returns the direction in which the light is aimed.
FilterType get_minfilter() const
Returns the filter mode of the texture for minification.
Definition: samplerState.I:218
virtual PreparedGraphicsObjects * get_prepared_objects()
Returns the set of texture and geom objects that have been prepared with this GSG (and possibly other...
int get_current_tex_view_offset() const
Returns the current tex view offset, as set by the last call to prepare_display_region().
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
const LColor & get_specular_color() const FINAL
Returns the color of specular highlights generated by the light.
Definition: pointLight.I:49
CPTA_uchar get_simple_ram_image() const
Returns the image data associated with the &quot;simple&quot; texture image.
Definition: texture.I:2088
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
virtual void free_pointers()
Frees some memory that was explicitly allocated within the glgsg.
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
A light originating from a single point in space, and shining in a particular direction, with a cone-shaped falloff.
Definition: spotlight.h:37
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
const LColor & get_specular() const
Returns the specular color setting, if it has been set.
Definition: material.I:156
CPTA_uchar get_ram_mipmap_image(int n) const
Returns the system-RAM image data associated with the nth mipmap level, if present.
Definition: texture.cxx:1032
virtual void bind_light(PointLight *light_obj, const NodePath &light, int light_id)
Called the first time a particular light has been bound to a given id within a frame, this should set up the associated hardware light with the light&#39;s properties.
WrapMode get_wrap_u() const
Returns the wrap mode of the texture in the U direction.
Definition: samplerState.I:181
LColor get_border_color() const
Returns the solid color of the texture&#39;s border.
Definition: texture.I:1247
const LColor & get_specular_color() const FINAL
Returns the color of specular highlights generated by the light.
Definition: spotlight.I:79
size_t get_ram_mipmap_view_size(int n) const
Returns the number of bytes used by the in-memory image per view for mipmap level n...
Definition: texture.I:1874
Enables or disables writing of pixel to framebuffer based on its alpha value relative to a reference ...
TextureType get_texture_type() const
Returns the overall interpretation of the texture.
Definition: texture.I:859
virtual void reset()
Resets all internal state as if the gsg were newly created.
Defines the way an object appears in the presence of lighting.
Definition: material.h:34
bool has_simple_ram_image() const
Returns true if the Texture has a &quot;simple&quot; image available in main RAM.
Definition: texture.I:2053
int get_offset() const
Returns the depth offset represented by this attrib.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
Mode get_mode() const
Returns the render mode.
int get_tex_view_offset() const
Returns the current setting of the tex_view_offset.
Definition: textureStage.I:349
const LVecBase3 & get_attenuation() const FINAL
Returns the terms of the attenuation equation for the light.
Definition: pointLight.I:75
static void add_gsg(GraphicsStateGuardianBase *gsg)
Called by a GSG after it has been initialized, to add a new GSG to the available list.
Represents a set of settings that indicate how a texture is sampled.
Definition: samplerState.h:39
TextureContext * prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the texture on the particular GSG, if it does not already exist.
Definition: texture.cxx:1808
float get_cell(int row, int col) const
Returns a particular element of the matrix.
Definition: lmatrix.h:1444
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force)
Called before a sequence of draw_primitive() functions are called, this should prepare the vertex dat...
Applies a transform matrix to UV&#39;s before they are rendered.
Definition: zgl.h:64
bool is_null() const
Returns true if the PointerTo is a NULL pointer, false otherwise.
Definition: pointerToVoid.I:54
bool has_diffuse() const
Returns true if the diffuse color has been explicitly set for this material, false otherwise...
Definition: material.I:107
Mode get_mode() const
Returns the transparency mode.
Mode get_mode() const
Returns the render mode.
This specifies how colors are blended into the frame buffer, for special effects. ...
void generate_ram_mipmap_images()
Automatically fills in the n mipmap levels of the Texture, based on the texture&#39;s source image...
Definition: texture.I:2018
PN_stdfloat get_max_value() const
Returns the value for the maximum (farthest) depth value to be stored in the buffer, in the range 0
A thread; that is, a lightweight process.
Definition: thread.h:51
bool was_image_modified() const
Returns true if the texture image has been modified since the last time mark_loaded() was called...
void update_data_size_bytes(size_t new_data_size_bytes)
Should be called (usually by a derived class) when the on-card size of this object has changed...
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
int get_expected_mipmap_y_size(int n) const
Returns the y_size that the nth mipmap level should have, based on the texture&#39;s size.
Definition: texture.I:1404
This is a special kind of attribute that instructs the graphics driver to apply an offset or bias to ...
void mark_loaded()
Should be called after the texture has been loaded into graphics memory, this updates the internal fl...
Encapsulates all the communication with a particular instance of a given rendering backend...
Specifies how polygons are to be drawn.
int get_view() const
Returns the specific view of a multiview texture this context represents.
void set_active(bool flag)
Changes the active flag associated with this object.
Definition: bufferContext.I:73
size_t get_simple_ram_image_size() const
Returns the number of bytes used by the &quot;simple&quot; image, or 0 if there is no simple image...
Definition: texture.I:2065
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
FilterType get_effective_minfilter() const
Returns the filter mode of the texture for minification, with special treatment for FT_default...
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a &quot;scene&quot; (usual...
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a &quot;scene&quot; (usual...
bool has_ram_image() const
Returns true if the Texture has its image contents available in main RAM, false if it exists only in ...
Definition: texture.I:1465
Specifies how polygons are to be drawn.
virtual TextureContext * prepare_texture(Texture *tex, int view)
Creates whatever structures the GSG requires to represent the texture internally, and returns a newly...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:61
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
const LVecBase3 & get_attenuation() const FINAL
Returns the terms of the attenuation equation for the light.
Definition: spotlight.I:105
virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force)
Called before a sequence of draw_primitive() functions are called, this should prepare the vertex dat...
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
Definition: scissorAttrib.h:41
int get_simple_y_size() const
Returns the height of the &quot;simple&quot; image in texels.
Definition: texture.I:2041
int get_component_width() const
Returns the number of bytes stored for each color component of a texel.
Definition: texture.I:848
This class is the main interface to controlling the render process.
This is a two-component point in space.
Definition: lpoint2.h:92
Operand get_operand_b() const
Returns the multiplier for the second component.
virtual void reset()
Resets all internal state as if the gsg were newly created.
virtual bool begin_frame(Thread *current_thread)
Called before each frame is rendered, to allow the GSG a chance to do any internal cleanup before beg...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
int get_expected_num_mipmap_levels() const
Returns the number of mipmap levels that should be defined for this texture, given the texture&#39;s size...
Definition: texture.I:1380
PTA_uchar modify_ram_image()
Returns a modifiable pointer to the system-RAM image.
Definition: texture.I:1640
virtual void set_state_and_transform(const RenderState *state, const TransformState *transform)
Simultaneously resets the render state and the transform state.
const LVecBase4 & get_frame() const
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.I:51
void mark_simple_loaded()
Should be called after the texture&#39;s &quot;simple&quot; image has been loaded into graphics memory...
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:782
virtual bool begin_frame(Thread *current_thread)
Called before each frame is rendered, to allow the GSG a chance to do any internal cleanup before beg...
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:38
Computes texture coordinates for geometry automatically based on vertex position and/or normal...
Definition: texGenAttrib.h:36
const LColor & get_diffuse() const
Returns the diffuse color setting, if it has been set.
Definition: material.I:119
virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of triangle strips.
LPoint3 get_nodal_point() const
Returns the center point of the lens: the point from which the lens is viewing.
Definition: lens.cxx:277
size_t get_expected_ram_page_size() const
Returns the number of bytes that should be used per each Z page of the 3-d texture.
Definition: texture.I:1574
int get_y_size() const
Returns the height of the texture image in texels.
Definition: texture.I:650
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer, etc.) of a drawing region.
Definition: renderBuffer.h:30
bool might_have_ram_image() const
Returns true if the texture&#39;s image contents are currently available in main RAM, or there is reason ...
Definition: texture.I:1494
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
An interface to the TinyPanda software rendering code within this module.
Operand get_operand_a() const
Returns the multiplier for the first component.
A light originating from a single point in space, and shining in all directions.
Definition: pointLight.h:27
int get_x_size() const
Returns the width of the texture image in texels.
Definition: texture.I:638
void get_region_pixels_i(int &xo, int &yo, int &w, int &h) const
Similar to get_region_pixels(), but returns the upper left corner, and the pixel numbers are numbered...
QualityLevel get_quality_level() const
Returns the current quality_level hint.
Definition: texture.I:1346
Similar to PointerToArray, except that its contents may not be modified.
Indicates which set of lights should be considered &quot;on&quot; to illuminate geometry at this level and belo...
Definition: lightAttrib.h:33
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a &quot;scene&quot; ...
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:492
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Prepare a display region for rendering (set up scissor region and viewport)
Lens * get_lens(int index=0) const
Returns a pointer to the particular Lens associated with this LensNode, or NULL if there is not yet a...
Definition: lensNode.I:60
virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected line segments.