Panda3D
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 ////////////////////////////////////////////////////////////////////
204 PT(GeomMunger) TinyGraphicsStateGuardian::
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  _texturing_state = 0; // just in case
1804 
1805  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
1806  delete gtc;
1807 }
1808 
1809 ////////////////////////////////////////////////////////////////////
1810 // Function: TinyGraphicsStateGuardian::do_issue_light
1811 // Access: Protected, Virtual
1812 // Description:
1813 ////////////////////////////////////////////////////////////////////
1816  // Initialize the current ambient light total and newly enabled
1817  // light list
1818  LColor cur_ambient_light(0.0f, 0.0f, 0.0f, 0.0f);
1819 
1820  int num_enabled = 0;
1821  int num_on_lights = 0;
1822 
1823  const LightAttrib *target_light = DCAST(LightAttrib, _target_rs->get_attrib_def(LightAttrib::get_class_slot()));
1824  if (display_cat.is_spam()) {
1825  display_cat.spam()
1826  << "do_issue_light: " << target_light << "\n";
1827  }
1828 
1829  // First, release all of the previously-assigned lights.
1830  clear_light_state();
1831 
1832  // Now, assign new lights.
1833  if (target_light != (LightAttrib *)NULL) {
1834  CPT(LightAttrib) new_light = target_light->filter_to_max(_max_lights);
1835  if (display_cat.is_spam()) {
1836  new_light->write(display_cat.spam(false), 2);
1837  }
1838 
1839  num_on_lights = new_light->get_num_on_lights();
1840  for (int li = 0; li < num_on_lights; li++) {
1841  NodePath light = new_light->get_on_light(li);
1842  nassertv(!light.is_empty());
1843  Light *light_obj = light.node()->as_light();
1844  nassertv(light_obj != (Light *)NULL);
1845 
1846  _lighting_enabled = true;
1847  _c->lighting_enabled = true;
1848 
1849  if (light_obj->get_type() == AmbientLight::get_class_type()) {
1850  // Accumulate all of the ambient lights together into one.
1851  cur_ambient_light += light_obj->get_color();
1852 
1853  } else {
1854  // Other kinds of lights each get their own GLLight object.
1855  light_obj->bind(this, light, num_enabled);
1856  num_enabled++;
1857 
1858  // Handle the diffuse color here, since all lights have this
1859  // property.
1860  GLLight *gl_light = _c->first_light;
1861  nassertv(gl_light != NULL);
1862  const LColor &diffuse = light_obj->get_color();
1863  gl_light->diffuse.v[0] = diffuse[0];
1864  gl_light->diffuse.v[1] = diffuse[1];
1865  gl_light->diffuse.v[2] = diffuse[2];
1866  gl_light->diffuse.v[3] = diffuse[3];
1867  }
1868  }
1869  }
1870 
1871  _c->ambient_light_model.v[0] = cur_ambient_light[0];
1872  _c->ambient_light_model.v[1] = cur_ambient_light[1];
1873  _c->ambient_light_model.v[2] = cur_ambient_light[2];
1874  _c->ambient_light_model.v[3] = cur_ambient_light[3];
1875 
1876  // Changing the lighting state means we need to reapply the
1877  // transform in begin_draw_primitives().
1878  _transform_stale = true;
1879 }
1880 
1881 ////////////////////////////////////////////////////////////////////
1882 // Function: TinyGraphicsStateGuardian::bind_light
1883 // Access: Public, Virtual
1884 // Description: Called the first time a particular light has been
1885 // bound to a given id within a frame, this should set
1886 // up the associated hardware light with the light's
1887 // properties.
1888 ////////////////////////////////////////////////////////////////////
1890 bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
1891  pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
1892  GLLight *gl_light = &(*lookup.first).second;
1893  if (lookup.second) {
1894  // It's a brand new light. Define it.
1895  memset(gl_light, 0, sizeof(GLLight));
1896 
1897  const LColor &specular = light_obj->get_specular_color();
1898  gl_light->specular.v[0] = specular[0];
1899  gl_light->specular.v[1] = specular[1];
1900  gl_light->specular.v[2] = specular[2];
1901  gl_light->specular.v[3] = specular[3];
1902 
1903  // Position needs to specify x, y, z, and w
1904  // w == 1 implies non-infinite position
1905  CPT(TransformState) render_transform =
1906  _cs_transform->compose(_scene_setup->get_world_transform());
1907 
1908  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
1909  CPT(TransformState) net_transform = render_transform->compose(transform);
1910 
1911  LPoint3 pos = light_obj->get_point() * net_transform->get_mat();
1912  gl_light->position.v[0] = pos[0];
1913  gl_light->position.v[1] = pos[1];
1914  gl_light->position.v[2] = pos[2];
1915  gl_light->position.v[3] = 1.0f;
1916 
1917  // Exponent == 0 implies uniform light distribution
1918  gl_light->spot_exponent = 0.0f;
1919 
1920  // Cutoff == 180 means uniform point light source
1921  gl_light->spot_cutoff = 180.0f;
1922 
1923  const LVecBase3 &att = light_obj->get_attenuation();
1924  gl_light->attenuation[0] = att[0];
1925  gl_light->attenuation[1] = att[1];
1926  gl_light->attenuation[2] = att[2];
1927  }
1928 
1929  nassertv(gl_light->next == NULL);
1930 
1931  // Add it to the linked list of active lights.
1932  gl_light->next = _c->first_light;
1933  _c->first_light = gl_light;
1934 }
1935 
1936 ////////////////////////////////////////////////////////////////////
1937 // Function: TinyGraphicsStateGuardian::bind_light
1938 // Access: Public, Virtual
1939 // Description: Called the first time a particular light has been
1940 // bound to a given id within a frame, this should set
1941 // up the associated hardware light with the light's
1942 // properties.
1943 ////////////////////////////////////////////////////////////////////
1945 bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
1946  pair<Lights::iterator, bool> lookup = _dlights.insert(Lights::value_type(light, GLLight()));
1947  GLLight *gl_light = &(*lookup.first).second;
1948  if (lookup.second) {
1949  // It's a brand new light. Define it.
1950  memset(gl_light, 0, sizeof(GLLight));
1951 
1952  const LColor &specular = light_obj->get_specular_color();
1953  gl_light->specular.v[0] = specular[0];
1954  gl_light->specular.v[1] = specular[1];
1955  gl_light->specular.v[2] = specular[2];
1956  gl_light->specular.v[3] = specular[3];
1957 
1958  // Position needs to specify x, y, z, and w
1959  // w == 0 implies light is at infinity
1960  CPT(TransformState) render_transform =
1961  _cs_transform->compose(_scene_setup->get_world_transform());
1962 
1963  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
1964  CPT(TransformState) net_transform = render_transform->compose(transform);
1965 
1966  LVector3 dir = light_obj->get_direction() * net_transform->get_mat();
1967  dir.normalize();
1968  gl_light->position.v[0] = -dir[0];
1969  gl_light->position.v[1] = -dir[1];
1970  gl_light->position.v[2] = -dir[2];
1971  gl_light->position.v[3] = 0.0f;
1972 
1973  gl_light->norm_position.v[0] = -dir[0];
1974  gl_light->norm_position.v[1] = -dir[1];
1975  gl_light->norm_position.v[2] = -dir[2];
1976  gl_V3_Norm(&gl_light->norm_position);
1977 
1978  // Exponent == 0 implies uniform light distribution
1979  gl_light->spot_exponent = 0.0f;
1980 
1981  // Cutoff == 180 means uniform point light source
1982  gl_light->spot_cutoff = 180.0f;
1983 
1984  // Default attenuation values (only spotlight and point light can
1985  // modify these)
1986  gl_light->attenuation[0] = 1.0f;
1987  gl_light->attenuation[1] = 0.0f;
1988  gl_light->attenuation[2] = 0.0f;
1989  }
1990 
1991  nassertv(gl_light->next == NULL);
1992 
1993  // Add it to the linked list of active lights.
1994  gl_light->next = _c->first_light;
1995  _c->first_light = gl_light;
1996 }
1997 
1998 ////////////////////////////////////////////////////////////////////
1999 // Function: TinyGraphicsStateGuardian::bind_light
2000 // Access: Public, Virtual
2001 // Description: Called the first time a particular light has been
2002 // bound to a given id within a frame, this should set
2003 // up the associated hardware light with the light's
2004 // properties.
2005 ////////////////////////////////////////////////////////////////////
2007 bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
2008  pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
2009  GLLight *gl_light = &(*lookup.first).second;
2010  if (lookup.second) {
2011  // It's a brand new light. Define it.
2012  memset(gl_light, 0, sizeof(GLLight));
2013 
2014  const LColor &specular = light_obj->get_specular_color();
2015  gl_light->specular.v[0] = specular[0];
2016  gl_light->specular.v[1] = specular[1];
2017  gl_light->specular.v[2] = specular[2];
2018  gl_light->specular.v[3] = specular[3];
2019 
2020  Lens *lens = light_obj->get_lens();
2021  nassertv(lens != (Lens *)NULL);
2022 
2023  // Position needs to specify x, y, z, and w
2024  // w == 1 implies non-infinite position
2025  CPT(TransformState) render_transform =
2026  _cs_transform->compose(_scene_setup->get_world_transform());
2027 
2028  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
2029  CPT(TransformState) net_transform = render_transform->compose(transform);
2030 
2031  const LMatrix4 &light_mat = net_transform->get_mat();
2032  LPoint3 pos = lens->get_nodal_point() * light_mat;
2033  LVector3 dir = lens->get_view_vector() * light_mat;
2034  dir.normalize();
2035 
2036  gl_light->position.v[0] = pos[0];
2037  gl_light->position.v[1] = pos[1];
2038  gl_light->position.v[2] = pos[2];
2039  gl_light->position.v[3] = 1.0f;
2040 
2041  gl_light->spot_direction.v[0] = dir[0];
2042  gl_light->spot_direction.v[1] = dir[1];
2043  gl_light->spot_direction.v[2] = dir[2];
2044 
2045  gl_light->norm_spot_direction.v[0] = dir[0];
2046  gl_light->norm_spot_direction.v[1] = dir[1];
2047  gl_light->norm_spot_direction.v[2] = dir[2];
2048  gl_V3_Norm(&gl_light->norm_spot_direction);
2049 
2050  gl_light->spot_exponent = light_obj->get_exponent();
2051  gl_light->spot_cutoff = lens->get_hfov() * 0.5f;
2052 
2053  const LVecBase3 &att = light_obj->get_attenuation();
2054  gl_light->attenuation[0] = att[0];
2055  gl_light->attenuation[1] = att[1];
2056  gl_light->attenuation[2] = att[2];
2057  }
2058 
2059  nassertv(gl_light->next == NULL);
2060 
2061  // Add it to the linked list of active lights.
2062  gl_light->next = _c->first_light;
2063  _c->first_light = gl_light;
2064 }
2065 
2066 ////////////////////////////////////////////////////////////////////
2067 // Function: TinyGraphicsStateGuardian::do_issue_transform
2068 // Access: Protected
2069 // Description: Sends the indicated transform matrix to the graphics
2070 // API to be applied to future vertices.
2071 //
2072 // This transform is the internal_transform, already
2073 // converted into the GSG's internal coordinate system.
2074 ////////////////////////////////////////////////////////////////////
2075 void TinyGraphicsStateGuardian::
2076 do_issue_transform() {
2077  _transform_state_pcollector.add_level(1);
2078  _transform_stale = true;
2079 
2080  if (_auto_rescale_normal) {
2081  do_auto_rescale_normal();
2082  }
2083 }
2084 
2085 ////////////////////////////////////////////////////////////////////
2086 // Function: TinyGraphicsStateGuardian::do_issue_render_mode
2087 // Access: Protected
2088 // Description:
2089 ////////////////////////////////////////////////////////////////////
2090 void TinyGraphicsStateGuardian::
2091 do_issue_render_mode() {
2092  const RenderModeAttrib *target_render_mode = DCAST(RenderModeAttrib, _target_rs->get_attrib_def(RenderModeAttrib::get_class_slot()));
2093 
2094  _filled_flat = false;
2095 
2096  switch (target_render_mode->get_mode()) {
2097  case RenderModeAttrib::M_unchanged:
2098  case RenderModeAttrib::M_filled:
2099  _c->draw_triangle_front = gl_draw_triangle_fill;
2100  _c->draw_triangle_back = gl_draw_triangle_fill;
2101  break;
2102 
2103  case RenderModeAttrib::M_filled_flat:
2104  _c->draw_triangle_front = gl_draw_triangle_fill;
2105  _c->draw_triangle_back = gl_draw_triangle_fill;
2106  _filled_flat = true;
2107  break;
2108 
2109  case RenderModeAttrib::M_wireframe:
2110  _c->draw_triangle_front = gl_draw_triangle_line;
2111  _c->draw_triangle_back = gl_draw_triangle_line;
2112  break;
2113 
2114  case RenderModeAttrib::M_point:
2115  _c->draw_triangle_front = gl_draw_triangle_point;
2116  _c->draw_triangle_back = gl_draw_triangle_point;
2117  break;
2118 
2119  default:
2120  tinydisplay_cat.error()
2121  << "Unknown render mode " << (int)target_render_mode->get_mode() << endl;
2122  }
2123 }
2124 
2125 ////////////////////////////////////////////////////////////////////
2126 // Function: TinyGraphicsStateGuardian::do_issue_rescale_normal
2127 // Access: Protected
2128 // Description:
2129 ////////////////////////////////////////////////////////////////////
2130 void TinyGraphicsStateGuardian::
2131 do_issue_rescale_normal() {
2132  const RescaleNormalAttrib *target_rescale_normal = DCAST(RescaleNormalAttrib, _target_rs->get_attrib_def(RescaleNormalAttrib::get_class_slot()));
2133  RescaleNormalAttrib::Mode mode = target_rescale_normal->get_mode();
2134 
2135  _auto_rescale_normal = false;
2136 
2137  switch (mode) {
2138  case RescaleNormalAttrib::M_none:
2139  _c->normalize_enabled = false;
2140  _c->normal_scale = 1.0f;
2141  break;
2142 
2143  case RescaleNormalAttrib::M_normalize:
2144  _c->normalize_enabled = true;
2145  _c->normal_scale = 1.0f;
2146  break;
2147 
2148  case RescaleNormalAttrib::M_rescale:
2149  case RescaleNormalAttrib::M_auto:
2150  _auto_rescale_normal = true;
2151  do_auto_rescale_normal();
2152  break;
2153 
2154  default:
2155  tinydisplay_cat.error()
2156  << "Unknown rescale_normal mode " << (int)mode << endl;
2157  }
2158 }
2159 
2160 ////////////////////////////////////////////////////////////////////
2161 // Function: TinyGraphicsStateGuardian::do_issue_depth_offset
2162 // Access: Protected
2163 // Description:
2164 ////////////////////////////////////////////////////////////////////
2165 void TinyGraphicsStateGuardian::
2166 do_issue_depth_offset() {
2167  const DepthOffsetAttrib *target_depth_offset = DCAST(DepthOffsetAttrib, _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot()));
2168  int offset = target_depth_offset->get_offset();
2169  _c->zbias = offset;
2170 
2171  PN_stdfloat min_value = target_depth_offset->get_min_value();
2172  PN_stdfloat max_value = target_depth_offset->get_max_value();
2173  if (min_value == 0.0f && max_value == 1.0f) {
2174  _c->has_zrange = false;
2175  } else {
2176  _c->has_zrange = true;
2177  _c->zmin = min_value;
2178  _c->zrange = max_value - min_value;
2179  }
2180 }
2181 
2182 ////////////////////////////////////////////////////////////////////
2183 // Function: TinyGraphicsStateGuardian::do_issue_cull_face
2184 // Access: Protected
2185 // Description:
2186 ////////////////////////////////////////////////////////////////////
2187 void TinyGraphicsStateGuardian::
2188 do_issue_cull_face() {
2189  const CullFaceAttrib *target_cull_face = DCAST(CullFaceAttrib, _target_rs->get_attrib_def(CullFaceAttrib::get_class_slot()));
2190  CullFaceAttrib::Mode mode = target_cull_face->get_effective_mode();
2191 
2192  switch (mode) {
2193  case CullFaceAttrib::M_cull_none:
2194  _c->cull_face_enabled = false;
2195  break;
2196  case CullFaceAttrib::M_cull_clockwise:
2197  _c->cull_face_enabled = true;
2198  _c->cull_clockwise = true;
2199  break;
2200  case CullFaceAttrib::M_cull_counter_clockwise:
2201  _c->cull_face_enabled = true;
2202  _c->cull_clockwise = false;
2203  break;
2204  default:
2205  tinydisplay_cat.error()
2206  << "invalid cull face mode " << (int)mode << endl;
2207  break;
2208  }
2209 }
2210 
2211 ////////////////////////////////////////////////////////////////////
2212 // Function: TinyGraphicsStateGuardian::do_issue_material
2213 // Access: Protected
2214 // Description:
2215 ////////////////////////////////////////////////////////////////////
2216 void TinyGraphicsStateGuardian::
2217 do_issue_material() {
2218  static Material empty;
2219 
2220  const MaterialAttrib *target_material = DCAST(MaterialAttrib, _target_rs->get_attrib_def(MaterialAttrib::get_class_slot()));
2221 
2222  const Material *material;
2223  if (target_material == (MaterialAttrib *)NULL ||
2224  target_material->is_off()) {
2225  material = &empty;
2226  } else {
2227  material = target_material->get_material();
2228  }
2229 
2230  // Apply the material parameters to the front face.
2231  setup_material(&_c->materials[0], material);
2232 
2233  if (material->get_twoside()) {
2234  // Also apply the material parameters to the back face.
2235  setup_material(&_c->materials[1], material);
2236  }
2237 
2238  _c->local_light_model = material->get_local();
2239  _c->light_model_two_side = material->get_twoside();
2240 }
2241 
2242 ////////////////////////////////////////////////////////////////////
2243 // Function: TinyGraphicsStateGuardian::do_issue_texture
2244 // Access: Protected
2245 // Description:
2246 ////////////////////////////////////////////////////////////////////
2247 void TinyGraphicsStateGuardian::
2248 do_issue_texture() {
2249  _texturing_state = 0; // untextured
2250  _c->num_textures_enabled = 0;
2251 
2252  int num_stages = _target_texture->get_num_on_ff_stages();
2253  if (num_stages == 0) {
2254  // No texturing.
2255  return;
2256  }
2257  nassertv(num_stages <= MAX_TEXTURE_STAGES);
2258 
2259  bool all_replace = true;
2260  bool all_nearest = true;
2261  bool all_mipmap_nearest = true;
2262  bool any_mipmap = false;
2263  bool needs_general = false;
2264  Texture::QualityLevel best_quality_level = Texture::QL_default;
2265 
2266  for (int si = 0; si < num_stages; ++si) {
2267  TextureStage *stage = _target_texture->get_on_ff_stage(si);
2268  Texture *texture = _target_texture->get_on_texture(stage);
2269  nassertv(texture != (Texture *)NULL);
2270 
2271  int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
2272  TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
2273  if (tc == (TextureContext *)NULL) {
2274  // Something wrong with this texture; skip it.
2275  return;
2276  }
2277 
2278  // Get the sampler state that we are supposed to use.
2279  const SamplerState &sampler = _target_texture->get_on_sampler(stage);
2280 
2281  // Then, turn on the current texture mode.
2282  if (!update_texture(tc, false, si, sampler.uses_mipmaps())) {
2283  return;
2284  }
2285 
2286  // M_replace means M_replace; anything else is treated the same as
2287  // M_modulate.
2288  if (stage->get_mode() != TextureStage::M_replace) {
2289  all_replace = false;
2290  }
2291 
2292  Texture::QualityLevel quality_level = _texture_quality_override;
2293  if (quality_level == Texture::QL_default) {
2294  quality_level = texture->get_quality_level();
2295  }
2296  if (quality_level == Texture::QL_default) {
2297  quality_level = texture_quality_level;
2298  }
2299 
2300  best_quality_level = max(best_quality_level, quality_level);
2301 
2302  ZTextureDef *texture_def = &_c->zb->current_textures[si];
2303 
2304  // Fill in the filter func pointers. These may not actually get
2305  // called, if we decide below we can inline the filters.
2306  SamplerState::FilterType minfilter = sampler.get_minfilter();
2307  SamplerState::FilterType magfilter = sampler.get_magfilter();
2308 
2309  if (td_ignore_mipmaps && SamplerState::is_mipmap(minfilter)) {
2310  // Downgrade mipmaps.
2311  if (minfilter == SamplerState::FT_nearest_mipmap_nearest) {
2312  minfilter = SamplerState::FT_nearest;
2313  } else {
2314  minfilter = SamplerState::FT_linear;
2315  }
2316  }
2317 
2318  // Depending on this particular texture's quality level, we may
2319  // downgrade the requested filters.
2320  if (quality_level == Texture::QL_fastest) {
2321  minfilter = SamplerState::FT_nearest;
2322  magfilter = SamplerState::FT_nearest;
2323 
2324  } else if (quality_level == Texture::QL_normal) {
2325  if (SamplerState::is_mipmap(minfilter)) {
2326  minfilter = SamplerState::FT_nearest_mipmap_nearest;
2327  } else {
2328  minfilter = SamplerState::FT_nearest;
2329  }
2330  magfilter = SamplerState::FT_nearest;
2331 
2332  } else if (quality_level == Texture::QL_best) {
2333  minfilter = sampler.get_effective_minfilter();
2334  magfilter = sampler.get_effective_magfilter();
2335  }
2336 
2337  texture_def->tex_minfilter_func = get_tex_filter_func(minfilter);
2338  texture_def->tex_magfilter_func = get_tex_filter_func(magfilter);
2339 
2340  SamplerState::WrapMode wrap_u = sampler.get_wrap_u();
2341  SamplerState::WrapMode wrap_v = sampler.get_wrap_v();
2342  if (td_ignore_clamp) {
2343  wrap_u = SamplerState::WM_repeat;
2344  wrap_v = SamplerState::WM_repeat;
2345  }
2346 
2347  if (wrap_u != SamplerState::WM_repeat || wrap_v != SamplerState::WM_repeat) {
2348  // We have some nonstandard wrap mode. This will force the use
2349  // of the general texfilter mode.
2350  needs_general = true;
2351 
2352  // We need another level of indirection to implement the
2353  // different texcoord wrap modes. This means we will be using
2354  // the _impl function pointers, which are called by the toplevel
2355  // function.
2356 
2357  texture_def->tex_minfilter_func_impl = texture_def->tex_minfilter_func;
2358  texture_def->tex_magfilter_func_impl = texture_def->tex_magfilter_func;
2359 
2360  // Now assign the toplevel function pointer to do the
2361  // appropriate texture coordinate wrapping/clamping.
2362  texture_def->tex_minfilter_func = apply_wrap_general_minfilter;
2363  texture_def->tex_magfilter_func = apply_wrap_general_magfilter;
2364 
2365  texture_def->tex_wrap_u_func = get_tex_wrap_func(wrap_u);
2366  texture_def->tex_wrap_v_func = get_tex_wrap_func(wrap_v);
2367 
2368  // The following special cases are handled inline, rather than
2369  // relying on the above wrap function pointers.
2370  if (wrap_u && SamplerState::WM_border_color && wrap_v == SamplerState::WM_border_color) {
2371  texture_def->tex_minfilter_func = apply_wrap_border_color_minfilter;
2372  texture_def->tex_magfilter_func = apply_wrap_border_color_magfilter;
2373  } else if (wrap_u && SamplerState::WM_clamp && wrap_v == SamplerState::WM_clamp) {
2374  texture_def->tex_minfilter_func = apply_wrap_clamp_minfilter;
2375  texture_def->tex_magfilter_func = apply_wrap_clamp_magfilter;
2376  }
2377  }
2378 
2379  if (minfilter != SamplerState::FT_nearest || magfilter != SamplerState::FT_nearest) {
2380  all_nearest = false;
2381  }
2382 
2383  if (minfilter != SamplerState::FT_nearest_mipmap_nearest ||
2384  magfilter != SamplerState::FT_nearest) {
2385  all_mipmap_nearest = false;
2386  }
2387 
2388  if (SamplerState::is_mipmap(minfilter)) {
2389  any_mipmap = true;
2390  }
2391  }
2392 
2393  // Set a few state cache values.
2394  _c->num_textures_enabled = num_stages;
2395  _texture_replace = all_replace;
2396 
2397  _texturing_state = 2; // perspective (perspective-correct texturing)
2398  if (num_stages >= 3) {
2399  _texturing_state = 4; // multitex3
2400  } else if (num_stages == 2) {
2401  _texturing_state = 3; // multitex2
2402  } else if (!td_perspective_textures) {
2403  _texturing_state = 1; // textured (not perspective correct)
2404  }
2405 
2406  if (best_quality_level == Texture::QL_best) {
2407  // This is the most generic texture filter. Slow, but pretty.
2408  _texfilter_state = 2; // tgeneral
2409 
2410  if (!needs_general) {
2411  if (all_nearest) {
2412  // This case is inlined.
2413  _texfilter_state = 0; // tnearest
2414  } else if (all_mipmap_nearest) {
2415  // So is this case.
2416  _texfilter_state = 1; // tmipmap
2417  }
2418  }
2419 
2420  } else if (best_quality_level == Texture::QL_fastest) {
2421  // This is the cheapest texture filter. We disallow mipmaps and
2422  // perspective correctness.
2423  _texfilter_state = 0; // tnearest
2424  _texturing_state = 1; // textured (not perspective correct, no multitexture)
2425 
2426  } else {
2427  // This is the default texture filter. We use nearest sampling if
2428  // there are no mipmaps, and mipmap_nearest if there are any
2429  // mipmaps--these are the two inlined filters.
2430  _texfilter_state = 0; // tnearest
2431  if (any_mipmap) {
2432  _texfilter_state = 1; // tmipmap
2433  }
2434 
2435  if (needs_general) {
2436  // To support nonstandard texcoord wrapping etc, we need to
2437  // force the general texfilter mode.
2438  _texfilter_state = 2; // tgeneral
2439  }
2440  }
2441 }
2442 
2443 ////////////////////////////////////////////////////////////////////
2444 // Function: TinyGraphicsStateGuardian::do_issue_scissor
2445 // Access: Protected
2446 // Description:
2447 ////////////////////////////////////////////////////////////////////
2448 void TinyGraphicsStateGuardian::
2449 do_issue_scissor() {
2450  const ScissorAttrib *target_scissor = DCAST(ScissorAttrib, _target_rs->get_attrib_def(ScissorAttrib::get_class_slot()));
2451  const LVecBase4 &frame = target_scissor->get_frame();
2452  set_scissor(frame[0], frame[1], frame[2], frame[3]);
2453 }
2454 
2455 ////////////////////////////////////////////////////////////////////
2456 // Function: TinyGraphicsStateGuardian::set_scissor
2457 // Access: Private
2458 // Description: Sets up the scissor region, as a set of coordinates
2459 // relative to the current viewport.
2460 ////////////////////////////////////////////////////////////////////
2461 void TinyGraphicsStateGuardian::
2462 set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
2463  _c->scissor.left = left;
2464  _c->scissor.right = right;
2465  _c->scissor.bottom = bottom;
2466  _c->scissor.top = top;
2467  gl_eval_viewport(_c);
2468 
2469  PN_stdfloat xsize = right - left;
2470  PN_stdfloat ysize = top - bottom;
2471  PN_stdfloat xcenter = (left + right) - 1.0f;
2472  PN_stdfloat ycenter = (bottom + top) - 1.0f;
2473  if (xsize == 0.0f || ysize == 0.0f) {
2474  // If the scissor region is zero, nothing will be drawn anyway, so
2475  // don't worry about it.
2476  _scissor_mat = TransformState::make_identity();
2477  } else {
2478  _scissor_mat = TransformState::make_scale(LVecBase3(1.0f / xsize, 1.0f / ysize, 1.0f))->compose(TransformState::make_pos(LPoint3(-xcenter, -ycenter, 0.0f)));
2479  }
2480 }
2481 
2482 ////////////////////////////////////////////////////////////////////
2483 // Function: TinyGraphicsStateGuardian::apply_texture
2484 // Access: Protected
2485 // Description: Updates the graphics state with the current
2486 // information for this texture, and makes it the
2487 // current texture available for rendering.
2488 ////////////////////////////////////////////////////////////////////
2489 bool TinyGraphicsStateGuardian::
2490 apply_texture(TextureContext *tc) {
2491  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
2492 
2493  gtc->set_active(true);
2494  return true;
2495 }
2496 
2497 ////////////////////////////////////////////////////////////////////
2498 // Function: TinyGraphicsStateGuardian::upload_texture
2499 // Access: Protected
2500 // Description: Uploads the texture image to the graphics state.
2501 //
2502 // The return value is true if successful, or false if
2503 // the texture has no image.
2504 ////////////////////////////////////////////////////////////////////
2505 bool TinyGraphicsStateGuardian::
2506 upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) {
2507  Texture *tex = gtc->get_texture();
2508 
2509  if (_effective_incomplete_render && !force) {
2510  if (!tex->has_ram_image() && tex->might_have_ram_image() &&
2511  tex->has_simple_ram_image() &&
2512  !_loader.is_null()) {
2513  // If we don't have the texture data right now, go get it, but in
2514  // the meantime load a temporary simple image in its place.
2515  async_reload_texture(gtc);
2516  if (!tex->has_ram_image()) {
2517  if (gtc->was_simple_image_modified()) {
2518  return upload_simple_texture(gtc);
2519  }
2520  return true;
2521  }
2522  }
2523  }
2524 
2525  PStatTimer timer(_load_texture_pcollector);
2526  CPTA_uchar src_image = tex->get_uncompressed_ram_image();
2527  if (src_image.is_null()) {
2528  return false;
2529  }
2530 
2531 #ifdef DO_PSTATS
2532  _data_transferred_pcollector.add_level(tex->get_ram_image_size());
2533 #endif
2534  GLTexture *gltex = &gtc->_gltex;
2535 
2536  int num_levels = 1;
2537  if (uses_mipmaps) {
2538  num_levels = tex->get_expected_num_mipmap_levels();
2539  if (tex->get_num_ram_mipmap_images() < num_levels) {
2541  }
2542  }
2543 
2544  if (tinydisplay_cat.is_debug()) {
2545  tinydisplay_cat.debug()
2546  << "loading texture " << tex->get_name() << ", "
2547  << tex->get_x_size() << " x " << tex->get_y_size() << ", mipmaps = "
2548  << num_levels << ", uses_mipmaps = " << uses_mipmaps << "\n";
2549  }
2550 
2551  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), num_levels)) {
2552  return false;
2553  }
2554  LColor border_color = tex->get_border_color();
2555  border_color = border_color.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
2556  gltex->border_color.v[0] = border_color[0];
2557  gltex->border_color.v[1] = border_color[1];
2558  gltex->border_color.v[2] = border_color[2];
2559  gltex->border_color.v[3] = border_color[3];
2560 
2561  int bytecount = 0;
2562  int xsize = gltex->xsize;
2563  int ysize = gltex->ysize;
2564 
2565  for (int level = 0; level < gltex->num_levels; ++level) {
2566  ZTextureLevel *dest = &gltex->levels[level];
2567 
2568  switch (tex->get_format()) {
2569  case Texture::F_rgb:
2570  case Texture::F_rgb5:
2571  case Texture::F_rgb8:
2572  case Texture::F_rgb12:
2573  case Texture::F_rgb332:
2574  copy_rgb_image(dest, xsize, ysize, gtc, level);
2575  break;
2576 
2577  case Texture::F_rgba:
2578  case Texture::F_rgbm:
2579  case Texture::F_rgba4:
2580  case Texture::F_rgba5:
2581  case Texture::F_rgba8:
2582  case Texture::F_rgba12:
2583  case Texture::F_rgba16:
2584  case Texture::F_rgba32:
2585  copy_rgba_image(dest, xsize, ysize, gtc, level);
2586  break;
2587 
2588  case Texture::F_luminance:
2589  copy_lum_image(dest, xsize, ysize, gtc, level);
2590  break;
2591 
2592  case Texture::F_red:
2593  copy_one_channel_image(dest, xsize, ysize, gtc, level, 0);
2594  break;
2595 
2596  case Texture::F_green:
2597  copy_one_channel_image(dest, xsize, ysize, gtc, level, 1);
2598  break;
2599 
2600  case Texture::F_blue:
2601  copy_one_channel_image(dest, xsize, ysize, gtc, level, 2);
2602  break;
2603 
2604  case Texture::F_alpha:
2605  copy_alpha_image(dest, xsize, ysize, gtc, level);
2606  break;
2607 
2608  case Texture::F_luminance_alphamask:
2609  case Texture::F_luminance_alpha:
2610  copy_la_image(dest, xsize, ysize, gtc, level);
2611  break;
2612 
2613  default:
2614  tinydisplay_cat.error()
2615  << "Unsupported texture format "
2616  << tex->get_format() << "!\n";
2617  return false;
2618  }
2619 
2620  bytecount += xsize * ysize * 4;
2621  xsize = max(xsize >> 1, 1);
2622  ysize = max(ysize >> 1, 1);
2623  }
2624 
2625  gtc->update_data_size_bytes(bytecount);
2626 
2627  get_engine()->texture_uploaded(tex);
2628  gtc->mark_loaded();
2629 
2630  return true;
2631 }
2632 
2633 ////////////////////////////////////////////////////////////////////
2634 // Function: TinyGraphicsStateGuardian::upload_simple_texture
2635 // Access: Protected
2636 // Description: This is used as a standin for upload_texture
2637 // when the texture in question is unavailable (e.g. it
2638 // hasn't yet been loaded from disk). Until the texture
2639 // image itself becomes available, we will render the
2640 // texture's "simple" image--a sharply reduced version
2641 // of the same texture.
2642 ////////////////////////////////////////////////////////////////////
2643 bool TinyGraphicsStateGuardian::
2644 upload_simple_texture(TinyTextureContext *gtc) {
2645  PStatTimer timer(_load_texture_pcollector);
2646  Texture *tex = gtc->get_texture();
2647  nassertr(tex != (Texture *)NULL, false);
2648 
2649  const unsigned char *image_ptr = tex->get_simple_ram_image();
2650  if (image_ptr == (const unsigned char *)NULL) {
2651  return false;
2652  }
2653 
2654  size_t image_size = tex->get_simple_ram_image_size();
2655  int width = tex->get_simple_x_size();
2656  int height = tex->get_simple_y_size();
2657 
2658 #ifdef DO_PSTATS
2659  _data_transferred_pcollector.add_level(image_size);
2660 #endif
2661  GLTexture *gltex = &gtc->_gltex;
2662 
2663  if (tinydisplay_cat.is_debug()) {
2664  tinydisplay_cat.debug()
2665  << "loading simple image for " << tex->get_name() << "\n";
2666  }
2667 
2668  if (!setup_gltex(gltex, width, height, 1)) {
2669  return false;
2670  }
2671  LColor border_color = tex->get_border_color();
2672  border_color = border_color.fmin(LColor(1, 1, 1, 1)).fmax(LColor::zero());
2673  gltex->border_color.v[0] = border_color[0];
2674  gltex->border_color.v[1] = border_color[1];
2675  gltex->border_color.v[2] = border_color[2];
2676  gltex->border_color.v[3] = border_color[3];
2677 
2678  ZTextureLevel *dest = &gltex->levels[0];
2679  memcpy(dest->pixmap, image_ptr, image_size);
2680 
2681  gtc->mark_simple_loaded();
2682 
2683  return true;
2684 }
2685 
2686 ////////////////////////////////////////////////////////////////////
2687 // Function: TinyGraphicsStateGuardian::setup_gltex
2688 // Access: Private
2689 // Description: Sets the GLTexture size, bits, and masks
2690 // appropriately, and allocates space for a pixmap.
2691 // Does not fill the pixmap contents. Returns true if
2692 // the texture is a valid size, false otherwise.
2693 ////////////////////////////////////////////////////////////////////
2694 bool TinyGraphicsStateGuardian::
2695 setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
2696  int s_bits = get_tex_shift(x_size);
2697  int t_bits = get_tex_shift(y_size);
2698 
2699  if (s_bits < 0 || t_bits < 0) {
2700  tinydisplay_cat.error()
2701  << "Texture size " << x_size << 'x' << y_size
2702  << " unsupported: dimensions must be power of two"
2703  << " and smaller than " << _max_texture_dimension << '\n';
2704  return false;
2705  }
2706 
2707  num_levels = min(num_levels, MAX_MIPMAP_LEVELS);
2708 
2709  gltex->xsize = x_size;
2710  gltex->ysize = y_size;
2711 
2712  gltex->s_max = 1 << (s_bits + ZB_POINT_ST_FRAC_BITS);
2713  gltex->t_max = 1 << (t_bits + ZB_POINT_ST_FRAC_BITS);
2714 
2715  gltex->num_levels = num_levels;
2716 
2717  // We allocate one big buffer, large enough to include all the
2718  // mipmap levels, and index into that buffer for each level. This
2719  // cuts down on the number of individual alloc calls we have to make
2720  // for each texture.
2721  int total_bytecount = 0;
2722 
2723  // Count up the total bytes required for all mipmap levels.
2724  {
2725  int x = x_size;
2726  int y = y_size;
2727  for (int level = 0; level < num_levels; ++level) {
2728  int bytecount = x * y * 4;
2729  total_bytecount += bytecount;
2730  x = max((x >> 1), 1);
2731  y = max((y >> 1), 1);
2732  }
2733  }
2734 
2735  if (gltex->total_bytecount != total_bytecount) {
2736  if (gltex->allocated_buffer != NULL) {
2737  PANDA_FREE_ARRAY(gltex->allocated_buffer);
2738  TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount);
2739  }
2740  gltex->allocated_buffer = PANDA_MALLOC_ARRAY(total_bytecount);
2741  gltex->total_bytecount = total_bytecount;
2742  TinyTextureContext::get_class_type().inc_memory_usage(TypeHandle::MC_array, total_bytecount);
2743  }
2744 
2745  char *next_buffer = (char *)gltex->allocated_buffer;
2746  char *end_of_buffer = next_buffer + total_bytecount;
2747 
2748  int level = 0;
2749  ZTextureLevel *dest = NULL;
2750  while (level < num_levels) {
2751  dest = &gltex->levels[level];
2752  int bytecount = x_size * y_size * 4;
2753  dest->pixmap = (PIXEL *)next_buffer;
2754  next_buffer += bytecount;
2755  nassertr(next_buffer <= end_of_buffer, false);
2756 
2757  dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
2758  dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
2759  dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level);
2760  dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level);
2761 
2762  x_size = max((x_size >> 1), 1);
2763  y_size = max((y_size >> 1), 1);
2764  s_bits = max(s_bits - 1, 0);
2765  t_bits = max(t_bits - 1, 0);
2766 
2767  ++level;
2768  }
2769 
2770  // Fill out the remaining mipmap arrays with copies of the last
2771  // level, so we don't have to be concerned with running off the end
2772  // of this array while scanning out triangles.
2773  while (level < MAX_MIPMAP_LEVELS) {
2774  gltex->levels[level] = *dest;
2775  ++level;
2776  }
2777 
2778  return true;
2779 }
2780 
2781 ////////////////////////////////////////////////////////////////////
2782 // Function: TinyGraphicsStateGuardian::get_tex_shift
2783 // Access: Private
2784 // Description: Calculates the bit shift count, such that (1 << shift)
2785 // == size. Returns -1 if the size is not a power of 2
2786 // or is larger than our largest allowable size.
2787 ////////////////////////////////////////////////////////////////////
2788 int TinyGraphicsStateGuardian::
2789 get_tex_shift(int orig_size) {
2790  if ((orig_size & (orig_size - 1)) != 0) {
2791  // Not a power of 2.
2792  return -1;
2793  }
2794  if (orig_size > _max_texture_dimension) {
2795  return -1;
2796  }
2797 
2798  return count_bits_in_word((unsigned int)orig_size - 1);
2799 }
2800 
2801 ////////////////////////////////////////////////////////////////////
2802 // Function: TinyGraphicsStateGuardian::copy_lum_image
2803 // Access: Private, Static
2804 // Description: Copies and scales the one-channel luminance image
2805 // from the texture into the indicated ZTexture pixmap.
2806 ////////////////////////////////////////////////////////////////////
2807 void TinyGraphicsStateGuardian::
2808 copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2809  Texture *tex = gtc->get_texture();
2810  nassertv(tex->get_num_components() == 1);
2811  nassertv(tex->get_expected_mipmap_x_size(level) == xsize &&
2812  tex->get_expected_mipmap_y_size(level) == ysize);
2813 
2814  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2815  nassertv(!src_image.is_null());
2816  const unsigned char *src = src_image.p();
2817  size_t view_size = tex->get_ram_mipmap_view_size(level);
2818  src += view_size * gtc->get_view();
2819 
2820  // Component width, and offset to the high-order byte.
2821  int cw = tex->get_component_width();
2822 #ifdef WORDS_BIGENDIAN
2823  // Big-endian: the high-order byte is always first.
2824  static const int co = 0;
2825 #else
2826  // Little-endian: the high-order byte is last.
2827  int co = cw - 1;
2828 #endif
2829 
2830  unsigned int *dpix = (unsigned int *)dest->pixmap;
2831  nassertv(dpix != NULL);
2832  const unsigned char *spix = src;
2833  int pixel_count = xsize * ysize;
2834  while (pixel_count-- > 0) {
2835  *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], 0xff);
2836  ++dpix;
2837  spix += cw;
2838  }
2839 }
2840 
2841 ////////////////////////////////////////////////////////////////////
2842 // Function: TinyGraphicsStateGuardian::copy_alpha_image
2843 // Access: Private, Static
2844 // Description: Copies and scales the one-channel alpha image
2845 // from the texture into the indicated ZTexture pixmap.
2846 ////////////////////////////////////////////////////////////////////
2847 void TinyGraphicsStateGuardian::
2848 copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2849  Texture *tex = gtc->get_texture();
2850  nassertv(tex->get_num_components() == 1);
2851 
2852  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2853  nassertv(!src_image.is_null());
2854  const unsigned char *src = src_image.p();
2855  size_t view_size = tex->get_ram_mipmap_view_size(level);
2856  src += view_size * gtc->get_view();
2857 
2858  // Component width, and offset to the high-order byte.
2859  int cw = tex->get_component_width();
2860 #ifdef WORDS_BIGENDIAN
2861  // Big-endian: the high-order byte is always first.
2862  static const int co = 0;
2863 #else
2864  // Little-endian: the high-order byte is last.
2865  int co = cw - 1;
2866 #endif
2867 
2868  unsigned int *dpix = (unsigned int *)dest->pixmap;
2869  nassertv(dpix != NULL);
2870  const unsigned char *spix = src;
2871  int pixel_count = xsize * ysize;
2872  while (pixel_count-- > 0) {
2873  *dpix = RGBA8_TO_PIXEL(0xff, 0xff, 0xff, spix[co]);
2874  ++dpix;
2875  spix += cw;
2876  }
2877 }
2878 
2879 ////////////////////////////////////////////////////////////////////
2880 // Function: TinyGraphicsStateGuardian::copy_one_channel_image
2881 // Access: Private, Static
2882 // Description: Copies and scales the one-channel image (with a
2883 // single channel, e.g. red, green, or blue) from
2884 // the texture into the indicated ZTexture pixmap.
2885 ////////////////////////////////////////////////////////////////////
2886 void TinyGraphicsStateGuardian::
2887 copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel) {
2888  Texture *tex = gtc->get_texture();
2889  nassertv(tex->get_num_components() == 1);
2890 
2891  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2892  nassertv(!src_image.is_null());
2893  const unsigned char *src = src_image.p();
2894  size_t view_size = tex->get_ram_mipmap_view_size(level);
2895  src += view_size * gtc->get_view();
2896 
2897  // Component width, and offset to the high-order byte.
2898  int cw = tex->get_component_width();
2899 #ifdef WORDS_BIGENDIAN
2900  // Big-endian: the high-order byte is always first.
2901  static const int co = 0;
2902 #else
2903  // Little-endian: the high-order byte is last.
2904  int co = cw - 1;
2905 #endif
2906 
2907  unsigned int *dpix = (unsigned int *)dest->pixmap;
2908  nassertv(dpix != NULL);
2909  const unsigned char *spix = src;
2910  int pixel_count = xsize * ysize;
2911 
2912  switch (channel) {
2913  case 0:
2914  while (pixel_count-- > 0) {
2915  *dpix = RGBA8_TO_PIXEL(spix[co], 0, 0, 0xff);
2916  ++dpix;
2917  spix += cw;
2918  }
2919  break;
2920 
2921  case 1:
2922  while (pixel_count-- > 0) {
2923  *dpix = RGBA8_TO_PIXEL(0, spix[co], 0, 0xff);
2924  ++dpix;
2925  spix += cw;
2926  }
2927  break;
2928 
2929  case 2:
2930  while (pixel_count-- > 0) {
2931  *dpix = RGBA8_TO_PIXEL(0, 0, spix[co], 0xff);
2932  ++dpix;
2933  spix += cw;
2934  }
2935  break;
2936 
2937  case 3:
2938  while (pixel_count-- > 0) {
2939  *dpix = RGBA8_TO_PIXEL(0, 0, 0, spix[co]);
2940  ++dpix;
2941  spix += cw;
2942  }
2943  break;
2944  }
2945 }
2946 
2947 ////////////////////////////////////////////////////////////////////
2948 // Function: TinyGraphicsStateGuardian::copy_la_image
2949 // Access: Private, Static
2950 // Description: Copies and scales the two-channel luminance-alpha
2951 // image from the texture into the indicated ZTexture
2952 // pixmap.
2953 ////////////////////////////////////////////////////////////////////
2954 void TinyGraphicsStateGuardian::
2955 copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2956  Texture *tex = gtc->get_texture();
2957  nassertv(tex->get_num_components() == 2);
2958 
2959  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2960  nassertv(!src_image.is_null());
2961  const unsigned char *src = src_image.p();
2962  size_t view_size = tex->get_ram_mipmap_view_size(level);
2963  src += view_size * gtc->get_view();
2964 
2965  // Component width, and offset to the high-order byte.
2966  int cw = tex->get_component_width();
2967 #ifdef WORDS_BIGENDIAN
2968  // Big-endian: the high-order byte is always first.
2969  static const int co = 0;
2970 #else
2971  // Little-endian: the high-order byte is last.
2972  int co = cw - 1;
2973 #endif
2974 
2975  unsigned int *dpix = (unsigned int *)dest->pixmap;
2976  nassertv(dpix != NULL);
2977  const unsigned char *spix = src;
2978  int pixel_count = xsize * ysize;
2979  int inc = 2 * cw;
2980  while (pixel_count-- > 0) {
2981  *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], spix[cw + co]);
2982  ++dpix;
2983  spix += inc;
2984  }
2985 }
2986 
2987 ////////////////////////////////////////////////////////////////////
2988 // Function: TinyGraphicsStateGuardian::copy_rgb_image
2989 // Access: Private, Static
2990 // Description: Copies and scales the three-channel RGB image from
2991 // the texture into the indicated ZTexture pixmap.
2992 ////////////////////////////////////////////////////////////////////
2993 void TinyGraphicsStateGuardian::
2994 copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
2995  Texture *tex = gtc->get_texture();
2996  nassertv(tex->get_num_components() == 3);
2997 
2998  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
2999  nassertv(!src_image.is_null());
3000  const unsigned char *src = src_image.p();
3001  size_t view_size = tex->get_ram_mipmap_view_size(level);
3002  src += view_size * gtc->get_view();
3003 
3004  // Component width, and offset to the high-order byte.
3005  int cw = tex->get_component_width();
3006 #ifdef WORDS_BIGENDIAN
3007  // Big-endian: the high-order byte is always first.
3008  static const int co = 0;
3009 #else
3010  // Little-endian: the high-order byte is last.
3011  int co = cw - 1;
3012 #endif
3013 
3014  unsigned int *dpix = (unsigned int *)dest->pixmap;
3015  nassertv(dpix != NULL);
3016  const unsigned char *spix = src;
3017  int pixel_count = xsize * ysize;
3018  int inc = 3 * cw;
3019  while (pixel_count-- > 0) {
3020  *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], 0xff);
3021  ++dpix;
3022  spix += inc;
3023  }
3024 }
3025 
3026 ////////////////////////////////////////////////////////////////////
3027 // Function: TinyGraphicsStateGuardian::copy_rgba_image
3028 // Access: Private, Static
3029 // Description: Copies and scales the four-channel RGBA image from
3030 // the texture into the indicated ZTexture pixmap.
3031 ////////////////////////////////////////////////////////////////////
3032 void TinyGraphicsStateGuardian::
3033 copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
3034  Texture *tex = gtc->get_texture();
3035  nassertv(tex->get_num_components() == 4);
3036 
3037  CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
3038  nassertv(!src_image.is_null());
3039  const unsigned char *src = src_image.p();
3040  size_t view_size = tex->get_ram_mipmap_view_size(level);
3041  src += view_size * gtc->get_view();
3042 
3043  // Component width, and offset to the high-order byte.
3044  int cw = tex->get_component_width();
3045 #ifdef WORDS_BIGENDIAN
3046  // Big-endian: the high-order byte is always first.
3047  static const int co = 0;
3048 #else
3049  // Little-endian: the high-order byte is last.
3050  int co = cw - 1;
3051 #endif
3052 
3053  unsigned int *dpix = (unsigned int *)dest->pixmap;
3054  nassertv(dpix != NULL);
3055  const unsigned char *spix = src;
3056  int pixel_count = xsize * ysize;
3057  int inc = 4 * cw;
3058  while (pixel_count-- > 0) {
3059  *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], spix[cw + cw + cw + co]);
3060  ++dpix;
3061  spix += inc;
3062  }
3063 }
3064 
3065 ////////////////////////////////////////////////////////////////////
3066 // Function: TinyGraphicsStateGuardian::setup_material
3067 // Access: Private
3068 // Description: Applies the desired parametesr to the indicated
3069 // GLMaterial object.
3070 ////////////////////////////////////////////////////////////////////
3071 void TinyGraphicsStateGuardian::
3072 setup_material(GLMaterial *gl_material, const Material *material) {
3073  const LColor &specular = material->get_specular();
3074  gl_material->specular.v[0] = specular[0];
3075  gl_material->specular.v[1] = specular[1];
3076  gl_material->specular.v[2] = specular[2];
3077  gl_material->specular.v[3] = specular[3];
3078 
3079  const LColor &emission = material->get_emission();
3080  gl_material->emission.v[0] = emission[0];
3081  gl_material->emission.v[1] = emission[1];
3082  gl_material->emission.v[2] = emission[2];
3083  gl_material->emission.v[3] = emission[3];
3084 
3085  gl_material->shininess = material->get_shininess();
3086  gl_material->shininess_i = (int)((material->get_shininess() / 128.0f) * SPECULAR_BUFFER_RESOLUTION);
3087 
3088  _color_material_flags = CMF_ambient | CMF_diffuse;
3089 
3090  if (material->has_ambient()) {
3091  const LColor &ambient = material->get_ambient();
3092  gl_material->ambient.v[0] = ambient[0];
3093  gl_material->ambient.v[1] = ambient[1];
3094  gl_material->ambient.v[2] = ambient[2];
3095  gl_material->ambient.v[3] = ambient[3];
3096 
3097  _color_material_flags &= ~CMF_ambient;
3098  }
3099 
3100  if (material->has_diffuse()) {
3101  const LColor &diffuse = material->get_diffuse();
3102  gl_material->diffuse.v[0] = diffuse[0];
3103  gl_material->diffuse.v[1] = diffuse[1];
3104  gl_material->diffuse.v[2] = diffuse[2];
3105  gl_material->diffuse.v[3] = diffuse[3];
3106 
3107  _color_material_flags &= ~CMF_diffuse;
3108  }
3109 }
3110 
3111 ////////////////////////////////////////////////////////////////////
3112 // Function: TinyGraphicsStateGuardian::do_auto_rescale_normal
3113 // Access: Protected
3114 // Description: Sets the state to either rescale or normalize the
3115 // normals according to the current transform.
3116 ////////////////////////////////////////////////////////////////////
3117 void TinyGraphicsStateGuardian::
3118 do_auto_rescale_normal() {
3119  if (_internal_transform->has_uniform_scale()) {
3120  // There's a uniform scale; rescale the normals uniformly.
3121  _c->normalize_enabled = false;
3122  _c->normal_scale = _internal_transform->get_uniform_scale();
3123 
3124  } else {
3125  // If there's a non-uniform scale, normalize everything.
3126  _c->normalize_enabled = true;
3127  _c->normal_scale = 1.0f;
3128  }
3129 }
3130 
3131 ////////////////////////////////////////////////////////////////////
3132 // Function: TinyGraphicsStateGuardian::load_matrix
3133 // Access: Private, Static
3134 // Description: Copies the Panda matrix stored in the indicated
3135 // TransformState object into the indicated TinyGL
3136 // matrix.
3137 ////////////////////////////////////////////////////////////////////
3138 void TinyGraphicsStateGuardian::
3139 load_matrix(M4 *matrix, const TransformState *transform) {
3140  const LMatrix4 &pm = transform->get_mat();
3141  for (int i = 0; i < 4; ++i) {
3142  matrix->m[0][i] = pm.get_cell(i, 0);
3143  matrix->m[1][i] = pm.get_cell(i, 1);
3144  matrix->m[2][i] = pm.get_cell(i, 2);
3145  matrix->m[3][i] = pm.get_cell(i, 3);
3146  }
3147 }
3148 
3149 ////////////////////////////////////////////////////////////////////
3150 // Function: TinyGraphicsStateGuardian::get_color_blend_op
3151 // Access: Private, Static
3152 // Description: Returns the integer element of store_pixel_funcs (as
3153 // defined by store_pixel.py) that corresponds to the
3154 // indicated ColorBlendAttrib operand code.
3155 ////////////////////////////////////////////////////////////////////
3156 int TinyGraphicsStateGuardian::
3157 get_color_blend_op(ColorBlendAttrib::Operand operand) {
3158  switch (operand) {
3159  case ColorBlendAttrib::O_zero:
3160  return 0;
3161  case ColorBlendAttrib::O_one:
3162  return 1;
3163  case ColorBlendAttrib::O_incoming_color:
3164  return 2;
3165  case ColorBlendAttrib::O_one_minus_incoming_color:
3166  return 3;
3167  case ColorBlendAttrib::O_fbuffer_color:
3168  return 4;
3169  case ColorBlendAttrib::O_one_minus_fbuffer_color:
3170  return 5;
3171  case ColorBlendAttrib::O_incoming_alpha:
3172  return 6;
3173  case ColorBlendAttrib::O_one_minus_incoming_alpha:
3174  return 7;
3175  case ColorBlendAttrib::O_fbuffer_alpha:
3176  return 8;
3177  case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
3178  return 9;
3179  case ColorBlendAttrib::O_constant_color:
3180  return 10;
3181  case ColorBlendAttrib::O_one_minus_constant_color:
3182  return 11;
3183  case ColorBlendAttrib::O_constant_alpha:
3184  return 12;
3185  case ColorBlendAttrib::O_one_minus_constant_alpha:
3186  return 13;
3187 
3188  case ColorBlendAttrib::O_incoming_color_saturate:
3189  return 1;
3190 
3191  case ColorBlendAttrib::O_color_scale:
3192  return 10;
3193  case ColorBlendAttrib::O_one_minus_color_scale:
3194  return 11;
3195  case ColorBlendAttrib::O_alpha_scale:
3196  return 12;
3197  case ColorBlendAttrib::O_one_minus_alpha_scale:
3198  return 13;
3199  }
3200  return 0;
3201 }
3202 
3203 ////////////////////////////////////////////////////////////////////
3204 // Function: TinyGraphicsStateGuardian::get_tex_filter_func
3205 // Access: Private, Static
3206 // Description: Returns the pointer to the appropriate filter
3207 // function according to the texture's filter type.
3208 ////////////////////////////////////////////////////////////////////
3209 ZB_lookupTextureFunc TinyGraphicsStateGuardian::
3210 get_tex_filter_func(SamplerState::FilterType filter) {
3211  switch (filter) {
3212  case SamplerState::FT_nearest:
3213  return &lookup_texture_nearest;
3214 
3215  case SamplerState::FT_linear:
3216  return &lookup_texture_bilinear;
3217 
3218  case SamplerState::FT_nearest_mipmap_nearest:
3219  return &lookup_texture_mipmap_nearest;
3220 
3221  case SamplerState::FT_nearest_mipmap_linear:
3222  return &lookup_texture_mipmap_linear;
3223 
3224  case SamplerState::FT_linear_mipmap_nearest:
3225  return &lookup_texture_mipmap_bilinear;
3226 
3227  case SamplerState::FT_linear_mipmap_linear:
3228  return &lookup_texture_mipmap_trilinear;
3229 
3230  default:
3231  return &lookup_texture_nearest;
3232  }
3233 }
3234 
3235 ////////////////////////////////////////////////////////////////////
3236 // Function: TinyGraphicsStateGuardian::get_tex_wrap_func
3237 // Access: Private, Static
3238 // Description: Returns the pointer to the appropriate wrap
3239 // function according to the texture's wrap mode.
3240 ////////////////////////////////////////////////////////////////////
3241 ZB_texWrapFunc TinyGraphicsStateGuardian::
3242 get_tex_wrap_func(SamplerState::WrapMode wrap_mode) {
3243  switch (wrap_mode) {
3244  case SamplerState::WM_clamp:
3245  case SamplerState::WM_border_color: // border_color is handled later.
3246  return &texcoord_clamp;
3247 
3248  case SamplerState::WM_repeat:
3249  case SamplerState::WM_invalid:
3250  return &texcoord_repeat;
3251 
3252  case SamplerState::WM_mirror:
3253  return &texcoord_mirror;
3254 
3255  case SamplerState::WM_mirror_once:
3256  return &texcoord_mirror_once;
3257  }
3258 
3259  return &texcoord_repeat;
3260 }
3261 
3262 ////////////////////////////////////////////////////////////////////
3263 // Function: TinyGraphicsStateGuardian::texgen_null
3264 // Access: Private, Static
3265 // Description: Generates invalid texture coordinates. Used when
3266 // texture coordinate params are invalid or unsupported.
3267 ////////////////////////////////////////////////////////////////////
3268 void TinyGraphicsStateGuardian::
3269 texgen_null(V2 &result, TinyGraphicsStateGuardian::TexCoordData &) {
3270  result.v[0] = 0.0;
3271  result.v[1] = 0.0;
3272 }
3273 
3274 ////////////////////////////////////////////////////////////////////
3275 // Function: TinyGraphicsStateGuardian::texgen_simple
3276 // Access: Private, Static
3277 // Description: Extracts a simple 2-d texture coordinate pair from
3278 // the vertex data, without applying any texture matrix.
3279 ////////////////////////////////////////////////////////////////////
3280 void TinyGraphicsStateGuardian::
3281 texgen_simple(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3282  // No need to transform, so just extract as two-component.
3283  const LVecBase2 &d = tcdata._r1.get_data2();
3284  result.v[0] = d[0];
3285  result.v[1] = d[1];
3286 }
3287 
3288 ////////////////////////////////////////////////////////////////////
3289 // Function: TinyGraphicsStateGuardian::texgen_simple
3290 // Access: Private, Static
3291 // Description: Extracts a simple 2-d texture coordinate pair from
3292 // the vertex data, and then applies a texture matrix.
3293 ////////////////////////////////////////////////////////////////////
3294 void TinyGraphicsStateGuardian::
3295 texgen_texmat(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3296  // Transform texcoords as a four-component vector for most generality.
3297  LVecBase4 d = tcdata._r1.get_data4() * tcdata._mat;
3298  result.v[0] = d[0] / d[3];
3299  result.v[1] = d[1] / d[3];
3300 }
3301 
3302 ////////////////////////////////////////////////////////////////////
3303 // Function: TinyGraphicsStateGuardian::texgen_sphere_map
3304 // Access: Private, Static
3305 // Description: Computes appropriate sphere map texture coordinates
3306 // based on the eye normal coordinates.
3307 ////////////////////////////////////////////////////////////////////
3308 void TinyGraphicsStateGuardian::
3309 texgen_sphere_map(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
3310  // Get the normal and point in eye coordinates.
3311  LVector3 n = tcdata._mat.xform_vec(tcdata._r1.get_data3());
3312  LVector3 u = tcdata._mat.xform_point(tcdata._r2.get_data3());
3313 
3314  // Normalize the vectors.
3315  n.normalize();
3316  u.normalize();
3317 
3318  // Compute the reflection vector.
3319  LVector3 r = u - n * dot(n, u) * 2.0f;
3320 
3321  // compute the denominator, m.
3322  PN_stdfloat m = 2.0f * csqrt(r[0] * r[0] + r[1] * r[1] + (r[2] + 1.0f) * (r[2] + 1.0f));
3323 
3324  // Now we can compute the s and t coordinates.
3325  result.v[0] = r[0] / m + 0.5f;
3326  result.v[1] = r[1] / m + 0.5f;
3327 
3328  /*
3329  cerr << "n = " << n << " u = " << u << "\n"
3330  << " r = " << r << "\n"
3331  << " m = " << m << ", result = " << result.v[0] << " " << result.v[1]
3332  << "\n";
3333  */
3334 }
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:615
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.
int get_num_ram_mipmap_images() const
Returns the maximum number of mipmap level images available in system memory.
Definition: texture.I:1806
Format get_format() const
Returns the format of the texture, which represents both the semantic meaning of the texels and...
Definition: texture.I:872
Texture * get_texture() const
Returns the pointer to the associated Texture object.
int get_offset() const
Returns the depth offset represented by this attrib.
bool was_image_modified() const
Returns true if the texture image has been modified since the last time mark_loaded() was called...
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
The abstract interface to all kinds of lights.
Definition: light.h:42
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
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
virtual bool update_texture(TextureContext *tc, bool force)
Ensures that the current Texture data is refreshed onto the GSG.
int get_z_size() const
Returns the depth of the texture image in texels.
Definition: texture.I:663
Mode get_mode() const
Returns the colorBlend mode.
Mode get_mode() const
Returns the render mode.
LColor get_border_color() const
Returns the solid color of the texture&#39;s border.
Definition: texture.I:1247
void setup_2d_texture()
Sets the texture as an empty 2-d texture with no dimensions.
Definition: texture.I:108
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.
Material * get_material() const
If the MaterialAttrib is not an &#39;off&#39; MaterialAttrib, returns the material that is associated...
virtual void do_issue_light()
This implementation of do_issue_light() assumes we have a limited number of hardware lights available...
Mode get_mode() const
Returns the shade mode.
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
Definition: zmath.h:18
Definition: zmath.h:6
TextureType get_texture_type() const
Returns the overall interpretation of the texture.
Definition: texture.I:859
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
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 "scene" ...
int get_view() const
Returns the specific view of a multiview texture this context represents.
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
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...
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
Indicates which, if any, material should be applied to geometry.
bool has_ambient() const
Returns true if the ambient color has been explicitly set for this material, false otherwise...
Definition: material.I:70
Enables or disables writing to the depth buffer.
virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected triangles.
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
Definition: zgl.h:141
LPoint3 get_nodal_point() const
Returns the center point of the lens: the point from which the lens is viewing.
Definition: lens.cxx:277
This is a convenience class to specialize ConfigVariable as a boolean type.
virtual void end_draw_primitives()
Called after a sequence of draw_primitive() functions are called, this should do whatever cleanup is ...
PN_stdfloat get_shininess() const
Returns the shininess exponent of the material.
Definition: material.I:217
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.
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
unsigned int get_channels() const
Returns the mask of color channels that are enabled by this attrib.
Mode get_mode() const
Returns the transparency mode.
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
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
PandaCompareFunc get_mode() const
Returns the alpha write mode.
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...
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...
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
Definition: zmath.h:26
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
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
bool was_simple_image_modified() const
Returns true if the texture&#39;s "simple" image has been modified since the last time mark_simple_loaded...
Mode get_mode() const
Return the mode of this stage.
Definition: textureStage.I:206
virtual void close_gsg()
This is called by the associated GraphicsWindow when close_window() is called.
int get_component_width() const
Returns the number of bytes stored for each color component of a texel.
Definition: texture.I:848
PN_stdfloat get_hfov() const
Returns the horizontal component of fov only.
Definition: lens.I:435
FilterType get_magfilter() const
Returns the filter mode of the texture for magnification.
Definition: samplerState.I:231
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
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
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
ComponentType get_component_type() const
Returns the numeric interpretation of each component of the texture.
Definition: texture.I:884
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
int get_simple_y_size() const
Returns the height of the "simple" image in texels.
Definition: texture.I:2041
Mode get_mode() const
Returns the depth write mode.
void texture_uploaded(Texture *tex)
This method is called by the GraphicsStateGuardian after a texture has been successfully uploaded to ...
Definition: zgl.h:48
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
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...
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
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
Indicates which faces should be culled based on their vertex ordering.
Operand get_operand_a() const
Returns the multiplier for the first component.
This specialization on GeomMunger finesses vertices for TinyPanda rendering.
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:1039
FilterType get_effective_minfilter() const
Returns the filter mode of the texture for minification, with special treatment for FT_default...
const LColor & get_diffuse() const
Returns the diffuse color setting, if it has been set.
Definition: material.I:119
bool uses_mipmaps() const
Returns true if the minfilter settings on this sampler indicate the use of mipmapping, false otherwise.
Definition: samplerState.I:318
const LColor & get_ambient() const
Returns the ambient color setting, if it has been set.
Definition: material.I:82
A lightweight class that represents a single element that may be timed and/or counted via stats...
CPTA_uchar get_simple_ram_image() const
Returns the image data associated with the "simple" texture image.
Definition: texture.I:2088
FilterType get_minfilter() const
Returns the filter mode of the texture for minification.
Definition: samplerState.I:218
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...
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping, false otherwise.
Definition: texture.I:1319
virtual void clear(DrawableRegion *clearable)
Clears the framebuffer within the current DisplayRegion, according to the flags indicated by the give...
Definition: zgl.h:106
const LVecBase4 & get_frame() const
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.I:51
virtual void release_texture(TextureContext *tc)
Frees the GL resources previously allocated for the texture.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
Definition: zgl.h:123
const LPoint3 & get_point() const
Returns the point in space at which the light is located.
Definition: pointLight.I:102
bool get_clear_color_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
virtual PreparedGraphicsObjects * get_prepared_objects()
Returns the set of texture and geom objects that have been prepared with this GSG (and possibly other...
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
bool get_clear_depth_active() const
Returns the current setting of the flag that indicates whether the depth buffer should be cleared eve...
bool has_simple_ram_image() const
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.I:2053
const LColor & get_specular_color() const FINAL
Returns the color of specular highlights generated by the light.
Definition: pointLight.I:49
const LColor & get_clear_color() const
Returns the current clear color value.
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.
LColor get_color() const
Returns the constant color associated with the attrib.
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
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...
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
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.
QualityLevel get_quality_level() const
Returns the current quality_level hint.
Definition: texture.I:1346
bool get_clear_stencil_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
InternalName * get_texcoord_name() const
See set_texcoord_name.
Definition: textureStage.I:148
int get_tex_view_offset() const
Returns the current setting of the tex_view_offset.
Definition: textureStage.I:349
const LColor & get_specular_color() const FINAL
Returns the color of specular highlights generated by the light.
Definition: spotlight.I:79
Enables or disables writing of pixel to framebuffer based on its alpha value relative to a reference ...
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
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
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:1815
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
PN_stdfloat get_reference_alpha() const
Returns the alpha reference value.
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
const LMatrix4 & get_projection_mat(StereoChannel channel=SC_mono) const
Returns the complete transformation matrix from a 3-d point in space to a point on the film...
Definition: lens.I:717
const LVector3 & get_direction() const
Returns the direction in which the light is aimed.
This specifies how colors are blended into the frame buffer, for special effects. ...
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
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
PandaCompareFunc get_mode() const
Returns the depth write mode.
float get_cell(int row, int col) const
Returns a particular element of the matrix.
Definition: lmatrix.h:1444
A thread; that is, a lightweight process.
Definition: thread.h:51
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...
This is a special kind of attribute that instructs the graphics driver to apply an offset or bias to ...
int get_y_size() const
Returns the height of the texture image in texels.
Definition: texture.I:650
Operand get_operand_b() const
Returns the multiplier for the second component.
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.
void set_active(bool flag)
Changes the active flag associated with this object.
Definition: bufferContext.I:73
bool is_off() const
Returns true if the MaterialAttrib is an &#39;off&#39; MaterialAttrib, indicating that it should disable the ...
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
bool is_null() const
Returns true if the PointerTo is a NULL pointer, false otherwise.
Definition: pointerToVoid.I:54
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...
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
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_num_components() const
Returns the number of color components for each texel of the texture image.
Definition: texture.I:835
Mode get_effective_mode() const
Returns the effective culling mode.
This class is the main interface to controlling the render process.
This is a two-component point in space.
Definition: lpoint2.h:92
const LColor & get_emission() const
Returns the emission color setting, if it has been set.
Definition: material.I:193
virtual void reset()
Resets all internal state as if the gsg were newly created.
Mode get_mode() const
Returns the render mode.
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
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
bool has_diffuse() const
Returns true if the diffuse color has been explicitly set for this material, false otherwise...
Definition: material.I:107
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.
void mark_simple_loaded()
Should be called after the texture&#39;s "simple" 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:783
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
virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of triangle strips.
int get_simple_x_size() const
Returns the width of the "simple" image in texels.
Definition: texture.I:2030
WrapMode get_wrap_v() const
Returns the wrap mode of the texture in the V direction.
Definition: samplerState.I:192
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer, etc.) of a drawing region.
Definition: renderBuffer.h:30
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.
size_t get_simple_ram_image_size() const
Returns the number of bytes used by the "simple" image, or 0 if there is no simple image...
Definition: texture.I:2065
const LVector3 & get_view_vector() const
Returns the axis along which the lens is facing.
Definition: lens.cxx:247
WrapMode get_wrap_u() const
Returns the wrap mode of the texture in the U direction.
Definition: samplerState.I:181
const LColor & get_specular() const
Returns the specular color setting, if it has been set.
Definition: material.I:156
A light originating from a single point in space, and shining in all directions.
Definition: pointLight.h:27
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
Similar to PointerToArray, except that its contents may not be modified.
int get_x_size() const
Returns the width of the texture image in texels.
Definition: texture.I:638
Indicates which set of lights should be considered "on" 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 "scene" ...
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:493
FilterType get_effective_magfilter() const
Returns the filter mode of the texture for magnification, with special treatment for FT_default...
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Prepare a display region for rendering (set up scissor region and viewport)
virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected line segments.