Panda3D

tinyGraphicsStateGuardian.cxx

00001 // Filename: tinyGraphicsStateGuardian.cxx
00002 // Created by:  drose (24Apr08)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "tinyGraphicsStateGuardian.h"
00016 #include "tinyGeomMunger.h"
00017 #include "tinyTextureContext.h"
00018 #include "config_tinydisplay.h"
00019 #include "pStatTimer.h"
00020 #include "geomVertexReader.h"
00021 #include "ambientLight.h"
00022 #include "pointLight.h"
00023 #include "directionalLight.h"
00024 #include "spotlight.h"
00025 #include "depthWriteAttrib.h"
00026 #include "depthOffsetAttrib.h"
00027 #include "colorWriteAttrib.h"
00028 #include "alphaTestAttrib.h"
00029 #include "depthTestAttrib.h"
00030 #include "shadeModelAttrib.h"
00031 #include "cullFaceAttrib.h"
00032 #include "rescaleNormalAttrib.h"
00033 #include "materialAttrib.h"
00034 #include "lightAttrib.h"
00035 #include "scissorAttrib.h"
00036 #include "bitMask.h"
00037 #include "zgl.h"
00038 #include "zmath.h"
00039 #include "ztriangle_table.h"
00040 #include "store_pixel_table.h"
00041 #include "graphicsEngine.h"
00042 
00043 TypeHandle TinyGraphicsStateGuardian::_type_handle;
00044 
00045 PStatCollector TinyGraphicsStateGuardian::_vertices_immediate_pcollector("Vertices:Immediate mode");
00046 PStatCollector TinyGraphicsStateGuardian::_draw_transform_pcollector("Draw:Transform");
00047 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_untextured_pcollector("Pixels:White untextured");
00048 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_untextured_pcollector("Pixels:Flat untextured");
00049 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_untextured_pcollector("Pixels:Smooth untextured");
00050 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_textured_pcollector("Pixels:White textured");
00051 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_textured_pcollector("Pixels:Flat textured");
00052 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_textured_pcollector("Pixels:Smooth textured");
00053 PStatCollector TinyGraphicsStateGuardian::_pixel_count_white_perspective_pcollector("Pixels:White perspective");
00054 PStatCollector TinyGraphicsStateGuardian::_pixel_count_flat_perspective_pcollector("Pixels:Flat perspective");
00055 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_perspective_pcollector("Pixels:Smooth perspective");
00056 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_multitex2_pcollector("Pixels:Smooth multitex 2");
00057 PStatCollector TinyGraphicsStateGuardian::_pixel_count_smooth_multitex3_pcollector("Pixels:Smooth multitex 3");
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: TinyGraphicsStateGuardian::Constructor
00061 //       Access: Public
00062 //  Description:
00063 ////////////////////////////////////////////////////////////////////
00064 TinyGraphicsStateGuardian::
00065 TinyGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
00066                           TinyGraphicsStateGuardian *share_with) :
00067   GraphicsStateGuardian(CS_yup_right, engine, pipe)
00068 {
00069   _current_frame_buffer = NULL;
00070   _aux_frame_buffer = NULL;
00071   _c = NULL;
00072   _vertices = NULL;
00073   _vertices_size = 0;
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: TinyGraphicsStateGuardian::Destructor
00078 //       Access: Public
00079 //  Description:
00080 ////////////////////////////////////////////////////////////////////
00081 TinyGraphicsStateGuardian::
00082 ~TinyGraphicsStateGuardian() {
00083 }
00084 
00085 ////////////////////////////////////////////////////////////////////
00086 //     Function: TinyGraphicsStateGuardian::reset
00087 //       Access: Public, Virtual
00088 //  Description: Resets all internal state as if the gsg were newly
00089 //               created.
00090 ////////////////////////////////////////////////////////////////////
00091 void TinyGraphicsStateGuardian::
00092 reset() {
00093   free_pointers();
00094   GraphicsStateGuardian::reset();
00095 
00096   // Build _inv_state_mask as a mask of 1's where we don't care, and
00097   // 0's where we do care, about the state.
00098   _inv_state_mask.clear_bit(ColorAttrib::get_class_slot());
00099   _inv_state_mask.clear_bit(ColorScaleAttrib::get_class_slot());
00100   _inv_state_mask.clear_bit(CullFaceAttrib::get_class_slot());
00101   _inv_state_mask.clear_bit(DepthOffsetAttrib::get_class_slot());
00102   _inv_state_mask.clear_bit(RenderModeAttrib::get_class_slot());
00103   _inv_state_mask.clear_bit(RescaleNormalAttrib::get_class_slot());
00104   _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
00105   _inv_state_mask.clear_bit(MaterialAttrib::get_class_slot());
00106   _inv_state_mask.clear_bit(LightAttrib::get_class_slot());
00107   _inv_state_mask.clear_bit(ScissorAttrib::get_class_slot());
00108 
00109   if (_c != (GLContext *)NULL) {
00110     glClose(_c);
00111     _c = NULL;
00112   }
00113 
00114   _c = (GLContext *)gl_zalloc(sizeof(GLContext));
00115   glInit(_c, _current_frame_buffer);
00116 
00117   _c->draw_triangle_front = gl_draw_triangle_fill;
00118   _c->draw_triangle_back = gl_draw_triangle_fill;
00119 
00120   _supported_geom_rendering =
00121     Geom::GR_point | 
00122     Geom::GR_indexed_other |
00123     Geom::GR_triangle_strip |
00124     Geom::GR_flat_last_vertex;
00125 
00126   _max_texture_dimension = (1 << ZB_POINT_ST_FRAC_BITS);
00127   _max_texture_stages = MAX_TEXTURE_STAGES;
00128   _max_lights = MAX_LIGHTS;
00129 
00130   _color_scale_via_lighting = false;
00131   _alpha_scale_via_texture = false;
00132   _runtime_color_scale = true;
00133 
00134   _color_material_flags = 0;
00135   _texturing_state = 0;
00136   _texfilter_state = 0;
00137   _texture_replace = false;
00138   _filled_flat = false;
00139   _auto_rescale_normal = false;
00140 
00141   // Now that the GSG has been initialized, make it available for
00142   // optimizations.
00143   add_gsg(this);
00144 }
00145 
00146 ////////////////////////////////////////////////////////////////////
00147 //     Function: TinyGraphicsStateGuardian::free_pointers
00148 //       Access: Protected, Virtual
00149 //  Description: Frees some memory that was explicitly allocated
00150 //               within the glgsg.
00151 ////////////////////////////////////////////////////////////////////
00152 void TinyGraphicsStateGuardian::
00153 free_pointers() {
00154   if (_aux_frame_buffer != (ZBuffer *)NULL) {
00155     ZB_close(_aux_frame_buffer);
00156     _aux_frame_buffer = NULL;
00157   }
00158 
00159   if (_vertices != (GLVertex *)NULL) {
00160     PANDA_FREE_ARRAY(_vertices);
00161     _vertices = NULL;
00162   }
00163   _vertices_size = 0;
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: TinyGraphicsStateGuardian::close_gsg
00168 //       Access: Protected, Virtual
00169 //  Description: This is called by the associated GraphicsWindow when
00170 //               close_window() is called.  It should null out the
00171 //               _win pointer and possibly free any open resources
00172 //               associated with the GSG.
00173 ////////////////////////////////////////////////////////////////////
00174 void TinyGraphicsStateGuardian::
00175 close_gsg() {
00176   GraphicsStateGuardian::close_gsg();
00177 
00178   if (_c != (GLContext *)NULL) {
00179     glClose(_c);
00180     _c = NULL;
00181   }
00182 }
00183 
00184 ////////////////////////////////////////////////////////////////////
00185 //     Function: TinyGraphicsStateGuardian::depth_offset_decals
00186 //       Access: Public, Virtual
00187 //  Description: Returns true if this GSG can implement decals using a
00188 //               DepthOffsetAttrib, or false if that is unreliable
00189 //               and the three-step rendering process should be used
00190 //               instead.
00191 ////////////////////////////////////////////////////////////////////
00192 bool TinyGraphicsStateGuardian::
00193 depth_offset_decals() {
00194   return false;
00195 }
00196 
00197 ////////////////////////////////////////////////////////////////////
00198 //     Function: TinyGraphicsStateGuardian::make_geom_munger
00199 //       Access: Public, Virtual
00200 //  Description: Creates a new GeomMunger object to munge vertices
00201 //               appropriate to this GSG for the indicated state.
00202 ////////////////////////////////////////////////////////////////////
00203 PT(GeomMunger) TinyGraphicsStateGuardian::
00204 make_geom_munger(const RenderState *state, Thread *current_thread) {
00205   PT(TinyGeomMunger) munger = new TinyGeomMunger(this, state);
00206   return GeomMunger::register_munger(munger, current_thread);
00207 }
00208 
00209 ////////////////////////////////////////////////////////////////////
00210 //     Function: TinyGraphicsStateGuardian::clear
00211 //       Access: Public
00212 //  Description: Clears the framebuffer within the current
00213 //               DisplayRegion, according to the flags indicated by
00214 //               the given DrawableRegion object.
00215 //
00216 //               This does not set the DisplayRegion first.  You
00217 //               should call prepare_display_region() to specify the
00218 //               region you wish the clear operation to apply to.
00219 ////////////////////////////////////////////////////////////////////
00220 void TinyGraphicsStateGuardian::
00221 clear(DrawableRegion *clearable) {
00222   PStatTimer timer(_clear_pcollector);
00223 
00224   if ((!clearable->get_clear_color_active())&&
00225       (!clearable->get_clear_depth_active())&&
00226       (!clearable->get_clear_stencil_active())) {
00227     return;
00228   }
00229   
00230   set_state_and_transform(RenderState::make_empty(), _internal_transform);
00231 
00232   bool clear_color = false;
00233   int r, g, b, a;
00234   if (clearable->get_clear_color_active()) {
00235     LColor v = clearable->get_clear_color();
00236     r = (int)(v[0] * 0xffff);
00237     g = (int)(v[1] * 0xffff);
00238     b = (int)(v[2] * 0xffff);
00239     a = (int)(v[3] * 0xffff);
00240     clear_color = true;
00241   }
00242   
00243   bool clear_z = false;
00244   int z;
00245   if (clearable->get_clear_depth_active()) {
00246     // We ignore the specified depth clear value, since we don't
00247     // support alternate depth compare functions anyway.
00248     z = 0;
00249     clear_z = true;
00250   }
00251 
00252   ZB_clear_viewport(_c->zb, clear_z, z,
00253                     clear_color, r, g, b, a,
00254                     _c->viewport.xmin, _c->viewport.ymin,
00255                     _c->viewport.xsize, _c->viewport.ysize);
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: TinyGraphicsStateGuardian::prepare_display_region
00260 //       Access: Public, Virtual
00261 //  Description: Prepare a display region for rendering (set up
00262 //               scissor region and viewport)
00263 ////////////////////////////////////////////////////////////////////
00264 void TinyGraphicsStateGuardian::
00265 prepare_display_region(DisplayRegionPipelineReader *dr) {
00266   nassertv(dr != (DisplayRegionPipelineReader *)NULL);
00267   GraphicsStateGuardian::prepare_display_region(dr);
00268 
00269   int xmin, ymin, xsize, ysize;
00270   dr->get_region_pixels_i(xmin, ymin, xsize, ysize);
00271 
00272   PN_stdfloat pixel_factor = _current_display_region->get_pixel_factor();
00273   if (pixel_factor != 1.0) {
00274     // Render into an aux buffer, and zoom it up into the main
00275     // frame buffer later.
00276     xmin = 0;
00277     ymin = 0;
00278     xsize = int(xsize * pixel_factor);
00279     ysize = int(ysize * pixel_factor);
00280     if (_aux_frame_buffer == (ZBuffer *)NULL) {
00281       _aux_frame_buffer = ZB_open(xsize, ysize, ZB_MODE_RGBA, 0, 0, 0, 0);
00282     } else if (_aux_frame_buffer->xsize < xsize || _aux_frame_buffer->ysize < ysize) {
00283       ZB_resize(_aux_frame_buffer, NULL, 
00284                 max(_aux_frame_buffer->xsize, xsize),
00285                 max(_aux_frame_buffer->ysize, ysize));
00286     }
00287     _c->zb = _aux_frame_buffer;
00288 
00289   } else {
00290     // Render directly into the main frame buffer.
00291     _c->zb = _current_frame_buffer;
00292   }
00293 
00294   _c->viewport.xmin = xmin;
00295   _c->viewport.ymin = ymin;
00296   _c->viewport.xsize = xsize;
00297   _c->viewport.ysize = ysize;
00298   set_scissor(0.0f, 1.0f, 0.0f, 1.0f);
00299 
00300   nassertv(xmin >= 0 && xmin < _c->zb->xsize && 
00301            ymin >= 0 && ymin < _c->zb->ysize &&
00302            xmin + xsize >= 0 && xmin + xsize <= _c->zb->xsize &&
00303            ymin + ysize >= 0 && ymin + ysize <= _c->zb->ysize);
00304 }
00305 
00306 ////////////////////////////////////////////////////////////////////
00307 //     Function: TinyGraphicsStateGuardian::calc_projection_mat
00308 //       Access: Public, Virtual
00309 //  Description: Given a lens, calculates the appropriate projection
00310 //               matrix for use with this gsg.  Note that the
00311 //               projection matrix depends a lot upon the coordinate
00312 //               system of the rendering API.
00313 //
00314 //               The return value is a TransformState if the lens is
00315 //               acceptable, NULL if it is not.
00316 ////////////////////////////////////////////////////////////////////
00317 CPT(TransformState) TinyGraphicsStateGuardian::
00318 calc_projection_mat(const Lens *lens) {
00319   if (lens == (Lens *)NULL) {
00320     return NULL;
00321   }
00322 
00323   if (!lens->is_linear()) {
00324     return NULL;
00325   }
00326 
00327   // The projection matrix must always be right-handed Y-up, even if
00328   // our coordinate system of choice is otherwise, because certain GL
00329   // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of
00330   // a coordinate system.  Sigh.  In order to implement a Z-up (or
00331   // other arbitrary) coordinate system, we'll use a Y-up projection
00332   // matrix, and store the conversion to our coordinate system of
00333   // choice in the modelview matrix.
00334 
00335   LMatrix4 result =
00336     LMatrix4::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
00337     lens->get_projection_mat(_current_stereo_channel);
00338 
00339   if (_scene_setup->get_inverted()) {
00340     // If the scene is supposed to be inverted, then invert the
00341     // projection matrix.
00342     result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
00343   }
00344 
00345   return TransformState::make_mat(result);
00346 }
00347 
00348 ////////////////////////////////////////////////////////////////////
00349 //     Function: TinyGraphicsStateGuardian::prepare_lens
00350 //       Access: Public, Virtual
00351 //  Description: Makes the current lens (whichever lens was most
00352 //               recently specified with set_scene()) active, so
00353 //               that it will transform future rendered geometry.
00354 //               Normally this is only called from the draw process,
00355 //               and usually it is called by set_scene().
00356 //
00357 //               The return value is true if the lens is acceptable,
00358 //               false if it is not.
00359 ////////////////////////////////////////////////////////////////////
00360 bool TinyGraphicsStateGuardian::
00361 prepare_lens() {
00362   _transform_stale = true;
00363   return true;
00364 }
00365 
00366 ////////////////////////////////////////////////////////////////////
00367 //     Function: GraphicsStateGuardian::begin_frame
00368 //       Access: Public, Virtual
00369 //  Description: Called before each frame is rendered, to allow the
00370 //               GSG a chance to do any internal cleanup before
00371 //               beginning the frame.
00372 //
00373 //               The return value is true if successful (in which case
00374 //               the frame will be drawn and end_frame() will be
00375 //               called later), or false if unsuccessful (in which
00376 //               case nothing will be drawn and end_frame() will not
00377 //               be called).
00378 ////////////////////////////////////////////////////////////////////
00379 bool TinyGraphicsStateGuardian::
00380 begin_frame(Thread *current_thread) {
00381   if (!GraphicsStateGuardian::begin_frame(current_thread)) {
00382     return false;
00383   }
00384 
00385   _c->zb = _current_frame_buffer;
00386 
00387 #ifdef DO_PSTATS
00388   _vertices_immediate_pcollector.clear_level();
00389 
00390   _pixel_count_white_untextured_pcollector.clear_level();
00391   _pixel_count_flat_untextured_pcollector.clear_level();
00392   _pixel_count_smooth_untextured_pcollector.clear_level();
00393   _pixel_count_white_textured_pcollector.clear_level();
00394   _pixel_count_flat_textured_pcollector.clear_level();
00395   _pixel_count_smooth_textured_pcollector.clear_level();
00396   _pixel_count_white_perspective_pcollector.clear_level();
00397   _pixel_count_flat_perspective_pcollector.clear_level();
00398   _pixel_count_smooth_perspective_pcollector.clear_level();
00399   _pixel_count_smooth_multitex2_pcollector.clear_level();
00400   _pixel_count_smooth_multitex3_pcollector.clear_level();
00401 #endif
00402 
00403   return true;
00404 }
00405 
00406 ////////////////////////////////////////////////////////////////////
00407 //     Function: GraphicsStateGuardian::begin_scene
00408 //       Access: Public, Virtual
00409 //  Description: Called between begin_frame() and end_frame() to mark
00410 //               the beginning of drawing commands for a "scene"
00411 //               (usually a particular DisplayRegion) within a frame.
00412 //               All 3-D drawing commands, except the clear operation,
00413 //               must be enclosed within begin_scene() .. end_scene().
00414 //
00415 //               The return value is true if successful (in which case
00416 //               the scene will be drawn and end_scene() will be
00417 //               called later), or false if unsuccessful (in which
00418 //               case nothing will be drawn and end_scene() will not
00419 //               be called).
00420 ////////////////////////////////////////////////////////////////////
00421 bool TinyGraphicsStateGuardian::
00422 begin_scene() {
00423   return GraphicsStateGuardian::begin_scene();
00424 }
00425 
00426 ////////////////////////////////////////////////////////////////////
00427 //     Function: TinyGraphicsStateGuardian::end_scene
00428 //       Access: Protected, Virtual
00429 //  Description: Called between begin_frame() and end_frame() to mark
00430 //               the end of drawing commands for a "scene" (usually a
00431 //               particular DisplayRegion) within a frame.  All 3-D
00432 //               drawing commands, except the clear operation, must be
00433 //               enclosed within begin_scene() .. end_scene().
00434 ////////////////////////////////////////////////////////////////////
00435 void TinyGraphicsStateGuardian::
00436 end_scene() {
00437   if (_c->zb == _aux_frame_buffer) {
00438     // Copy the aux frame buffer into the main scene now, zooming it
00439     // up to the appropriate size.
00440     int xmin, ymin, xsize, ysize;
00441     _current_display_region->get_region_pixels_i(xmin, ymin, xsize, ysize);
00442     PN_stdfloat pixel_factor = _current_display_region->get_pixel_factor();
00443 
00444     int fb_xsize = int(xsize * pixel_factor);
00445     int fb_ysize = int(ysize * pixel_factor);
00446 
00447     ZB_zoomFrameBuffer(_current_frame_buffer, xmin, ymin, xsize, ysize,
00448                        _aux_frame_buffer, 0, 0, fb_xsize, fb_ysize);
00449     _c->zb = _current_frame_buffer;
00450   }
00451 
00452   // Clear the lighting state.
00453   clear_light_state();
00454   _plights.clear();
00455   _dlights.clear();
00456   _slights.clear();
00457 
00458   GraphicsStateGuardian::end_scene();
00459 }
00460 
00461 ////////////////////////////////////////////////////////////////////
00462 //     Function: TinyGraphicsStateGuardian::end_frame
00463 //       Access: Public, Virtual
00464 //  Description: Called after each frame is rendered, to allow the
00465 //               GSG a chance to do any internal cleanup after
00466 //               rendering the frame, and before the window flips.
00467 ////////////////////////////////////////////////////////////////////
00468 void TinyGraphicsStateGuardian::
00469 end_frame(Thread *current_thread) {
00470   GraphicsStateGuardian::end_frame(current_thread);
00471 
00472 #ifndef NDEBUG
00473   static ConfigVariableBool td_show_zbuffer
00474     ("td-show-zbuffer", false,
00475      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"));
00476   if (td_show_zbuffer) {
00477     PIXEL *tp = _current_frame_buffer->pbuf;
00478     ZPOINT *tz = _current_frame_buffer->zbuf;
00479     for (int yi = 0; yi < _current_frame_buffer->ysize; ++yi) {
00480       for (int xi = 0; xi < _current_frame_buffer->xsize; ++xi) {
00481         (*tp) = (int)(*tz);
00482         ++tz;
00483         ++tp;
00484       }
00485     }
00486   }
00487 #endif // NDEBUG
00488 
00489 #ifdef DO_PSTATS
00490   // Flush any PCollectors specific to this kind of GSG.
00491   _vertices_immediate_pcollector.flush_level();
00492 
00493   _pixel_count_white_untextured_pcollector.flush_level();
00494   _pixel_count_flat_untextured_pcollector.flush_level();
00495   _pixel_count_smooth_untextured_pcollector.flush_level();
00496   _pixel_count_white_textured_pcollector.flush_level();
00497   _pixel_count_flat_textured_pcollector.flush_level();
00498   _pixel_count_smooth_textured_pcollector.flush_level();
00499   _pixel_count_white_perspective_pcollector.flush_level();
00500   _pixel_count_flat_perspective_pcollector.flush_level();
00501   _pixel_count_smooth_perspective_pcollector.flush_level();
00502   _pixel_count_smooth_multitex2_pcollector.flush_level();
00503   _pixel_count_smooth_multitex3_pcollector.flush_level();
00504 #endif  // DO_PSTATS
00505 }
00506 
00507 
00508 ////////////////////////////////////////////////////////////////////
00509 //     Function: TinyGraphicsStateGuardian::begin_draw_primitives
00510 //       Access: Public, Virtual
00511 //  Description: Called before a sequence of draw_primitive()
00512 //               functions are called, this should prepare the vertex
00513 //               data for rendering.  It returns true if the vertices
00514 //               are ok, false to abort this group of primitives.
00515 ////////////////////////////////////////////////////////////////////
00516 bool TinyGraphicsStateGuardian::
00517 begin_draw_primitives(const GeomPipelineReader *geom_reader,
00518                       const GeomMunger *munger,
00519                       const GeomVertexDataPipelineReader *data_reader,
00520                       bool force) {
00521 #ifndef NDEBUG
00522   if (tinydisplay_cat.is_spam()) {
00523     tinydisplay_cat.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
00524   }
00525 #endif  // NDEBUG
00526 
00527   if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
00528     return false;
00529   }
00530   nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
00531 
00532   PStatTimer timer(_draw_transform_pcollector);
00533 
00534   // Set up the proper transform.
00535   if (_data_reader->is_vertex_transformed()) {
00536     // If the vertex data claims to be already transformed into clip
00537     // coordinates, wipe out the current projection and modelview
00538     // matrix (so we don't attempt to transform it again).
00539     const TransformState *ident = TransformState::make_identity();
00540     load_matrix(&_c->matrix_model_view, ident);
00541     load_matrix(&_c->matrix_projection, _scissor_mat);
00542     load_matrix(&_c->matrix_model_view_inv, ident);
00543     load_matrix(&_c->matrix_model_projection, _scissor_mat);
00544     _c->matrix_model_projection_no_w_transform = 1;
00545     _transform_stale = true;
00546 
00547   } else if (_transform_stale) {
00548     // Load the actual transform.
00549 
00550     CPT(TransformState) scissor_proj_mat = _scissor_mat->compose(_projection_mat);
00551 
00552     if (_c->lighting_enabled) {
00553       // With the lighting equation, we need to keep the modelview and
00554       // projection matrices separate.
00555 
00556       load_matrix(&_c->matrix_model_view, _internal_transform);
00557       load_matrix(&_c->matrix_projection, scissor_proj_mat);
00558 
00559       /* precompute inverse modelview */
00560       M4 tmp;
00561       gl_M4_Inv(&tmp, &_c->matrix_model_view);
00562       gl_M4_Transpose(&_c->matrix_model_view_inv, &tmp);
00563 
00564     }
00565 
00566     // Compose the modelview and projection matrices.
00567     load_matrix(&_c->matrix_model_projection, 
00568                 scissor_proj_mat->compose(_internal_transform));
00569 
00570     /* test to accelerate computation */
00571     _c->matrix_model_projection_no_w_transform = 0;
00572     PN_stdfloat *m = &_c->matrix_model_projection.m[0][0];
00573     if (m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0) {
00574       _c->matrix_model_projection_no_w_transform = 1;
00575     }
00576     _transform_stale = false;
00577   }
00578 
00579   // Figure out the subset of vertices we will be using in this
00580   // operation.
00581   int num_vertices = data_reader->get_num_rows();
00582   _min_vertex = num_vertices;
00583   _max_vertex = 0;
00584   int num_prims = geom_reader->get_num_primitives();
00585   int i;
00586   for (i = 0; i < num_prims; ++i) {
00587     CPT(GeomPrimitive) prim = geom_reader->get_primitive(i);
00588     int nv = prim->get_min_vertex();
00589     _min_vertex = min(_min_vertex, nv);
00590     int xv = prim->get_max_vertex();
00591     _max_vertex = max(_max_vertex, xv);
00592   }
00593   if (_min_vertex > _max_vertex) {
00594     return false;
00595   }
00596 
00597   // Now copy all of those vertices into our working table,
00598   // transforming into screen space them as we go.
00599   int num_used_vertices = _max_vertex - _min_vertex + 1;
00600   if (_vertices_size < num_used_vertices) {
00601     if (_vertices_size == 0) {
00602       _vertices_size = 1;
00603     }
00604     while (_vertices_size < num_used_vertices) {
00605       _vertices_size *= 2;
00606     }
00607     if (_vertices != (GLVertex *)NULL) {
00608       PANDA_FREE_ARRAY(_vertices);
00609     }
00610     _vertices = (GLVertex *)PANDA_MALLOC_ARRAY(_vertices_size * sizeof(GLVertex));
00611   }
00612 
00613   GeomVertexReader  rcolor, rnormal;
00614 
00615   // We now support up to 3-stage multitexturing.
00616   GenTexcoordFunc *texgen_func[MAX_TEXTURE_STAGES];
00617   TexCoordData tcdata[MAX_TEXTURE_STAGES];
00618 
00619   const TexGenAttrib *target_tex_gen = DCAST(TexGenAttrib, _target_rs->get_attrib_def(TexGenAttrib::get_class_slot()));
00620   const TexMatrixAttrib *target_tex_matrix = DCAST(TexMatrixAttrib, _target_rs->get_attrib_def(TexMatrixAttrib::get_class_slot()));
00621 
00622   int max_stage_index = _target_texture->get_num_on_ff_stages();
00623   for (int si = 0; si < max_stage_index; ++si) {
00624     TextureStage *stage = _target_texture->get_on_ff_stage(si);
00625 
00626     switch (target_tex_gen->get_mode(stage)) {
00627     case TexGenAttrib::M_eye_sphere_map:
00628       tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_normal(),
00629                                         force);
00630       tcdata[si]._r2 = GeomVertexReader(data_reader, InternalName::get_vertex(),
00631                                         force);
00632       texgen_func[si] = &texgen_sphere_map;
00633       tcdata[si]._mat = _internal_transform->get_mat();
00634       break;
00635 
00636     case TexGenAttrib::M_eye_position:
00637       tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_vertex(),
00638                                        force);
00639       texgen_func[si] = &texgen_texmat;
00640       {
00641         CPT(TransformState) eye_transform =
00642           _cs_transform->invert_compose(_internal_transform);
00643         tcdata[si]._mat = eye_transform->get_mat();
00644       }
00645       if (target_tex_matrix->has_stage(stage)) {
00646         tcdata[si]._mat = tcdata[si]._mat * target_tex_matrix->get_mat(stage);
00647       }
00648       break;
00649 
00650     case TexGenAttrib::M_world_position:
00651       tcdata[si]._r1 = GeomVertexReader(data_reader, InternalName::get_vertex(),
00652                                        force);
00653       texgen_func[si] = &texgen_texmat;
00654       {
00655         CPT(TransformState) render_transform =
00656           _cs_transform->compose(_scene_setup->get_world_transform());
00657         CPT(TransformState) world_inv_transform = 
00658           render_transform->invert_compose(_internal_transform);
00659         tcdata[si]._mat = world_inv_transform->get_mat();
00660       }
00661       if (target_tex_matrix->has_stage(stage)) {
00662         tcdata[si]._mat = tcdata[si]._mat * target_tex_matrix->get_mat(stage);
00663       }
00664       break;
00665 
00666     default:
00667       // Fall through: use the standard texture coordinates.
00668       tcdata[si]._r1 = GeomVertexReader(data_reader, stage->get_texcoord_name(),
00669                                        force);
00670       texgen_func[si] = &texgen_simple;
00671       if (target_tex_matrix->has_stage(stage)) {
00672         texgen_func[si] = &texgen_texmat;
00673         tcdata[si]._mat = target_tex_matrix->get_mat(stage);
00674       }
00675 
00676       break;
00677     }
00678     tcdata[si]._r1.set_row_unsafe(_min_vertex);
00679     tcdata[si]._r2.set_row_unsafe(_min_vertex);
00680     if (!tcdata[si]._r1.has_column()) {
00681       texgen_func[si] = &texgen_null;
00682     }
00683   }
00684 
00685   bool needs_color = false;
00686   if (_vertex_colors_enabled) {
00687     rcolor = GeomVertexReader(data_reader, InternalName::get_color(), force);
00688     rcolor.set_row_unsafe(_min_vertex);
00689     needs_color = rcolor.has_column();
00690   }
00691 
00692   if (!needs_color) {
00693     const LColor &d = _scene_graph_color;
00694     const LColor &s = _current_color_scale;
00695     _c->current_color.v[0] = d[0] * s[0];
00696     _c->current_color.v[1] = d[1] * s[1];
00697     _c->current_color.v[2] = d[2] * s[2];
00698     _c->current_color.v[3] = d[3] * s[3];
00699   }
00700 
00701   bool needs_normal = false;
00702   if (_c->lighting_enabled) {
00703     rnormal = GeomVertexReader(data_reader, InternalName::get_normal(), force);
00704     rnormal.set_row_unsafe(_min_vertex);
00705     needs_normal = rnormal.has_column();
00706   }
00707 
00708   GeomVertexReader rvertex(data_reader, InternalName::get_vertex(), force); 
00709   rvertex.set_row_unsafe(_min_vertex);
00710 
00711   if (!rvertex.has_column()) {
00712     // Whoops, guess the vertex data isn't resident.
00713     return false;
00714   }
00715 
00716   if (!needs_color && _color_material_flags) {
00717     if (_color_material_flags & CMF_ambient) {
00718       _c->materials[0].ambient = _c->current_color;
00719       _c->materials[1].ambient = _c->current_color;
00720     }
00721     if (_color_material_flags & CMF_diffuse) {
00722       _c->materials[0].diffuse = _c->current_color;
00723       _c->materials[1].diffuse = _c->current_color;
00724     }
00725   }
00726 
00727   if (_texturing_state != 0 && _texture_replace) {
00728     // We don't need the vertex color or lighting calculation after
00729     // all, since the current texture will just hide all of that.
00730     needs_color = false;
00731     needs_normal = false;
00732   }
00733 
00734   bool lighting_enabled = (needs_normal && _c->lighting_enabled);
00735 
00736   for (i = 0; i < num_used_vertices; ++i) {
00737     GLVertex *v = &_vertices[i];
00738     const LVecBase4 &d = rvertex.get_data4();
00739     
00740     v->coord.v[0] = d[0];
00741     v->coord.v[1] = d[1];
00742     v->coord.v[2] = d[2];
00743     v->coord.v[3] = d[3];
00744 
00745     // Texture coordinates.
00746     for (int si = 0; si < max_stage_index; ++si) {
00747       LTexCoord d;
00748       (*texgen_func[si])(v->tex_coord[si], tcdata[si]);
00749     }
00750 
00751     if (needs_color) {
00752       const LColor &d = rcolor.get_data4();
00753       const LColor &s = _current_color_scale;
00754       _c->current_color.v[0] = d[0] * s[0];
00755       _c->current_color.v[1] = d[1] * s[1];
00756       _c->current_color.v[2] = d[2] * s[2];
00757       _c->current_color.v[3] = d[3] * s[3];
00758       
00759       if (_color_material_flags) {
00760         if (_color_material_flags & CMF_ambient) {
00761           _c->materials[0].ambient = _c->current_color;
00762           _c->materials[1].ambient = _c->current_color;
00763         }
00764         if (_color_material_flags & CMF_diffuse) {
00765           _c->materials[0].diffuse = _c->current_color;
00766           _c->materials[1].diffuse = _c->current_color;
00767         }
00768       }
00769     }
00770 
00771     v->color = _c->current_color;
00772 
00773     if (lighting_enabled) {
00774       const LVecBase3 &d = rnormal.get_data3();
00775       _c->current_normal.v[0] = d[0];
00776       _c->current_normal.v[1] = d[1];
00777       _c->current_normal.v[2] = d[2];
00778       _c->current_normal.v[3] = 0.0f;
00779 
00780       gl_vertex_transform(_c, v);
00781       gl_shade_vertex(_c, v);
00782 
00783     } else {
00784       gl_vertex_transform(_c, v);
00785     }
00786 
00787     if (v->clip_code == 0) {
00788       gl_transform_to_viewport(_c, v);
00789     }
00790 
00791     v->edge_flag = 1;
00792   }
00793 
00794   // Set up the appropriate function callback for filling triangles,
00795   // according to the current state.
00796 
00797   int depth_write_state = 0;  // zon
00798   const DepthWriteAttrib *target_depth_write = DCAST(DepthWriteAttrib, _target_rs->get_attrib_def(DepthWriteAttrib::get_class_slot()));
00799   if (target_depth_write->get_mode() != DepthWriteAttrib::M_on) {
00800     depth_write_state = 1;  // zoff
00801   }
00802 
00803   int color_write_state = 0;  // cstore
00804 
00805   const ColorWriteAttrib *target_color_write = DCAST(ColorWriteAttrib, _target_rs->get_attrib_def(ColorWriteAttrib::get_class_slot()));
00806   unsigned int color_channels =
00807     target_color_write->get_channels() & _color_write_mask;
00808   if (color_channels != ColorWriteAttrib::C_all) {
00809     // Implement a color mask.
00810     int op_a = get_color_blend_op(ColorBlendAttrib::O_one);
00811     int op_b = get_color_blend_op(ColorBlendAttrib::O_zero);
00812     _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
00813     color_write_state = 2;   // cgeneral
00814   }
00815 
00816   const TransparencyAttrib *target_transparency = DCAST(TransparencyAttrib, _target_rs->get_attrib_def(TransparencyAttrib::get_class_slot()));
00817   switch (target_transparency->get_mode()) {
00818   case TransparencyAttrib::M_alpha:
00819   case TransparencyAttrib::M_dual:
00820     color_write_state = 1;    // cblend
00821     if (color_channels != ColorWriteAttrib::C_all) {
00822       // Implement a color mask, with alpha blending.
00823       int op_a = get_color_blend_op(ColorBlendAttrib::O_incoming_alpha);
00824       int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha);
00825       _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
00826       color_write_state = 2;   // cgeneral
00827     }
00828     break;
00829 
00830   default:
00831     break;
00832   }
00833 
00834   const ColorBlendAttrib *target_color_blend = DCAST(ColorBlendAttrib, _target_rs->get_attrib_def(ColorBlendAttrib::get_class_slot()));
00835   if (target_color_blend->get_mode() == ColorBlendAttrib::M_add) {
00836     // If we have a color blend set that we can support, it overrides
00837     // the transparency set.
00838     int op_a = get_color_blend_op(target_color_blend->get_operand_a());
00839     int op_b = get_color_blend_op(target_color_blend->get_operand_b());
00840     _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
00841     LColor c = target_color_blend->get_color();
00842     _c->zb->blend_r = (int)(c[0] * ZB_POINT_RED_MAX);
00843     _c->zb->blend_g = (int)(c[1] * ZB_POINT_GREEN_MAX);
00844     _c->zb->blend_b = (int)(c[2] * ZB_POINT_BLUE_MAX);
00845     _c->zb->blend_a = (int)(c[3] * ZB_POINT_ALPHA_MAX);
00846 
00847     color_write_state = 2;     // cgeneral
00848   }
00849 
00850   if (color_channels == ColorWriteAttrib::C_off) {
00851     color_write_state = 3;    // coff
00852   }
00853 
00854   int alpha_test_state = 0;   // anone
00855   const AlphaTestAttrib *target_alpha_test = DCAST(AlphaTestAttrib, _target_rs->get_attrib_def(AlphaTestAttrib::get_class_slot()));
00856   switch (target_alpha_test->get_mode()) {
00857   case AlphaTestAttrib::M_none:
00858   case AlphaTestAttrib::M_never:
00859   case AlphaTestAttrib::M_always:
00860   case AlphaTestAttrib::M_equal:
00861   case AlphaTestAttrib::M_not_equal:
00862     alpha_test_state = 0;    // anone
00863     break;
00864 
00865   case AlphaTestAttrib::M_less:
00866   case AlphaTestAttrib::M_less_equal:
00867     alpha_test_state = 1;    // aless
00868     _c->zb->reference_alpha = (int)(target_alpha_test->get_reference_alpha() * ZB_POINT_ALPHA_MAX);
00869     break;
00870 
00871   case AlphaTestAttrib::M_greater:
00872   case AlphaTestAttrib::M_greater_equal:
00873     alpha_test_state = 2;    // amore
00874     _c->zb->reference_alpha = (int)(target_alpha_test->get_reference_alpha() * ZB_POINT_ALPHA_MAX);
00875     break;
00876   }
00877 
00878   int depth_test_state = 1;    // zless
00879   _c->depth_test = 1;  // set this for ZB_line
00880   const DepthTestAttrib *target_depth_test = DCAST(DepthTestAttrib, _target_rs->get_attrib_def(DepthTestAttrib::get_class_slot()));
00881   if (target_depth_test->get_mode() == DepthTestAttrib::M_none) {
00882     depth_test_state = 0;      // zless
00883     _c->depth_test = 0;
00884   }
00885   
00886   const ShadeModelAttrib *target_shade_model = DCAST(ShadeModelAttrib, _target_rs->get_attrib_def(ShadeModelAttrib::get_class_slot()));
00887   ShadeModelAttrib::Mode shade_model = target_shade_model->get_mode();
00888   if (!needs_normal && !needs_color) {
00889     // With no per-vertex lighting, and no per-vertex colors, we might
00890     // as well use the flat shading model.
00891     shade_model = ShadeModelAttrib::M_flat;
00892   }
00893   int shade_model_state = 2;  // smooth
00894   _c->smooth_shade_model = true;
00895 
00896   if (shade_model == ShadeModelAttrib::M_flat) {
00897     _c->smooth_shade_model = false;
00898     shade_model_state = 1;  // flat
00899     if (_c->current_color.v[0] == 1.0f &&
00900         _c->current_color.v[1] == 1.0f &&
00901         _c->current_color.v[2] == 1.0f &&
00902         _c->current_color.v[3] == 1.0f) {
00903       shade_model_state = 0;  // white
00904     }
00905   }
00906 
00907   int texturing_state = _texturing_state;
00908   int texfilter_state = 0;  // tnearest
00909   if (texturing_state > 0) {
00910     texfilter_state = _texfilter_state;
00911 
00912     if (texturing_state < 3 &&
00913         (_c->matrix_model_projection_no_w_transform || _filled_flat)) {
00914       // Don't bother with the perspective-correct algorithm if we're
00915       // under an orthonormal lens, e.g. render2d; or if
00916       // RenderMode::M_filled_flat is in effect.
00917       texturing_state = 1;    // textured (not perspective correct)
00918     }
00919 
00920     if (_texture_replace) {
00921       // If we're completely replacing the underlying color, then it
00922       // doesn't matter what the color is.
00923       shade_model_state = 0;
00924     }
00925   }
00926 
00927   _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];
00928 
00929 #ifdef DO_PSTATS
00930   pixel_count_white_untextured = 0;
00931   pixel_count_flat_untextured = 0;
00932   pixel_count_smooth_untextured = 0;
00933   pixel_count_white_textured = 0;
00934   pixel_count_flat_textured = 0;
00935   pixel_count_smooth_textured = 0;
00936   pixel_count_white_perspective = 0;
00937   pixel_count_flat_perspective = 0;
00938   pixel_count_smooth_perspective = 0;
00939   pixel_count_smooth_multitex2 = 0;
00940   pixel_count_smooth_multitex3 = 0;
00941 #endif  // DO_PSTATS
00942   
00943   return true;
00944 }
00945 
00946 ////////////////////////////////////////////////////////////////////
00947 //     Function: TinyGraphicsStateGuardian::draw_triangles
00948 //       Access: Public, Virtual
00949 //  Description: Draws a series of disconnected triangles.
00950 ////////////////////////////////////////////////////////////////////
00951 bool TinyGraphicsStateGuardian::
00952 draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
00953   PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
00954 
00955 #ifndef NDEBUG
00956   if (tinydisplay_cat.is_spam()) {
00957     tinydisplay_cat.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
00958   }
00959 #endif  // NDEBUG
00960 
00961   int num_vertices = reader->get_num_vertices();
00962   _vertices_tri_pcollector.add_level(num_vertices);
00963 
00964   if (reader->is_indexed()) {
00965     switch (reader->get_index_type()) {
00966     case Geom::NT_uint8:
00967       {
00968         PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
00969         if (index == NULL) {
00970           return false;
00971         }
00972         for (int i = 0; i < num_vertices; i += 3) {
00973           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
00974           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
00975           GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
00976           gl_draw_triangle(_c, v0, v1, v2);
00977         }
00978       }
00979       break;
00980 
00981     case Geom::NT_uint16:
00982       {
00983         PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
00984         if (index == NULL) {
00985           return false;
00986         }
00987         for (int i = 0; i < num_vertices; i += 3) {
00988           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
00989           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
00990           GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
00991           gl_draw_triangle(_c, v0, v1, v2);
00992         }
00993       }
00994       break;
00995 
00996     case Geom::NT_uint32:
00997       {
00998         PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
00999         if (index == NULL) {
01000           return false;
01001         }
01002         for (int i = 0; i < num_vertices; i += 3) {
01003           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01004           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
01005           GLVertex *v2 = &_vertices[index[i + 2] - _min_vertex];
01006           gl_draw_triangle(_c, v0, v1, v2);
01007         }
01008       }
01009       break;
01010 
01011     default:
01012       break;
01013     }
01014 
01015   } else {
01016     int delta = reader->get_first_vertex() - _min_vertex;
01017     for (int vi = 0; vi < num_vertices; vi += 3) {
01018       GLVertex *v0 = &_vertices[vi + delta];
01019       GLVertex *v1 = &_vertices[vi + delta + 1];
01020       GLVertex *v2 = &_vertices[vi + delta + 2];
01021       gl_draw_triangle(_c, v0, v1, v2);
01022     }
01023   }
01024 
01025   return true;
01026 }
01027 
01028 ////////////////////////////////////////////////////////////////////
01029 //     Function: TinyGraphicsStateGuardian::draw_tristrips
01030 //       Access: Public, Virtual
01031 //  Description: Draws a series of triangle strips.
01032 ////////////////////////////////////////////////////////////////////
01033 bool TinyGraphicsStateGuardian::
01034 draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
01035   PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
01036 
01037 #ifndef NDEBUG
01038   if (tinydisplay_cat.is_spam()) {
01039     tinydisplay_cat.spam() << "draw_tristrips: " << *(reader->get_object()) << "\n";
01040   }
01041 #endif  // NDEBUG
01042 
01043   // Send the individual triangle strips, stepping over the
01044   // degenerate vertices.
01045   CPTA_int ends = reader->get_ends();
01046 
01047   _primitive_batches_tristrip_pcollector.add_level(ends.size());
01048   if (reader->is_indexed()) {
01049     unsigned int start = 0;
01050     for (size_t i = 0; i < ends.size(); i++) {
01051       _vertices_tristrip_pcollector.add_level(ends[i] - start);
01052 
01053       int end = ends[i];
01054       nassertr(end - start >= 3, false);
01055 
01056       switch (reader->get_index_type()) {
01057       case Geom::NT_uint8:
01058         {
01059           PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
01060           if (index == NULL) {
01061             return false;
01062           }
01063           GLVertex *v0 = &_vertices[index[start] - _min_vertex];
01064           GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
01065 
01066           bool reversed = false;
01067           for (int vi = start + 2; vi < end; ++vi) {
01068             GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
01069             if (reversed) {
01070               gl_draw_triangle(_c, v1, v0, v2);
01071               reversed = false;
01072             } else {
01073               gl_draw_triangle(_c, v0, v1, v2);
01074               reversed = true;
01075             }
01076             v0 = v1;
01077             v1 = v2;
01078           }
01079         }
01080         break;
01081 
01082       case Geom::NT_uint16:
01083         {
01084           PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
01085           if (index == NULL) {
01086             return false;
01087           }
01088           GLVertex *v0 = &_vertices[index[start] - _min_vertex];
01089           GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
01090 
01091           bool reversed = false;
01092           for (int vi = start + 2; vi < end; ++vi) {
01093             GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
01094             if (reversed) {
01095               gl_draw_triangle(_c, v1, v0, v2);
01096               reversed = false;
01097             } else {
01098               gl_draw_triangle(_c, v0, v1, v2);
01099               reversed = true;
01100             }
01101             v0 = v1;
01102             v1 = v2;
01103           }
01104         }
01105         break;
01106 
01107       case Geom::NT_uint32:
01108         {
01109           PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
01110           if (index == NULL) {
01111             return false;
01112           }
01113           GLVertex *v0 = &_vertices[index[start] - _min_vertex];
01114           GLVertex *v1 = &_vertices[index[start + 1] - _min_vertex];
01115 
01116           bool reversed = false;
01117           for (int vi = start + 2; vi < end; ++vi) {
01118             GLVertex *v2 = &_vertices[index[vi] - _min_vertex];
01119             if (reversed) {
01120               gl_draw_triangle(_c, v1, v0, v2);
01121               reversed = false;
01122             } else {
01123               gl_draw_triangle(_c, v0, v1, v2);
01124               reversed = true;
01125             }
01126             v0 = v1;
01127             v1 = v2;
01128           }
01129         }
01130         break;
01131       }
01132 
01133       start = ends[i] + 2;
01134     }
01135   } else {
01136     unsigned int start = 0;
01137     int delta = reader->get_first_vertex() - _min_vertex;
01138     for (size_t i = 0; i < ends.size(); i++) {
01139       _vertices_tristrip_pcollector.add_level(ends[i] - start);
01140 
01141       int end = ends[i];
01142       nassertr(end - start >= 3, false);
01143       GLVertex *v0 = &_vertices[start + delta];
01144       GLVertex *v1 = &_vertices[start + delta + 1];
01145 
01146       bool reversed = false;
01147       for (int vi = start + 2; vi < end; ++vi) {
01148         GLVertex *v2 = &_vertices[vi + delta];
01149         if (reversed) {
01150           gl_draw_triangle(_c, v1, v0, v2);
01151           reversed = false;
01152         } else {
01153           gl_draw_triangle(_c, v0, v1, v2);
01154           reversed = true;
01155         }
01156         v0 = v1;
01157         v1 = v2;
01158       }
01159       start = ends[i] + 2;
01160     }
01161   }
01162 
01163   return true;
01164 }
01165 
01166 ////////////////////////////////////////////////////////////////////
01167 //     Function: TinyGraphicsStateGuardian::draw_lines
01168 //       Access: Public, Virtual
01169 //  Description: Draws a series of disconnected line segments.
01170 ////////////////////////////////////////////////////////////////////
01171 bool TinyGraphicsStateGuardian::
01172 draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
01173   PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
01174 #ifndef NDEBUG
01175   if (tinydisplay_cat.is_spam()) {
01176     tinydisplay_cat.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
01177   }
01178 #endif  // NDEBUG
01179 
01180   int num_vertices = reader->get_num_vertices();
01181   _vertices_other_pcollector.add_level(num_vertices);
01182 
01183   if (reader->is_indexed()) {
01184     switch (reader->get_index_type()) {
01185     case Geom::NT_uint8:
01186       {
01187         PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
01188         if (index == NULL) {
01189           return false;
01190         }
01191         for (int i = 0; i < num_vertices; i += 2) {
01192           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01193           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
01194           gl_draw_line(_c, v0, v1);
01195         }
01196       }
01197       break;
01198 
01199     case Geom::NT_uint16:
01200       {
01201         PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
01202         if (index == NULL) {
01203           return false;
01204         }
01205         for (int i = 0; i < num_vertices; i += 2) {
01206           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01207           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
01208           gl_draw_line(_c, v0, v1);
01209         }
01210       }
01211       break;
01212 
01213     case Geom::NT_uint32:
01214       {
01215         PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
01216         if (index == NULL) {
01217           return false;
01218         }
01219         for (int i = 0; i < num_vertices; i += 2) {
01220           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01221           GLVertex *v1 = &_vertices[index[i + 1] - _min_vertex];
01222           gl_draw_line(_c, v0, v1);
01223         }
01224       }
01225       break;
01226 
01227     default:
01228       break;
01229     }
01230 
01231   } else {
01232     int delta = reader->get_first_vertex() - _min_vertex;
01233     for (int vi = 0; vi < num_vertices; vi += 2) {
01234       GLVertex *v0 = &_vertices[vi + delta];
01235       GLVertex *v1 = &_vertices[vi + delta + 1];
01236       gl_draw_line(_c, v0, v1);
01237     }
01238   }
01239 
01240   return true;
01241 }
01242 
01243 ////////////////////////////////////////////////////////////////////
01244 //     Function: TinyGraphicsStateGuardian::draw_points
01245 //       Access: Public, Virtual
01246 //  Description: Draws a series of disconnected points.
01247 ////////////////////////////////////////////////////////////////////
01248 bool TinyGraphicsStateGuardian::
01249 draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
01250   PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
01251 #ifndef NDEBUG
01252   if (tinydisplay_cat.is_spam()) {
01253     tinydisplay_cat.spam() << "draw_points: " << *(reader->get_object()) << "\n";
01254   }
01255 #endif  // NDEBUG
01256 
01257   int num_vertices = reader->get_num_vertices();
01258   _vertices_other_pcollector.add_level(num_vertices);
01259 
01260   if (reader->is_indexed()) {
01261     switch (reader->get_index_type()) {
01262     case Geom::NT_uint8:
01263       {
01264         PN_uint8 *index = (PN_uint8 *)reader->get_read_pointer(force);
01265         if (index == NULL) {
01266           return false;
01267         }
01268         for (int i = 0; i < num_vertices; ++i) {
01269           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01270           gl_draw_point(_c, v0);
01271         }
01272       }
01273       break;
01274 
01275     case Geom::NT_uint16:
01276       {
01277         PN_uint16 *index = (PN_uint16 *)reader->get_read_pointer(force);
01278         if (index == NULL) {
01279           return false;
01280         }
01281         for (int i = 0; i < num_vertices; ++i) {
01282           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01283           gl_draw_point(_c, v0);
01284         }
01285       }
01286       break;
01287 
01288     case Geom::NT_uint32:
01289       {
01290         PN_uint32 *index = (PN_uint32 *)reader->get_read_pointer(force);
01291         if (index == NULL) {
01292           return false;
01293         }
01294         for (int i = 0; i < num_vertices; ++i) {
01295           GLVertex *v0 = &_vertices[index[i] - _min_vertex];
01296           gl_draw_point(_c, v0);
01297         }
01298       }
01299       break;
01300 
01301     default:
01302       break;
01303     }
01304 
01305   } else {
01306     int delta = reader->get_first_vertex() - _min_vertex;
01307     for (int vi = 0; vi < num_vertices; ++vi) {
01308       GLVertex *v0 = &_vertices[vi + delta];
01309       gl_draw_point(_c, v0);
01310     }
01311   }
01312 
01313   return true;
01314 }
01315 
01316 ////////////////////////////////////////////////////////////////////
01317 //     Function: TinyGraphicsStateGuardian::end_draw_primitives()
01318 //       Access: Public, Virtual
01319 //  Description: Called after a sequence of draw_primitive()
01320 //               functions are called, this should do whatever cleanup
01321 //               is appropriate.
01322 ////////////////////////////////////////////////////////////////////
01323 void TinyGraphicsStateGuardian::
01324 end_draw_primitives() {
01325 
01326 #ifdef DO_PSTATS
01327   _pixel_count_white_untextured_pcollector.add_level(pixel_count_white_untextured);
01328   _pixel_count_flat_untextured_pcollector.add_level(pixel_count_flat_untextured);
01329   _pixel_count_smooth_untextured_pcollector.add_level(pixel_count_smooth_untextured);
01330   _pixel_count_white_textured_pcollector.add_level(pixel_count_white_textured);
01331   _pixel_count_flat_textured_pcollector.add_level(pixel_count_flat_textured);
01332   _pixel_count_smooth_textured_pcollector.add_level(pixel_count_smooth_textured);
01333   _pixel_count_white_perspective_pcollector.add_level(pixel_count_white_perspective);
01334   _pixel_count_flat_perspective_pcollector.add_level(pixel_count_flat_perspective);
01335   _pixel_count_smooth_perspective_pcollector.add_level(pixel_count_smooth_perspective);
01336   _pixel_count_smooth_multitex2_pcollector.add_level(pixel_count_smooth_multitex2);
01337   _pixel_count_smooth_multitex3_pcollector.add_level(pixel_count_smooth_multitex3);
01338 #endif  // DO_PSTATS
01339 
01340   GraphicsStateGuardian::end_draw_primitives();
01341 }
01342 
01343 ////////////////////////////////////////////////////////////////////
01344 //     Function: TinyGraphicsStateGuardian::framebuffer_copy_to_texture
01345 //       Access: Public, Virtual
01346 //  Description: Copy the pixels within the indicated display
01347 //               region from the framebuffer into texture memory.
01348 //
01349 //               If z > -1, it is the cube map index into which to
01350 //               copy.
01351 ////////////////////////////////////////////////////////////////////
01352 bool TinyGraphicsStateGuardian::
01353 framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
01354                             const RenderBuffer &rb) {
01355   nassertr(tex != NULL && dr != NULL, false);
01356   
01357   int xo, yo, w, h;
01358   dr->get_region_pixels_i(xo, yo, w, h);
01359 
01360   tex->setup_2d_texture(w, h, Texture::T_unsigned_byte, Texture::F_rgba);
01361 
01362   int view = dr->get_tex_view_offset();
01363   TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
01364   nassertr(tc != (TextureContext *)NULL, false);
01365   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
01366 
01367   GLTexture *gltex = &gtc->_gltex;
01368   if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), 1)) {
01369     return false;
01370   }
01371   LColor border_color = tex->get_border_color();
01372   gltex->border_color.v[0] = border_color[0];
01373   gltex->border_color.v[1] = border_color[1];
01374   gltex->border_color.v[2] = border_color[2];
01375   gltex->border_color.v[3] = border_color[3];
01376 
01377   PIXEL *ip = gltex->levels[0].pixmap + gltex->xsize * gltex->ysize;
01378   PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB;
01379   for (int y = 0; y < gltex->ysize; ++y) {
01380     ip -= gltex->xsize;
01381     memcpy(ip, fo, gltex->xsize * PSZB);
01382     fo += _c->zb->linesize / PSZB;
01383   }
01384 
01385   gtc->update_data_size_bytes(gltex->xsize * gltex->ysize * 4);
01386   gtc->mark_loaded();
01387   gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
01388 
01389   return true;
01390 }
01391 
01392 
01393 ////////////////////////////////////////////////////////////////////
01394 //     Function: TinyGraphicsStateGuardian::framebuffer_copy_to_ram
01395 //       Access: Public, Virtual
01396 //  Description: Copy the pixels within the indicated display region
01397 //               from the framebuffer into system memory, not texture
01398 //               memory.  Returns true on success, false on failure.
01399 //
01400 //               This completely redefines the ram image of the
01401 //               indicated texture.
01402 ////////////////////////////////////////////////////////////////////
01403 bool TinyGraphicsStateGuardian::
01404 framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
01405                         const RenderBuffer &rb) {
01406   nassertr(tex != NULL && dr != NULL, false);
01407   
01408   int xo, yo, w, h;
01409   dr->get_region_pixels_i(xo, yo, w, h);
01410 
01411   Texture::TextureType texture_type;
01412   int z_size;
01413   if (z >= 0) {
01414     texture_type = Texture::TT_cube_map;
01415     z_size = 6;
01416   } else {
01417     texture_type = Texture::TT_2d_texture;
01418     z_size = 1;
01419   }
01420 
01421   Texture::ComponentType component_type = Texture::T_unsigned_byte;
01422   Texture::Format format = Texture::F_rgba;
01423 
01424   if (tex->get_x_size() != w || tex->get_y_size() != h ||
01425       tex->get_z_size() != z_size ||
01426       tex->get_component_type() != component_type ||
01427       tex->get_format() != format ||
01428       tex->get_texture_type() != texture_type) {
01429     // Re-setup the texture; its properties have changed.
01430     tex->setup_texture(texture_type, w, h, z_size,
01431                        component_type, format);
01432   }
01433 
01434   unsigned char *image_ptr = tex->modify_ram_image();
01435   size_t image_size = tex->get_ram_image_size();
01436   if (z >= 0) {
01437     nassertr(z < tex->get_z_size(), false);
01438     image_size = tex->get_expected_ram_page_size();
01439     image_ptr += z * image_size;
01440   }
01441 
01442   PIXEL *ip = (PIXEL *)(image_ptr + image_size);
01443   PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB;
01444   for (int y = 0; y < h; ++y) {
01445     ip -= w;
01446 #ifndef WORDS_BIGENDIAN
01447     // On a little-endian machine, we can copy the whole row at a time.
01448     memcpy(ip, fo, w * PSZB);
01449 #else
01450     // On a big-endian machine, we have to reverse the color-component order.
01451     const char *source = (const char *)fo;
01452     const char *stop = (const char *)fo + w * PSZB;
01453     char *dest = (char *)ip;
01454     while (source < stop) {
01455       char b = source[0];
01456       char g = source[1];
01457       char r = source[2];
01458       char a = source[3];
01459       dest[0] = a;
01460       dest[1] = r;
01461       dest[2] = g;
01462       dest[3] = b;
01463       dest += 4;
01464       source += 4;
01465     }
01466 #endif
01467     fo += _c->zb->linesize / PSZB;
01468   }
01469 
01470   return true;
01471 }
01472 
01473 ////////////////////////////////////////////////////////////////////
01474 //     Function: TinyGraphicsStateGuardian::set_state_and_transform
01475 //       Access: Public, Virtual
01476 //  Description: Simultaneously resets the render state and the
01477 //               transform state.
01478 //
01479 //               This transform specified is the "internal" net
01480 //               transform, already converted into the GSG's internal
01481 //               coordinate space by composing it to
01482 //               get_cs_transform().  (Previously, this used to be the
01483 //               "external" net transform, with the assumption that
01484 //               that GSG would convert it internally, but that is no
01485 //               longer the case.)
01486 //
01487 //               Special case: if (state==NULL), then the target
01488 //               state is already stored in _target.
01489 ////////////////////////////////////////////////////////////////////
01490 void TinyGraphicsStateGuardian::
01491 set_state_and_transform(const RenderState *target,
01492                         const TransformState *transform) {
01493 #ifndef NDEBUG
01494   if (tinydisplay_cat.is_spam()) {
01495     tinydisplay_cat.spam()
01496       << "Setting GSG state to " << (void *)target << ":\n";
01497     target->write(tinydisplay_cat.spam(false), 2);
01498     transform->write(tinydisplay_cat.spam(false), 2);
01499   }
01500 #endif
01501 
01502   _state_pcollector.add_level(1);
01503   PStatTimer timer1(_draw_set_state_pcollector);
01504 
01505   if (transform != _internal_transform) {
01506     PStatTimer timer(_draw_set_state_transform_pcollector);
01507     _state_pcollector.add_level(1);
01508     _internal_transform = transform;
01509     do_issue_transform();
01510   }
01511 
01512   if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) {
01513     return;
01514   }
01515   _target_rs = target;
01516 
01517   int color_slot = ColorAttrib::get_class_slot();
01518   int color_scale_slot = ColorScaleAttrib::get_class_slot();
01519   if (_target_rs->get_attrib(color_slot) != _state_rs->get_attrib(color_slot) ||
01520       _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) ||
01521       !_state_mask.get_bit(color_slot) ||
01522       !_state_mask.get_bit(color_scale_slot)) {
01523     PStatTimer timer(_draw_set_state_color_pcollector);
01524     do_issue_color();
01525     do_issue_color_scale();
01526     _state_mask.set_bit(color_slot);
01527     _state_mask.set_bit(color_scale_slot);
01528   }
01529 
01530   int cull_face_slot = CullFaceAttrib::get_class_slot();
01531   if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) ||
01532       !_state_mask.get_bit(cull_face_slot)) {
01533     PStatTimer timer(_draw_set_state_cull_face_pcollector);
01534     do_issue_cull_face();
01535     _state_mask.set_bit(cull_face_slot);
01536   }
01537 
01538   int depth_offset_slot = DepthOffsetAttrib::get_class_slot();
01539   if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) ||
01540       !_state_mask.get_bit(depth_offset_slot)) {
01541     //PStatTimer timer(_draw_set_state_depth_offset_pcollector);
01542     do_issue_depth_offset();
01543     _state_mask.set_bit(depth_offset_slot);
01544   }
01545 
01546   int rescale_normal_slot = RescaleNormalAttrib::get_class_slot();
01547   if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) ||
01548       !_state_mask.get_bit(rescale_normal_slot)) {
01549     PStatTimer timer(_draw_set_state_rescale_normal_pcollector);
01550     do_issue_rescale_normal();
01551     _state_mask.set_bit(rescale_normal_slot);
01552   }
01553 
01554   int render_mode_slot = RenderModeAttrib::get_class_slot();
01555   if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) ||
01556       !_state_mask.get_bit(render_mode_slot)) {
01557     PStatTimer timer(_draw_set_state_render_mode_pcollector);
01558     do_issue_render_mode();
01559     _state_mask.set_bit(render_mode_slot);
01560   }
01561 
01562   int texture_slot = TextureAttrib::get_class_slot();
01563   if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
01564       !_state_mask.get_bit(texture_slot)) {
01565     PStatTimer timer(_draw_set_state_texture_pcollector);
01566     determine_target_texture();
01567     do_issue_texture();
01568     _state_mask.set_bit(texture_slot);
01569   }
01570 
01571   int material_slot = MaterialAttrib::get_class_slot();
01572   if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) ||
01573       !_state_mask.get_bit(material_slot)) {
01574     PStatTimer timer(_draw_set_state_material_pcollector);
01575     do_issue_material();
01576     _state_mask.set_bit(material_slot);
01577   }
01578 
01579   int light_slot = LightAttrib::get_class_slot();
01580   if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) ||
01581       !_state_mask.get_bit(light_slot)) {
01582     PStatTimer timer(_draw_set_state_light_pcollector);
01583     do_issue_light();
01584     _state_mask.set_bit(light_slot);
01585   }
01586 
01587   int scissor_slot = ScissorAttrib::get_class_slot();
01588   if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) ||
01589       !_state_mask.get_bit(scissor_slot)) {
01590     PStatTimer timer(_draw_set_state_scissor_pcollector);
01591     do_issue_scissor();
01592     _state_mask.set_bit(scissor_slot);
01593   }
01594 
01595   _state_rs = _target_rs;
01596 }
01597 
01598 ////////////////////////////////////////////////////////////////////
01599 //     Function: TinyGraphicsStateGuardian::prepare_texture
01600 //       Access: Public, Virtual
01601 //  Description: Creates whatever structures the GSG requires to
01602 //               represent the texture internally, and returns a
01603 //               newly-allocated TextureContext object with this data.
01604 //               It is the responsibility of the calling function to
01605 //               later call release_texture() with this same pointer
01606 //               (which will also delete the pointer).
01607 //
01608 //               This function should not be called directly to
01609 //               prepare a texture.  Instead, call Texture::prepare().
01610 ////////////////////////////////////////////////////////////////////
01611 TextureContext *TinyGraphicsStateGuardian::
01612 prepare_texture(Texture *tex, int view) {
01613   switch (tex->get_texture_type()) {
01614   case Texture::TT_1d_texture:
01615   case Texture::TT_2d_texture:
01616     // These are supported.
01617     break;
01618 
01619   default:
01620     // Anything else is not supported.
01621     tinydisplay_cat.info()
01622       << "Not loading texture " << tex->get_name() << ": "
01623       << tex->get_texture_type() << "\n";
01624     return NULL;
01625   }
01626 
01627   // Even though the texture might be compressed now, it might have an
01628   // available uncompressed version that we can load.  So don't reject
01629   // it out-of-hand just because it's compressed.
01630   /*
01631   if (tex->get_ram_image_compression() != Texture::CM_off) {
01632     tinydisplay_cat.info()
01633       << "Not loading texture " << tex->get_name() << ": "
01634       << tex->get_ram_image_compression() << "\n";
01635     return NULL;
01636   }
01637   */
01638 
01639   TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex, view);
01640 
01641   return gtc;
01642 }
01643 
01644 ////////////////////////////////////////////////////////////////////
01645 //     Function: TinyGraphicsStateGuardian::update_texture
01646 //       Access: Public, Virtual
01647 //  Description: Ensures that the current Texture data is refreshed
01648 //               onto the GSG.  This means updating the texture
01649 //               properties and/or re-uploading the texture image, if
01650 //               necessary.  This should only be called within the
01651 //               draw thread.
01652 //
01653 //               If force is true, this function will not return until
01654 //               the texture has been fully uploaded.  If force is
01655 //               false, the function may choose to upload a simple
01656 //               version of the texture instead, if the texture is not
01657 //               fully resident (and if get_incomplete_render() is
01658 //               true).
01659 ////////////////////////////////////////////////////////////////////
01660 bool TinyGraphicsStateGuardian::
01661 update_texture(TextureContext *tc, bool force) {
01662   apply_texture(tc);
01663 
01664   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
01665 
01666   GLTexture *gltex = &gtc->_gltex;
01667 
01668   if (gtc->was_image_modified() || gltex->num_levels == 0) {
01669     // If the texture image was modified, reload the texture.
01670     bool okflag = upload_texture(gtc, force);
01671     if (!okflag) {
01672       tinydisplay_cat.error()
01673         << "Could not load " << *gtc->get_texture() << "\n";
01674       return false;
01675     }
01676   }
01677   gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
01678 
01679   return true;
01680 }
01681 
01682 ////////////////////////////////////////////////////////////////////
01683 //     Function: TinyGraphicsStateGuardian::update_texture
01684 //       Access: Public
01685 //  Description: Ensures that the current Texture data is refreshed
01686 //               onto the GSG.  This means updating the texture
01687 //               properties and/or re-uploading the texture image, if
01688 //               necessary.  This should only be called within the
01689 //               draw thread.
01690 //
01691 //               If force is true, this function will not return until
01692 //               the texture has been fully uploaded.  If force is
01693 //               false, the function may choose to upload a simple
01694 //               version of the texture instead, if the texture is not
01695 //               fully resident (and if get_incomplete_render() is
01696 //               true).
01697 ////////////////////////////////////////////////////////////////////
01698 bool TinyGraphicsStateGuardian::
01699 update_texture(TextureContext *tc, bool force, int stage_index) {
01700   if (!update_texture(tc, force)) {
01701     return false;
01702   }
01703 
01704   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
01705   GLTexture *gltex = &gtc->_gltex;
01706 
01707   _c->current_textures[stage_index] = gltex;
01708 
01709   ZTextureDef *texture_def = &_c->zb->current_textures[stage_index];
01710   texture_def->levels = gltex->levels;
01711   texture_def->s_max = gltex->s_max;
01712   texture_def->t_max = gltex->t_max;
01713 
01714   const V4 &bc = gltex->border_color;
01715   int r = (int)(bc.v[0] * (ZB_POINT_RED_MAX - ZB_POINT_RED_MIN) 
01716                 + ZB_POINT_RED_MIN);
01717   int g = (int)(bc.v[1] * (ZB_POINT_GREEN_MAX - ZB_POINT_GREEN_MIN) 
01718                 + ZB_POINT_GREEN_MIN);
01719   int b = (int)(bc.v[2] * (ZB_POINT_BLUE_MAX - ZB_POINT_BLUE_MIN) 
01720                 + ZB_POINT_BLUE_MIN);
01721   int a = (int)(bc.v[3] * (ZB_POINT_ALPHA_MAX - ZB_POINT_ALPHA_MIN) 
01722                 + ZB_POINT_ALPHA_MIN);
01723   texture_def->border_color = RGBA_TO_PIXEL(r, g, b, a);
01724 
01725   return true;
01726 }
01727 
01728 ////////////////////////////////////////////////////////////////////
01729 //     Function: TinyGraphicsStateGuardian::release_texture
01730 //       Access: Public, Virtual
01731 //  Description: Frees the GL resources previously allocated for the
01732 //               texture.  This function should never be called
01733 //               directly; instead, call Texture::release() (or simply
01734 //               let the Texture destruct).
01735 ////////////////////////////////////////////////////////////////////
01736 void TinyGraphicsStateGuardian::
01737 release_texture(TextureContext *tc) {
01738   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
01739 
01740   _texturing_state = 0;  // just in case
01741 
01742   GLTexture *gltex = &gtc->_gltex;
01743   if (gltex->allocated_buffer != NULL) {
01744     nassertv(gltex->num_levels != 0);
01745     TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount);
01746     PANDA_FREE_ARRAY(gltex->allocated_buffer);
01747     gltex->allocated_buffer = NULL;
01748     gltex->total_bytecount = 0;
01749     gltex->num_levels = 0;
01750   } else {
01751     nassertv(gltex->num_levels == 0);
01752   }
01753 
01754   gtc->dequeue_lru();
01755 
01756   delete gtc;
01757 }
01758 
01759 ////////////////////////////////////////////////////////////////////
01760 //     Function: TinyGraphicsStateGuardian::do_issue_light
01761 //       Access: Protected, Virtual
01762 //  Description: 
01763 ////////////////////////////////////////////////////////////////////
01764 void TinyGraphicsStateGuardian::
01765 do_issue_light() {
01766   // Initialize the current ambient light total and newly enabled
01767   // light list
01768   LColor cur_ambient_light(0.0f, 0.0f, 0.0f, 0.0f);
01769 
01770   int num_enabled = 0;
01771   int num_on_lights = 0;
01772 
01773   const LightAttrib *target_light = DCAST(LightAttrib, _target_rs->get_attrib_def(LightAttrib::get_class_slot()));
01774   if (display_cat.is_spam()) {
01775     display_cat.spam()
01776       << "do_issue_light: " << target_light << "\n";
01777   }
01778 
01779   // First, release all of the previously-assigned lights.
01780   clear_light_state();
01781 
01782   // Now, assign new lights.
01783   if (target_light != (LightAttrib *)NULL) {
01784     CPT(LightAttrib) new_light = target_light->filter_to_max(_max_lights);
01785     if (display_cat.is_spam()) {
01786       new_light->write(display_cat.spam(false), 2);
01787     }
01788 
01789     num_on_lights = new_light->get_num_on_lights();
01790     for (int li = 0; li < num_on_lights; li++) {
01791       NodePath light = new_light->get_on_light(li);
01792       nassertv(!light.is_empty());
01793       Light *light_obj = light.node()->as_light();
01794       nassertv(light_obj != (Light *)NULL);
01795 
01796       _lighting_enabled = true;
01797       _c->lighting_enabled = true;
01798 
01799       if (light_obj->get_type() == AmbientLight::get_class_type()) {
01800         // Accumulate all of the ambient lights together into one.
01801         cur_ambient_light += light_obj->get_color();
01802 
01803       } else {
01804         // Other kinds of lights each get their own GLLight object.
01805         light_obj->bind(this, light, num_enabled);
01806         num_enabled++;
01807 
01808         // Handle the diffuse color here, since all lights have this
01809         // property.
01810         GLLight *gl_light = _c->first_light;
01811         nassertv(gl_light != NULL);
01812         const LColor &diffuse = light_obj->get_color();
01813         gl_light->diffuse.v[0] = diffuse[0];
01814         gl_light->diffuse.v[1] = diffuse[1];
01815         gl_light->diffuse.v[2] = diffuse[2];
01816         gl_light->diffuse.v[3] = diffuse[3];
01817       }
01818     }
01819   }
01820 
01821   _c->ambient_light_model.v[0] = cur_ambient_light[0];
01822   _c->ambient_light_model.v[1] = cur_ambient_light[1];
01823   _c->ambient_light_model.v[2] = cur_ambient_light[2];
01824   _c->ambient_light_model.v[3] = cur_ambient_light[3];
01825 
01826   // Changing the lighting state means we need to reapply the
01827   // transform in begin_draw_primitives().
01828   _transform_stale = true;
01829 }
01830 
01831 ////////////////////////////////////////////////////////////////////
01832 //     Function: TinyGraphicsStateGuardian::bind_light
01833 //       Access: Public, Virtual
01834 //  Description: Called the first time a particular light has been
01835 //               bound to a given id within a frame, this should set
01836 //               up the associated hardware light with the light's
01837 //               properties.
01838 ////////////////////////////////////////////////////////////////////
01839 void TinyGraphicsStateGuardian::
01840 bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
01841   pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
01842   GLLight *gl_light = &(*lookup.first).second;
01843   if (lookup.second) {
01844     // It's a brand new light.  Define it.
01845     memset(gl_light, 0, sizeof(GLLight));
01846 
01847     const LColor &specular = light_obj->get_specular_color();
01848     gl_light->specular.v[0] = specular[0];
01849     gl_light->specular.v[1] = specular[1];
01850     gl_light->specular.v[2] = specular[2];
01851     gl_light->specular.v[3] = specular[3];
01852 
01853     // Position needs to specify x, y, z, and w
01854     // w == 1 implies non-infinite position
01855     CPT(TransformState) render_transform =
01856       _cs_transform->compose(_scene_setup->get_world_transform());
01857 
01858     CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
01859     CPT(TransformState) net_transform = render_transform->compose(transform);
01860 
01861     LPoint3 pos = light_obj->get_point() * net_transform->get_mat();
01862     gl_light->position.v[0] = pos[0];
01863     gl_light->position.v[1] = pos[1];
01864     gl_light->position.v[2] = pos[2];
01865     gl_light->position.v[3] = 1.0f;
01866 
01867     // Exponent == 0 implies uniform light distribution
01868     gl_light->spot_exponent = 0.0f;
01869 
01870     // Cutoff == 180 means uniform point light source
01871     gl_light->spot_cutoff = 180.0f;
01872 
01873     const LVecBase3 &att = light_obj->get_attenuation();
01874     gl_light->attenuation[0] = att[0];
01875     gl_light->attenuation[1] = att[1];
01876     gl_light->attenuation[2] = att[2];
01877   }
01878 
01879   nassertv(gl_light->next == NULL);
01880 
01881   // Add it to the linked list of active lights.
01882   gl_light->next = _c->first_light;
01883   _c->first_light = gl_light;
01884 }
01885 
01886 ////////////////////////////////////////////////////////////////////
01887 //     Function: TinyGraphicsStateGuardian::bind_light
01888 //       Access: Public, Virtual
01889 //  Description: Called the first time a particular light has been
01890 //               bound to a given id within a frame, this should set
01891 //               up the associated hardware light with the light's
01892 //               properties.
01893 ////////////////////////////////////////////////////////////////////
01894 void TinyGraphicsStateGuardian::
01895 bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
01896   pair<Lights::iterator, bool> lookup = _dlights.insert(Lights::value_type(light, GLLight()));
01897   GLLight *gl_light = &(*lookup.first).second;
01898   if (lookup.second) {
01899     // It's a brand new light.  Define it.
01900     memset(gl_light, 0, sizeof(GLLight));
01901 
01902     const LColor &specular = light_obj->get_specular_color();
01903     gl_light->specular.v[0] = specular[0];
01904     gl_light->specular.v[1] = specular[1];
01905     gl_light->specular.v[2] = specular[2];
01906     gl_light->specular.v[3] = specular[3];
01907 
01908     // Position needs to specify x, y, z, and w
01909     // w == 0 implies light is at infinity
01910     CPT(TransformState) render_transform =
01911       _cs_transform->compose(_scene_setup->get_world_transform());
01912 
01913     CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
01914     CPT(TransformState) net_transform = render_transform->compose(transform);
01915 
01916     LVector3 dir = light_obj->get_direction() * net_transform->get_mat();
01917     dir.normalize();
01918     gl_light->position.v[0] = -dir[0];
01919     gl_light->position.v[1] = -dir[1];
01920     gl_light->position.v[2] = -dir[2];
01921     gl_light->position.v[3] = 0.0f;
01922 
01923     gl_light->norm_position.v[0] = -dir[0];
01924     gl_light->norm_position.v[1] = -dir[1];
01925     gl_light->norm_position.v[2] = -dir[2];
01926     gl_V3_Norm(&gl_light->norm_position);
01927 
01928     // Exponent == 0 implies uniform light distribution
01929     gl_light->spot_exponent = 0.0f;
01930 
01931     // Cutoff == 180 means uniform point light source
01932     gl_light->spot_cutoff = 180.0f;
01933 
01934     // Default attenuation values (only spotlight and point light can
01935     // modify these)
01936     gl_light->attenuation[0] = 1.0f;
01937     gl_light->attenuation[1] = 0.0f;
01938     gl_light->attenuation[2] = 0.0f;
01939   }
01940 
01941   nassertv(gl_light->next == NULL);
01942 
01943   // Add it to the linked list of active lights.
01944   gl_light->next = _c->first_light;
01945   _c->first_light = gl_light;
01946 }
01947 
01948 ////////////////////////////////////////////////////////////////////
01949 //     Function: TinyGraphicsStateGuardian::bind_light
01950 //       Access: Public, Virtual
01951 //  Description: Called the first time a particular light has been
01952 //               bound to a given id within a frame, this should set
01953 //               up the associated hardware light with the light's
01954 //               properties.
01955 ////////////////////////////////////////////////////////////////////
01956 void TinyGraphicsStateGuardian::
01957 bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
01958   pair<Lights::iterator, bool> lookup = _plights.insert(Lights::value_type(light, GLLight()));
01959   GLLight *gl_light = &(*lookup.first).second;
01960   if (lookup.second) {
01961     // It's a brand new light.  Define it.
01962     memset(gl_light, 0, sizeof(GLLight));
01963 
01964     const LColor &specular = light_obj->get_specular_color();
01965     gl_light->specular.v[0] = specular[0];
01966     gl_light->specular.v[1] = specular[1];
01967     gl_light->specular.v[2] = specular[2];
01968     gl_light->specular.v[3] = specular[3];
01969   
01970     Lens *lens = light_obj->get_lens();
01971     nassertv(lens != (Lens *)NULL);
01972 
01973     // Position needs to specify x, y, z, and w
01974     // w == 1 implies non-infinite position
01975     CPT(TransformState) render_transform =
01976       _cs_transform->compose(_scene_setup->get_world_transform());
01977 
01978     CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
01979     CPT(TransformState) net_transform = render_transform->compose(transform);
01980 
01981     const LMatrix4 &light_mat = net_transform->get_mat();
01982     LPoint3 pos = lens->get_nodal_point() * light_mat;
01983     LVector3 dir = lens->get_view_vector() * light_mat;
01984     dir.normalize();
01985 
01986     gl_light->position.v[0] = pos[0];
01987     gl_light->position.v[1] = pos[1];
01988     gl_light->position.v[2] = pos[2];
01989     gl_light->position.v[3] = 1.0f;
01990 
01991     gl_light->spot_direction.v[0] = dir[0];
01992     gl_light->spot_direction.v[1] = dir[1];
01993     gl_light->spot_direction.v[2] = dir[2];
01994 
01995     gl_light->norm_spot_direction.v[0] = dir[0];
01996     gl_light->norm_spot_direction.v[1] = dir[1];
01997     gl_light->norm_spot_direction.v[2] = dir[2];
01998     gl_V3_Norm(&gl_light->norm_spot_direction);
01999 
02000     gl_light->spot_exponent = light_obj->get_exponent();
02001     gl_light->spot_cutoff = lens->get_hfov() * 0.5f;
02002 
02003     const LVecBase3 &att = light_obj->get_attenuation();
02004     gl_light->attenuation[0] = att[0];
02005     gl_light->attenuation[1] = att[1];
02006     gl_light->attenuation[2] = att[2];
02007   }
02008 
02009   nassertv(gl_light->next == NULL);
02010 
02011   // Add it to the linked list of active lights.
02012   gl_light->next = _c->first_light;
02013   _c->first_light = gl_light;
02014 }
02015 
02016 ////////////////////////////////////////////////////////////////////
02017 //     Function: TinyGraphicsStateGuardian::do_issue_transform
02018 //       Access: Protected
02019 //  Description: Sends the indicated transform matrix to the graphics
02020 //               API to be applied to future vertices.
02021 //
02022 //               This transform is the internal_transform, already
02023 //               converted into the GSG's internal coordinate system.
02024 ////////////////////////////////////////////////////////////////////
02025 void TinyGraphicsStateGuardian::
02026 do_issue_transform() {
02027   _transform_state_pcollector.add_level(1);
02028   _transform_stale = true;
02029 
02030   if (_auto_rescale_normal) {
02031     do_auto_rescale_normal();
02032   }
02033 }
02034 
02035 ////////////////////////////////////////////////////////////////////
02036 //     Function: TinyGraphicsStateGuardian::do_issue_render_mode
02037 //       Access: Protected
02038 //  Description:
02039 ////////////////////////////////////////////////////////////////////
02040 void TinyGraphicsStateGuardian::
02041 do_issue_render_mode() {
02042   const RenderModeAttrib *target_render_mode = DCAST(RenderModeAttrib, _target_rs->get_attrib_def(RenderModeAttrib::get_class_slot()));
02043 
02044   _filled_flat = false;
02045 
02046   switch (target_render_mode->get_mode()) {
02047   case RenderModeAttrib::M_unchanged:
02048   case RenderModeAttrib::M_filled:
02049     _c->draw_triangle_front = gl_draw_triangle_fill;
02050     _c->draw_triangle_back = gl_draw_triangle_fill;
02051     break;
02052 
02053   case RenderModeAttrib::M_filled_flat:
02054     _c->draw_triangle_front = gl_draw_triangle_fill;
02055     _c->draw_triangle_back = gl_draw_triangle_fill;
02056     _filled_flat = true;
02057     break;
02058 
02059   case RenderModeAttrib::M_wireframe:
02060     _c->draw_triangle_front = gl_draw_triangle_line;
02061     _c->draw_triangle_back = gl_draw_triangle_line;
02062     break;
02063 
02064   case RenderModeAttrib::M_point:
02065     _c->draw_triangle_front = gl_draw_triangle_point;
02066     _c->draw_triangle_back = gl_draw_triangle_point;
02067     break;
02068 
02069   default:
02070     tinydisplay_cat.error()
02071       << "Unknown render mode " << (int)target_render_mode->get_mode() << endl;
02072   }
02073 }
02074 
02075 ////////////////////////////////////////////////////////////////////
02076 //     Function: TinyGraphicsStateGuardian::do_issue_rescale_normal
02077 //       Access: Protected
02078 //  Description:
02079 ////////////////////////////////////////////////////////////////////
02080 void TinyGraphicsStateGuardian::
02081 do_issue_rescale_normal() {
02082   const RescaleNormalAttrib *target_rescale_normal = DCAST(RescaleNormalAttrib, _target_rs->get_attrib_def(RescaleNormalAttrib::get_class_slot()));
02083   RescaleNormalAttrib::Mode mode = target_rescale_normal->get_mode();
02084 
02085   _auto_rescale_normal = false;
02086 
02087   switch (mode) {
02088   case RescaleNormalAttrib::M_none:
02089     _c->normalize_enabled = false;
02090     _c->normal_scale = 1.0f;
02091     break;
02092 
02093   case RescaleNormalAttrib::M_normalize:
02094     _c->normalize_enabled = true;
02095     _c->normal_scale = 1.0f;
02096     break;
02097 
02098   case RescaleNormalAttrib::M_rescale:
02099   case RescaleNormalAttrib::M_auto:
02100     _auto_rescale_normal = true;
02101     do_auto_rescale_normal();
02102     break;
02103 
02104   default:
02105     tinydisplay_cat.error()
02106       << "Unknown rescale_normal mode " << (int)mode << endl;
02107   }
02108 }
02109 
02110 ////////////////////////////////////////////////////////////////////
02111 //     Function: TinyGraphicsStateGuardian::do_issue_depth_offset
02112 //       Access: Protected
02113 //  Description:
02114 ////////////////////////////////////////////////////////////////////
02115 void TinyGraphicsStateGuardian::
02116 do_issue_depth_offset() {
02117   const DepthOffsetAttrib *target_depth_offset = DCAST(DepthOffsetAttrib, _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot()));
02118   int offset = target_depth_offset->get_offset();
02119   _c->zbias = offset;
02120 }
02121 
02122 ////////////////////////////////////////////////////////////////////
02123 //     Function: TinyGraphicsStateGuardian::do_issue_cull_face
02124 //       Access: Protected
02125 //  Description:
02126 ////////////////////////////////////////////////////////////////////
02127 void TinyGraphicsStateGuardian::
02128 do_issue_cull_face() {
02129   const CullFaceAttrib *target_cull_face = DCAST(CullFaceAttrib, _target_rs->get_attrib_def(CullFaceAttrib::get_class_slot()));
02130   CullFaceAttrib::Mode mode = target_cull_face->get_effective_mode();
02131 
02132   switch (mode) {
02133   case CullFaceAttrib::M_cull_none:
02134     _c->cull_face_enabled = false;
02135     break;
02136   case CullFaceAttrib::M_cull_clockwise:
02137     _c->cull_face_enabled = true;
02138     _c->cull_clockwise = true;
02139     break;
02140   case CullFaceAttrib::M_cull_counter_clockwise:
02141     _c->cull_face_enabled = true;
02142     _c->cull_clockwise = false;
02143     break;
02144   default:
02145     tinydisplay_cat.error()
02146       << "invalid cull face mode " << (int)mode << endl;
02147     break;
02148   }
02149 }
02150 
02151 ////////////////////////////////////////////////////////////////////
02152 //     Function: TinyGraphicsStateGuardian::do_issue_material
02153 //       Access: Protected
02154 //  Description:
02155 ////////////////////////////////////////////////////////////////////
02156 void TinyGraphicsStateGuardian::
02157 do_issue_material() {
02158   static Material empty;
02159 
02160   const MaterialAttrib *target_material = DCAST(MaterialAttrib, _target_rs->get_attrib_def(MaterialAttrib::get_class_slot()));
02161 
02162   const Material *material;
02163   if (target_material == (MaterialAttrib *)NULL ||
02164       target_material->is_off()) {
02165     material = &empty;
02166   } else {
02167     material = target_material->get_material();
02168   }
02169 
02170   // Apply the material parameters to the front face.
02171   setup_material(&_c->materials[0], material);
02172 
02173   if (material->get_twoside()) {
02174     // Also apply the material parameters to the back face.
02175     setup_material(&_c->materials[1], material);
02176   }
02177 
02178   _c->local_light_model = material->get_local();
02179   _c->light_model_two_side = material->get_twoside();
02180 }
02181 
02182 ////////////////////////////////////////////////////////////////////
02183 //     Function: TinyGraphicsStateGuardian::do_issue_texture
02184 //       Access: Protected
02185 //  Description:
02186 ////////////////////////////////////////////////////////////////////
02187 void TinyGraphicsStateGuardian::
02188 do_issue_texture() {
02189   _texturing_state = 0;   // untextured
02190   _c->num_textures_enabled = 0;
02191 
02192   int num_stages = _target_texture->get_num_on_ff_stages();
02193   if (num_stages == 0) {
02194     // No texturing.
02195     return;
02196   }
02197   nassertv(num_stages <= MAX_TEXTURE_STAGES);
02198 
02199   bool all_replace = true;
02200   bool all_nearest = true;
02201   bool all_mipmap_nearest = true;
02202   bool any_mipmap = false;
02203   bool needs_general = false;
02204   Texture::QualityLevel best_quality_level = Texture::QL_default;
02205 
02206   for (int si = 0; si < num_stages; ++si) {
02207     TextureStage *stage = _target_texture->get_on_ff_stage(si);
02208     Texture *texture = _target_texture->get_on_texture(stage);
02209     nassertv(texture != (Texture *)NULL);
02210     
02211     int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
02212     TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
02213     if (tc == (TextureContext *)NULL) {
02214       // Something wrong with this texture; skip it.
02215       return;
02216     }
02217     
02218     // Then, turn on the current texture mode.
02219     if (!update_texture(tc, false, si)) {
02220       return;
02221     }
02222 
02223     // M_replace means M_replace; anything else is treated the same as
02224     // M_modulate.
02225     if (stage->get_mode() != TextureStage::M_replace) {
02226       all_replace = false;
02227     }
02228 
02229     Texture::QualityLevel quality_level = _texture_quality_override;
02230     if (quality_level == Texture::QL_default) {
02231       quality_level = texture->get_quality_level();
02232     }
02233     if (quality_level == Texture::QL_default) {
02234       quality_level = texture_quality_level;
02235     }
02236 
02237     best_quality_level = max(best_quality_level, quality_level);
02238 
02239     ZTextureDef *texture_def = &_c->zb->current_textures[si];
02240     
02241     // Fill in the filter func pointers.  These may not actually get
02242     // called, if we decide below we can inline the filters.
02243     Texture::FilterType minfilter = texture->get_minfilter();
02244     Texture::FilterType magfilter = texture->get_magfilter();
02245 
02246     if (td_ignore_mipmaps && Texture::is_mipmap(minfilter)) {
02247       // Downgrade mipmaps.
02248       if (minfilter == Texture::FT_nearest_mipmap_nearest) {
02249         minfilter = Texture::FT_nearest;
02250       } else {
02251         minfilter = Texture::FT_linear;
02252       }
02253     }
02254 
02255     // Depending on this particular texture's quality level, we may
02256     // downgrade the requested filters.
02257     if (quality_level == Texture::QL_fastest) {
02258       minfilter = Texture::FT_nearest;
02259       magfilter = Texture::FT_nearest;
02260 
02261     } else if (quality_level == Texture::QL_normal) {
02262       if (Texture::is_mipmap(minfilter)) {
02263         minfilter = Texture::FT_nearest_mipmap_nearest;
02264       } else {
02265         minfilter = Texture::FT_nearest;
02266       }
02267       magfilter = Texture::FT_nearest;
02268 
02269     } else if (quality_level == Texture::QL_best) {
02270       minfilter = texture->get_effective_minfilter();
02271       magfilter = texture->get_effective_magfilter();
02272     }
02273 
02274     texture_def->tex_minfilter_func = get_tex_filter_func(minfilter);
02275     texture_def->tex_magfilter_func = get_tex_filter_func(magfilter);
02276     
02277     Texture::WrapMode wrap_u = texture->get_wrap_u();
02278     Texture::WrapMode wrap_v = texture->get_wrap_v();
02279     if (td_ignore_clamp) {
02280       wrap_u = Texture::WM_repeat;
02281       wrap_v = Texture::WM_repeat;
02282     }
02283 
02284     if (wrap_u != Texture::WM_repeat || wrap_v != Texture::WM_repeat) {
02285       // We have some nonstandard wrap mode.  This will force the use
02286       // of the general texfilter mode.
02287       needs_general = true;
02288 
02289       // We need another level of indirection to implement the
02290       // different texcoord wrap modes.  This means we will be using
02291       // the _impl function pointers, which are called by the toplevel
02292       // function.
02293 
02294       texture_def->tex_minfilter_func_impl = texture_def->tex_minfilter_func;
02295       texture_def->tex_magfilter_func_impl = texture_def->tex_magfilter_func;
02296 
02297       // Now assign the toplevel function pointer to do the
02298       // appropriate texture coordinate wrapping/clamping.
02299       texture_def->tex_minfilter_func = apply_wrap_general_minfilter;
02300       texture_def->tex_magfilter_func = apply_wrap_general_magfilter;
02301 
02302       texture_def->tex_wrap_u_func = get_tex_wrap_func(wrap_u);
02303       texture_def->tex_wrap_v_func = get_tex_wrap_func(wrap_v);
02304 
02305       // The following special cases are handled inline, rather than
02306       // relying on the above wrap function pointers.
02307       if (wrap_u && Texture::WM_border_color && wrap_v == Texture::WM_border_color) {
02308         texture_def->tex_minfilter_func = apply_wrap_border_color_minfilter;
02309         texture_def->tex_magfilter_func = apply_wrap_border_color_magfilter;
02310       } else if (wrap_u && Texture::WM_clamp && wrap_v == Texture::WM_clamp) {
02311         texture_def->tex_minfilter_func = apply_wrap_clamp_minfilter;
02312         texture_def->tex_magfilter_func = apply_wrap_clamp_magfilter;
02313       }
02314     }
02315 
02316     if (minfilter != Texture::FT_nearest || magfilter != Texture::FT_nearest) {
02317       all_nearest = false;
02318     }
02319 
02320     if (minfilter != Texture::FT_nearest_mipmap_nearest ||
02321         magfilter != Texture::FT_nearest) {
02322       all_mipmap_nearest = false;
02323     }
02324 
02325     if (Texture::is_mipmap(minfilter)) {
02326       any_mipmap = true;
02327     }
02328   }
02329 
02330   // Set a few state cache values.
02331   _c->num_textures_enabled = num_stages;
02332   _texture_replace = all_replace;
02333 
02334   _texturing_state = 2;   // perspective (perspective-correct texturing)
02335   if (num_stages >= 3) {
02336     _texturing_state = 4;  // multitex3
02337   } else if (num_stages == 2) {
02338     _texturing_state = 3;  // multitex2
02339   } else if (!td_perspective_textures) {
02340     _texturing_state = 1;    // textured (not perspective correct)
02341   }
02342 
02343   if (best_quality_level == Texture::QL_best) {
02344     // This is the most generic texture filter.  Slow, but pretty.
02345     _texfilter_state = 2;  // tgeneral
02346 
02347     if (!needs_general) {
02348       if (all_nearest) {
02349         // This case is inlined.
02350         _texfilter_state = 0;    // tnearest
02351       } else if (all_mipmap_nearest) {
02352         // So is this case.
02353         _texfilter_state = 1;  // tmipmap
02354       }
02355     }
02356 
02357   } else if (best_quality_level == Texture::QL_fastest) {
02358     // This is the cheapest texture filter.  We disallow mipmaps and
02359     // perspective correctness.
02360     _texfilter_state = 0;    // tnearest
02361     _texturing_state = 1;    // textured (not perspective correct, no multitexture)
02362   
02363   } else {
02364     // This is the default texture filter.  We use nearest sampling if
02365     // there are no mipmaps, and mipmap_nearest if there are any
02366     // mipmaps--these are the two inlined filters.
02367     _texfilter_state = 0;    // tnearest
02368     if (any_mipmap) {
02369       _texfilter_state = 1;  // tmipmap
02370     }
02371 
02372     if (needs_general) {
02373       // To support nonstandard texcoord wrapping etc, we need to
02374       // force the general texfilter mode.
02375       _texfilter_state = 2;  // tgeneral
02376     }
02377   }
02378 }
02379 
02380 ////////////////////////////////////////////////////////////////////
02381 //     Function: TinyGraphicsStateGuardian::do_issue_scissor
02382 //       Access: Protected
02383 //  Description:
02384 ////////////////////////////////////////////////////////////////////
02385 void TinyGraphicsStateGuardian::
02386 do_issue_scissor() {
02387   const ScissorAttrib *target_scissor = DCAST(ScissorAttrib, _target_rs->get_attrib_def(ScissorAttrib::get_class_slot()));
02388   const LVecBase4 &frame = target_scissor->get_frame();
02389   set_scissor(frame[0], frame[1], frame[2], frame[3]);
02390 }
02391 
02392 ////////////////////////////////////////////////////////////////////
02393 //     Function: TinyGraphicsStateGuardian::set_scissor
02394 //       Access: Private
02395 //  Description: Sets up the scissor region, as a set of coordinates
02396 //               relative to the current viewport.
02397 ////////////////////////////////////////////////////////////////////
02398 void TinyGraphicsStateGuardian::
02399 set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
02400   _c->scissor.left = left;
02401   _c->scissor.right = right;
02402   _c->scissor.bottom = bottom;
02403   _c->scissor.top = top;
02404   gl_eval_viewport(_c);
02405 
02406   PN_stdfloat xsize = right - left;
02407   PN_stdfloat ysize = top - bottom;
02408   PN_stdfloat xcenter = (left + right) - 1.0f;
02409   PN_stdfloat ycenter = (bottom + top) - 1.0f;
02410   if (xsize == 0.0f || ysize == 0.0f) {
02411     // If the scissor region is zero, nothing will be drawn anyway, so
02412     // don't worry about it.
02413     _scissor_mat = TransformState::make_identity();
02414   } else {
02415     _scissor_mat = TransformState::make_scale(LVecBase3(1.0f / xsize, 1.0f / ysize, 1.0f))->compose(TransformState::make_pos(LPoint3(-xcenter, -ycenter, 0.0f)));
02416   }
02417 }
02418 
02419 ////////////////////////////////////////////////////////////////////
02420 //     Function: TinyGraphicsStateGuardian::apply_texture
02421 //       Access: Protected
02422 //  Description: Updates the graphics state with the current
02423 //               information for this texture, and makes it the
02424 //               current texture available for rendering.
02425 ////////////////////////////////////////////////////////////////////
02426 bool TinyGraphicsStateGuardian::
02427 apply_texture(TextureContext *tc) {
02428   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
02429 
02430   gtc->set_active(true);
02431   return true;
02432 }
02433 
02434 ////////////////////////////////////////////////////////////////////
02435 //     Function: TinyGraphicsStateGuardian::upload_texture
02436 //       Access: Protected
02437 //  Description: Uploads the texture image to the graphics state.
02438 //
02439 //               The return value is true if successful, or false if
02440 //               the texture has no image.
02441 ////////////////////////////////////////////////////////////////////
02442 bool TinyGraphicsStateGuardian::
02443 upload_texture(TinyTextureContext *gtc, bool force) {
02444   Texture *tex = gtc->get_texture();
02445 
02446   if (_effective_incomplete_render && !force) {
02447     if (!tex->has_ram_image() && tex->might_have_ram_image() &&
02448         tex->has_simple_ram_image() &&
02449         !_loader.is_null()) {
02450       // If we don't have the texture data right now, go get it, but in
02451       // the meantime load a temporary simple image in its place.
02452       async_reload_texture(gtc);
02453       if (!tex->has_ram_image()) {
02454         if (gtc->was_simple_image_modified()) {
02455           return upload_simple_texture(gtc);
02456         }
02457         return true;
02458       }
02459     }
02460   }
02461 
02462   PStatTimer timer(_load_texture_pcollector);
02463   CPTA_uchar src_image = tex->get_uncompressed_ram_image();
02464   if (src_image.is_null()) {
02465     return false;
02466   }
02467 
02468   if (tinydisplay_cat.is_debug()) {
02469     tinydisplay_cat.debug()
02470       << "loading texture " << tex->get_name() << "\n";
02471   }
02472 #ifdef DO_PSTATS
02473   _data_transferred_pcollector.add_level(tex->get_ram_image_size());
02474 #endif
02475   GLTexture *gltex = &gtc->_gltex;
02476 
02477   int num_levels = 1;
02478   if (tex->uses_mipmaps()) {
02479     if (!tex->has_all_ram_mipmap_images()) {
02480       tex->generate_ram_mipmap_images();
02481     }
02482     num_levels = tex->get_num_ram_mipmap_images();
02483   }
02484 
02485   if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), num_levels)) {
02486     return false;
02487   }
02488   LColor border_color = tex->get_border_color();
02489   gltex->border_color.v[0] = border_color[0];
02490   gltex->border_color.v[1] = border_color[1];
02491   gltex->border_color.v[2] = border_color[2];
02492   gltex->border_color.v[3] = border_color[3];
02493 
02494   int bytecount = 0;
02495   int xsize = gltex->xsize;
02496   int ysize = gltex->ysize;
02497 
02498   for (int level = 0; level < gltex->num_levels; ++level) {
02499     ZTextureLevel *dest = &gltex->levels[level];
02500 
02501     switch (tex->get_format()) {
02502     case Texture::F_rgb:
02503     case Texture::F_rgb5:
02504     case Texture::F_rgb8:
02505     case Texture::F_rgb12:
02506     case Texture::F_rgb332:
02507       copy_rgb_image(dest, xsize, ysize, gtc, level);
02508       break;
02509 
02510     case Texture::F_rgba:
02511     case Texture::F_rgbm:
02512     case Texture::F_rgba4:
02513     case Texture::F_rgba5:
02514     case Texture::F_rgba8:
02515     case Texture::F_rgba12:
02516     case Texture::F_rgba16:
02517     case Texture::F_rgba32:
02518       copy_rgba_image(dest, xsize, ysize, gtc, level);
02519       break;
02520 
02521     case Texture::F_luminance:
02522       copy_lum_image(dest, xsize, ysize, gtc, level);
02523       break;
02524 
02525     case Texture::F_red:
02526       copy_one_channel_image(dest, xsize, ysize, gtc, level, 0);
02527       break;
02528 
02529     case Texture::F_green:
02530       copy_one_channel_image(dest, xsize, ysize, gtc, level, 1);
02531       break;
02532 
02533     case Texture::F_blue:
02534       copy_one_channel_image(dest, xsize, ysize, gtc, level, 2);
02535       break;
02536 
02537     case Texture::F_alpha:
02538       copy_alpha_image(dest, xsize, ysize, gtc, level);
02539       break;
02540 
02541     case Texture::F_luminance_alphamask:
02542     case Texture::F_luminance_alpha:
02543       copy_la_image(dest, xsize, ysize, gtc, level);
02544       break;
02545     }
02546 
02547     bytecount += xsize * ysize * 4;
02548     xsize = max(xsize >> 1, 1);
02549     ysize = max(ysize >> 1, 1);
02550   }
02551 
02552   gtc->update_data_size_bytes(bytecount);
02553   
02554   get_engine()->texture_uploaded(tex);
02555   gtc->mark_loaded();
02556 
02557   return true;
02558 }
02559 
02560 ////////////////////////////////////////////////////////////////////
02561 //     Function: TinyGraphicsStateGuardian::upload_simple_texture
02562 //       Access: Protected
02563 //  Description: This is used as a standin for upload_texture
02564 //               when the texture in question is unavailable (e.g. it
02565 //               hasn't yet been loaded from disk).  Until the texture
02566 //               image itself becomes available, we will render the
02567 //               texture's "simple" image--a sharply reduced version
02568 //               of the same texture.
02569 ////////////////////////////////////////////////////////////////////
02570 bool TinyGraphicsStateGuardian::
02571 upload_simple_texture(TinyTextureContext *gtc) {
02572   PStatTimer timer(_load_texture_pcollector);
02573   Texture *tex = gtc->get_texture();
02574   nassertr(tex != (Texture *)NULL, false);
02575 
02576   const unsigned char *image_ptr = tex->get_simple_ram_image();
02577   if (image_ptr == (const unsigned char *)NULL) {
02578     return false;
02579   }
02580 
02581   size_t image_size = tex->get_simple_ram_image_size();
02582   int width = tex->get_simple_x_size();
02583   int height = tex->get_simple_y_size();
02584 
02585 #ifdef DO_PSTATS
02586   _data_transferred_pcollector.add_level(image_size);
02587 #endif
02588   GLTexture *gltex = &gtc->_gltex;
02589 
02590   if (tinydisplay_cat.is_debug()) {
02591     tinydisplay_cat.debug()
02592       << "loading simple image for " << tex->get_name() << "\n";
02593   }
02594 
02595   if (!setup_gltex(gltex, width, height, 1)) {
02596     return false;
02597   }
02598   LColor border_color = tex->get_border_color();
02599   gltex->border_color.v[0] = border_color[0];
02600   gltex->border_color.v[1] = border_color[1];
02601   gltex->border_color.v[2] = border_color[2];
02602   gltex->border_color.v[3] = border_color[3];
02603 
02604   ZTextureLevel *dest = &gltex->levels[0];
02605   memcpy(dest->pixmap, image_ptr, image_size);
02606 
02607   gtc->mark_simple_loaded();
02608 
02609   return true;
02610 }
02611 
02612 ////////////////////////////////////////////////////////////////////
02613 //     Function: TinyGraphicsStateGuardian::setup_gltex
02614 //       Access: Private
02615 //  Description: Sets the GLTexture size, bits, and masks
02616 //               appropriately, and allocates space for a pixmap.
02617 //               Does not fill the pixmap contents.  Returns true if
02618 //               the texture is a valid size, false otherwise.
02619 ////////////////////////////////////////////////////////////////////
02620 bool TinyGraphicsStateGuardian::
02621 setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
02622   int s_bits = get_tex_shift(x_size);
02623   int t_bits = get_tex_shift(y_size);
02624   
02625   if (s_bits < 0 || t_bits < 0) {
02626     return false;
02627   }
02628 
02629   num_levels = min(num_levels, MAX_MIPMAP_LEVELS);
02630 
02631   gltex->xsize = x_size;
02632   gltex->ysize = y_size;
02633 
02634   gltex->s_max = 1 << (s_bits + ZB_POINT_ST_FRAC_BITS);
02635   gltex->t_max = 1 << (t_bits + ZB_POINT_ST_FRAC_BITS);
02636 
02637   gltex->num_levels = num_levels;
02638   
02639   // We allocate one big buffer, large enough to include all the
02640   // mipmap levels, and index into that buffer for each level.  This
02641   // cuts down on the number of individual alloc calls we have to make
02642   // for each texture.
02643   int total_bytecount = 0;
02644 
02645   // Count up the total bytes required for all mipmap levels.
02646   {
02647     int x = x_size;
02648     int y = y_size;
02649     for (int level = 0; level < num_levels; ++level) {
02650       int bytecount = x * y * 4;
02651       total_bytecount += bytecount;
02652       x = max((x >> 1), 1);
02653       y = max((y >> 1), 1);
02654     }
02655   }
02656 
02657   if (gltex->total_bytecount != total_bytecount) {
02658     if (gltex->allocated_buffer != NULL) {
02659       PANDA_FREE_ARRAY(gltex->allocated_buffer);
02660       TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount);
02661     }
02662     gltex->allocated_buffer = PANDA_MALLOC_ARRAY(total_bytecount);
02663     gltex->total_bytecount = total_bytecount;
02664     TinyTextureContext::get_class_type().inc_memory_usage(TypeHandle::MC_array, total_bytecount);
02665   }
02666 
02667   char *next_buffer = (char *)gltex->allocated_buffer;
02668   char *end_of_buffer = next_buffer + total_bytecount;
02669 
02670   int level = 0;
02671   ZTextureLevel *dest = NULL;
02672   while (level < num_levels) {
02673     dest = &gltex->levels[level];
02674     int bytecount = x_size * y_size * 4;
02675     dest->pixmap = (PIXEL *)next_buffer;
02676     next_buffer += bytecount;
02677     nassertr(next_buffer <= end_of_buffer, false);
02678 
02679     dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
02680     dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
02681     dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level);
02682     dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level);
02683     
02684     x_size = max((x_size >> 1), 1);
02685     y_size = max((y_size >> 1), 1);
02686     s_bits = max(s_bits - 1, 0);
02687     t_bits = max(t_bits - 1, 0);
02688 
02689     ++level;
02690   }
02691 
02692   // Fill out the remaining mipmap arrays with copies of the last
02693   // level, so we don't have to be concerned with running off the end
02694   // of this array while scanning out triangles.
02695   while (level < MAX_MIPMAP_LEVELS) {
02696     gltex->levels[level] = *dest;
02697     ++level;
02698   }
02699 
02700   return true;
02701 }
02702 
02703 ////////////////////////////////////////////////////////////////////
02704 //     Function: TinyGraphicsStateGuardian::get_tex_shift
02705 //       Access: Private
02706 //  Description: Calculates the bit shift count, such that (1 << shift)
02707 //               == size.  Returns -1 if the size is not a power of 2
02708 //               or is larger than our largest allowable size.
02709 ////////////////////////////////////////////////////////////////////
02710 int TinyGraphicsStateGuardian::
02711 get_tex_shift(int orig_size) {
02712   if ((orig_size & (orig_size - 1)) != 0) {
02713     // Not a power of 2.
02714     return -1;
02715   }
02716   if (orig_size > _max_texture_dimension) {
02717     return -1;
02718   }
02719 
02720   return count_bits_in_word((unsigned int)orig_size - 1);
02721 }
02722 
02723 ////////////////////////////////////////////////////////////////////
02724 //     Function: TinyGraphicsStateGuardian::copy_lum_image
02725 //       Access: Private, Static
02726 //  Description: Copies and scales the one-channel luminance image
02727 //               from the texture into the indicated ZTexture pixmap.
02728 ////////////////////////////////////////////////////////////////////
02729 void TinyGraphicsStateGuardian::
02730 copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
02731   Texture *tex = gtc->get_texture();
02732   nassertv(tex->get_num_components() == 1);
02733   nassertv(tex->get_expected_mipmap_x_size(level) == xsize &&
02734            tex->get_expected_mipmap_y_size(level) == ysize);
02735 
02736   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02737   nassertv(!src_image.is_null());
02738   const unsigned char *src = src_image.p();
02739   size_t view_size = tex->get_ram_mipmap_view_size(level);
02740   src += view_size * gtc->get_view();
02741 
02742   // Component width, and offset to the high-order byte.
02743   int cw = tex->get_component_width();
02744 #ifdef WORDS_BIGENDIAN
02745   // Big-endian: the high-order byte is always first.
02746   static const int co = 0;
02747 #else
02748   // Little-endian: the high-order byte is last.
02749   int co = cw - 1;
02750 #endif
02751 
02752   unsigned int *dpix = (unsigned int *)dest->pixmap;
02753   nassertv(dpix != NULL);
02754   const unsigned char *spix = src;
02755   int pixel_count = xsize * ysize;
02756   while (pixel_count-- > 0) {
02757     *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], 0xff);
02758     ++dpix;
02759     spix += cw;
02760   }
02761 }
02762 
02763 ////////////////////////////////////////////////////////////////////
02764 //     Function: TinyGraphicsStateGuardian::copy_alpha_image
02765 //       Access: Private, Static
02766 //  Description: Copies and scales the one-channel alpha image
02767 //               from the texture into the indicated ZTexture pixmap.
02768 ////////////////////////////////////////////////////////////////////
02769 void TinyGraphicsStateGuardian::
02770 copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
02771   Texture *tex = gtc->get_texture();
02772   nassertv(tex->get_num_components() == 1);
02773 
02774   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02775   nassertv(!src_image.is_null());
02776   const unsigned char *src = src_image.p();
02777   size_t view_size = tex->get_ram_mipmap_view_size(level);
02778   src += view_size * gtc->get_view();
02779 
02780   // Component width, and offset to the high-order byte.
02781   int cw = tex->get_component_width();
02782 #ifdef WORDS_BIGENDIAN
02783   // Big-endian: the high-order byte is always first.
02784   static const int co = 0;
02785 #else
02786   // Little-endian: the high-order byte is last.
02787   int co = cw - 1;
02788 #endif
02789 
02790   unsigned int *dpix = (unsigned int *)dest->pixmap;
02791   nassertv(dpix != NULL);
02792   const unsigned char *spix = src;
02793   int pixel_count = xsize * ysize;
02794   while (pixel_count-- > 0) {
02795     *dpix = RGBA8_TO_PIXEL(0xff, 0xff, 0xff, spix[co]);
02796     ++dpix;
02797     spix += cw;
02798   }
02799 }
02800 
02801 ////////////////////////////////////////////////////////////////////
02802 //     Function: TinyGraphicsStateGuardian::copy_one_channel_image
02803 //       Access: Private, Static
02804 //  Description: Copies and scales the one-channel image (with a
02805 //               single channel, e.g. red, green, or blue) from
02806 //               the texture into the indicated ZTexture pixmap.
02807 ////////////////////////////////////////////////////////////////////
02808 void TinyGraphicsStateGuardian::
02809 copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel) {
02810   Texture *tex = gtc->get_texture();
02811   nassertv(tex->get_num_components() == 1);
02812 
02813   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02814   nassertv(!src_image.is_null());
02815   const unsigned char *src = src_image.p();
02816   size_t view_size = tex->get_ram_mipmap_view_size(level);
02817   src += view_size * gtc->get_view();
02818 
02819   // Component width, and offset to the high-order byte.
02820   int cw = tex->get_component_width();
02821 #ifdef WORDS_BIGENDIAN
02822   // Big-endian: the high-order byte is always first.
02823   static const int co = 0;
02824 #else
02825   // Little-endian: the high-order byte is last.
02826   int co = cw - 1;
02827 #endif
02828 
02829   unsigned int *dpix = (unsigned int *)dest->pixmap;
02830   nassertv(dpix != NULL);
02831   const unsigned char *spix = src;
02832   int pixel_count = xsize * ysize;
02833 
02834   switch (channel) {
02835   case 0:
02836     while (pixel_count-- > 0) {
02837       *dpix = RGBA8_TO_PIXEL(spix[co], 0, 0, 0xff);
02838       ++dpix;
02839       spix += cw;
02840     }
02841     break;
02842 
02843   case 1:
02844     while (pixel_count-- > 0) {
02845       *dpix = RGBA8_TO_PIXEL(0, spix[co], 0, 0xff);
02846       ++dpix;
02847       spix += cw;
02848     }
02849     break;
02850 
02851   case 2:
02852     while (pixel_count-- > 0) {
02853       *dpix = RGBA8_TO_PIXEL(0, 0, spix[co], 0xff);
02854       ++dpix;
02855       spix += cw;
02856     }
02857     break;
02858 
02859   case 3:
02860     while (pixel_count-- > 0) {
02861       *dpix = RGBA8_TO_PIXEL(0, 0, 0, spix[co]);
02862       ++dpix;
02863       spix += cw;
02864     }
02865     break;
02866   }
02867 }
02868 
02869 ////////////////////////////////////////////////////////////////////
02870 //     Function: TinyGraphicsStateGuardian::copy_la_image
02871 //       Access: Private, Static
02872 //  Description: Copies and scales the two-channel luminance-alpha
02873 //               image from the texture into the indicated ZTexture
02874 //               pixmap.
02875 ////////////////////////////////////////////////////////////////////
02876 void TinyGraphicsStateGuardian::
02877 copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
02878   Texture *tex = gtc->get_texture();
02879   nassertv(tex->get_num_components() == 2);
02880 
02881   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02882   nassertv(!src_image.is_null());
02883   const unsigned char *src = src_image.p();
02884   size_t view_size = tex->get_ram_mipmap_view_size(level);
02885   src += view_size * gtc->get_view();
02886 
02887   // Component width, and offset to the high-order byte.
02888   int cw = tex->get_component_width();
02889 #ifdef WORDS_BIGENDIAN
02890   // Big-endian: the high-order byte is always first.
02891   static const int co = 0;
02892 #else
02893   // Little-endian: the high-order byte is last.
02894   int co = cw - 1;
02895 #endif
02896 
02897   unsigned int *dpix = (unsigned int *)dest->pixmap;
02898   nassertv(dpix != NULL);
02899   const unsigned char *spix = src;
02900   int pixel_count = xsize * ysize;
02901   int inc = 2 * cw;
02902   while (pixel_count-- > 0) {
02903     *dpix = RGBA8_TO_PIXEL(spix[co], spix[co], spix[co], spix[cw + co]);
02904     ++dpix;
02905     spix += inc;
02906   }
02907 }
02908 
02909 ////////////////////////////////////////////////////////////////////
02910 //     Function: TinyGraphicsStateGuardian::copy_rgb_image
02911 //       Access: Private, Static
02912 //  Description: Copies and scales the three-channel RGB image from
02913 //               the texture into the indicated ZTexture pixmap.
02914 ////////////////////////////////////////////////////////////////////
02915 void TinyGraphicsStateGuardian::
02916 copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
02917   Texture *tex = gtc->get_texture();
02918   nassertv(tex->get_num_components() == 3);
02919 
02920   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02921   nassertv(!src_image.is_null());
02922   const unsigned char *src = src_image.p();
02923   size_t view_size = tex->get_ram_mipmap_view_size(level);
02924   src += view_size * gtc->get_view();
02925 
02926   // Component width, and offset to the high-order byte.
02927   int cw = tex->get_component_width();
02928 #ifdef WORDS_BIGENDIAN
02929   // Big-endian: the high-order byte is always first.
02930   static const int co = 0;
02931 #else
02932   // Little-endian: the high-order byte is last.
02933   int co = cw - 1;
02934 #endif
02935 
02936   unsigned int *dpix = (unsigned int *)dest->pixmap;
02937   nassertv(dpix != NULL);
02938   const unsigned char *spix = src;
02939   int pixel_count = xsize * ysize;
02940   int inc = 3 * cw;
02941   while (pixel_count-- > 0) {
02942     *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], 0xff);
02943     ++dpix;
02944     spix += inc;
02945   }
02946 }
02947 
02948 ////////////////////////////////////////////////////////////////////
02949 //     Function: TinyGraphicsStateGuardian::copy_rgba_image
02950 //       Access: Private, Static
02951 //  Description: Copies and scales the four-channel RGBA image from
02952 //               the texture into the indicated ZTexture pixmap.
02953 ////////////////////////////////////////////////////////////////////
02954 void TinyGraphicsStateGuardian::
02955 copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
02956   Texture *tex = gtc->get_texture();
02957   nassertv(tex->get_num_components() == 4);
02958 
02959   CPTA_uchar src_image = tex->get_ram_mipmap_image(level);
02960   nassertv(!src_image.is_null());
02961   const unsigned char *src = src_image.p();
02962   size_t view_size = tex->get_ram_mipmap_view_size(level);
02963   src += view_size * gtc->get_view();
02964 
02965   // Component width, and offset to the high-order byte.
02966   int cw = tex->get_component_width();
02967 #ifdef WORDS_BIGENDIAN
02968   // Big-endian: the high-order byte is always first.
02969   static const int co = 0;
02970 #else
02971   // Little-endian: the high-order byte is last.
02972   int co = cw - 1;
02973 #endif
02974 
02975   unsigned int *dpix = (unsigned int *)dest->pixmap;
02976   nassertv(dpix != NULL);
02977   const unsigned char *spix = src;
02978   int pixel_count = xsize * ysize;
02979   int inc = 4 * cw;
02980   while (pixel_count-- > 0) {
02981     *dpix = RGBA8_TO_PIXEL(spix[cw + cw + co], spix[cw + co], spix[co], spix[cw + cw + cw + co]);
02982     ++dpix;
02983     spix += inc;
02984   }
02985 }
02986 
02987 ////////////////////////////////////////////////////////////////////
02988 //     Function: TinyGraphicsStateGuardian::setup_material
02989 //       Access: Private
02990 //  Description: Applies the desired parametesr to the indicated
02991 //               GLMaterial object.
02992 ////////////////////////////////////////////////////////////////////
02993 void TinyGraphicsStateGuardian::
02994 setup_material(GLMaterial *gl_material, const Material *material) {
02995   const LColor &specular = material->get_specular();
02996   gl_material->specular.v[0] = specular[0];
02997   gl_material->specular.v[1] = specular[1];
02998   gl_material->specular.v[2] = specular[2];
02999   gl_material->specular.v[3] = specular[3];
03000 
03001   const LColor &emission = material->get_emission();
03002   gl_material->emission.v[0] = emission[0];
03003   gl_material->emission.v[1] = emission[1];
03004   gl_material->emission.v[2] = emission[2];
03005   gl_material->emission.v[3] = emission[3];
03006 
03007   gl_material->shininess = material->get_shininess();
03008   gl_material->shininess_i = (int)((material->get_shininess() / 128.0f) * SPECULAR_BUFFER_RESOLUTION);
03009 
03010   _color_material_flags = CMF_ambient | CMF_diffuse;
03011 
03012   if (material->has_ambient()) {
03013     const LColor &ambient = material->get_ambient();
03014     gl_material->ambient.v[0] = ambient[0];
03015     gl_material->ambient.v[1] = ambient[1];
03016     gl_material->ambient.v[2] = ambient[2];
03017     gl_material->ambient.v[3] = ambient[3];
03018 
03019     _color_material_flags &= ~CMF_ambient;
03020   }
03021 
03022   if (material->has_diffuse()) {
03023     const LColor &diffuse = material->get_diffuse();
03024     gl_material->diffuse.v[0] = diffuse[0];
03025     gl_material->diffuse.v[1] = diffuse[1];
03026     gl_material->diffuse.v[2] = diffuse[2];
03027     gl_material->diffuse.v[3] = diffuse[3];
03028 
03029     _color_material_flags &= ~CMF_diffuse;
03030   }
03031 }
03032 
03033 ////////////////////////////////////////////////////////////////////
03034 //     Function: TinyGraphicsStateGuardian::do_auto_rescale_normal
03035 //       Access: Protected
03036 //  Description: Sets the state to either rescale or normalize the
03037 //               normals according to the current transform.
03038 ////////////////////////////////////////////////////////////////////
03039 void TinyGraphicsStateGuardian::
03040 do_auto_rescale_normal() {
03041   if (_internal_transform->has_uniform_scale()) {
03042     // There's a uniform scale; rescale the normals uniformly.
03043     _c->normalize_enabled = false;
03044     _c->normal_scale = _internal_transform->get_uniform_scale();
03045 
03046   } else {
03047     // If there's a non-uniform scale, normalize everything.
03048     _c->normalize_enabled = true;
03049     _c->normal_scale = 1.0f;
03050   }
03051 }
03052 
03053 ////////////////////////////////////////////////////////////////////
03054 //     Function: TinyGraphicsStateGuardian::load_matrix
03055 //       Access: Private, Static
03056 //  Description: Copies the Panda matrix stored in the indicated
03057 //               TransformState object into the indicated TinyGL
03058 //               matrix.
03059 ////////////////////////////////////////////////////////////////////
03060 void TinyGraphicsStateGuardian::
03061 load_matrix(M4 *matrix, const TransformState *transform) {
03062   const LMatrix4 &pm = transform->get_mat();
03063   for (int i = 0; i < 4; ++i) {
03064     matrix->m[0][i] = pm.get_cell(i, 0);
03065     matrix->m[1][i] = pm.get_cell(i, 1);
03066     matrix->m[2][i] = pm.get_cell(i, 2);
03067     matrix->m[3][i] = pm.get_cell(i, 3);
03068   }
03069 }
03070 
03071 ////////////////////////////////////////////////////////////////////
03072 //     Function: TinyGraphicsStateGuardian::get_color_blend_op
03073 //       Access: Private, Static
03074 //  Description: Returns the integer element of store_pixel_funcs (as
03075 //               defined by store_pixel.py) that corresponds to the
03076 //               indicated ColorBlendAttrib operand code.
03077 ////////////////////////////////////////////////////////////////////
03078 int TinyGraphicsStateGuardian::
03079 get_color_blend_op(ColorBlendAttrib::Operand operand) {
03080   switch (operand) {
03081   case ColorBlendAttrib::O_zero:
03082     return 0;
03083   case ColorBlendAttrib::O_one:
03084     return 1;
03085   case ColorBlendAttrib::O_incoming_color:
03086     return 2;
03087   case ColorBlendAttrib::O_one_minus_incoming_color:
03088     return 3;
03089   case ColorBlendAttrib::O_fbuffer_color:
03090     return 4;
03091   case ColorBlendAttrib::O_one_minus_fbuffer_color:
03092     return 5;
03093   case ColorBlendAttrib::O_incoming_alpha:
03094     return 6;
03095   case ColorBlendAttrib::O_one_minus_incoming_alpha:
03096     return 7;
03097   case ColorBlendAttrib::O_fbuffer_alpha:
03098     return 8;
03099   case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
03100     return 9;
03101   case ColorBlendAttrib::O_constant_color:
03102     return 10;
03103   case ColorBlendAttrib::O_one_minus_constant_color:
03104     return 11;
03105   case ColorBlendAttrib::O_constant_alpha:
03106     return 12;
03107   case ColorBlendAttrib::O_one_minus_constant_alpha:
03108     return 13;
03109 
03110   case ColorBlendAttrib::O_incoming_color_saturate:
03111     return 1;
03112 
03113   case ColorBlendAttrib::O_color_scale:
03114     return 10;
03115   case ColorBlendAttrib::O_one_minus_color_scale:
03116     return 11;
03117   case ColorBlendAttrib::O_alpha_scale:
03118     return 12;
03119   case ColorBlendAttrib::O_one_minus_alpha_scale:
03120     return 13;
03121   }
03122   return 0;
03123 }
03124  
03125 ////////////////////////////////////////////////////////////////////
03126 //     Function: TinyGraphicsStateGuardian::get_tex_filter_func
03127 //       Access: Private, Static
03128 //  Description: Returns the pointer to the appropriate filter
03129 //               function according to the texture's filter type.
03130 ////////////////////////////////////////////////////////////////////
03131 ZB_lookupTextureFunc TinyGraphicsStateGuardian::
03132 get_tex_filter_func(Texture::FilterType filter) {
03133   switch (filter) {
03134   case Texture::FT_nearest:
03135     return &lookup_texture_nearest;
03136 
03137   case Texture::FT_linear:
03138     return &lookup_texture_bilinear;
03139 
03140   case Texture::FT_nearest_mipmap_nearest:
03141     return &lookup_texture_mipmap_nearest;
03142 
03143   case Texture::FT_nearest_mipmap_linear:
03144     return &lookup_texture_mipmap_linear;
03145       
03146   case Texture::FT_linear_mipmap_nearest:
03147     return &lookup_texture_mipmap_bilinear;
03148 
03149   case Texture::FT_linear_mipmap_linear:
03150     return &lookup_texture_mipmap_trilinear;
03151 
03152   default:
03153     return &lookup_texture_nearest;
03154   }
03155 }
03156  
03157 ////////////////////////////////////////////////////////////////////
03158 //     Function: TinyGraphicsStateGuardian::get_tex_wrap_func
03159 //       Access: Private, Static
03160 //  Description: Returns the pointer to the appropriate wrap
03161 //               function according to the texture's wrap mode.
03162 ////////////////////////////////////////////////////////////////////
03163 ZB_texWrapFunc TinyGraphicsStateGuardian::
03164 get_tex_wrap_func(Texture::WrapMode wrap_mode) {
03165   switch (wrap_mode) {
03166   case Texture::WM_clamp:
03167   case Texture::WM_border_color:  // border_color is handled later.
03168     return &texcoord_clamp;
03169 
03170   case Texture::WM_repeat:
03171   case Texture::WM_invalid:
03172     return &texcoord_repeat;
03173 
03174   case Texture::WM_mirror:
03175     return &texcoord_mirror;
03176 
03177   case Texture::WM_mirror_once:
03178     return &texcoord_mirror_once;
03179   }
03180 
03181   return &texcoord_repeat;
03182 }
03183 
03184 ////////////////////////////////////////////////////////////////////
03185 //     Function: TinyGraphicsStateGuardian::texgen_null
03186 //       Access: Private, Static
03187 //  Description: Generates invalid texture coordinates.  Used when
03188 //               texture coordinate params are invalid or unsupported.
03189 ////////////////////////////////////////////////////////////////////
03190 void TinyGraphicsStateGuardian::
03191 texgen_null(V2 &result, TinyGraphicsStateGuardian::TexCoordData &) {
03192   result.v[0] = 0.0;
03193   result.v[1] = 0.0;
03194 }
03195 
03196 ////////////////////////////////////////////////////////////////////
03197 //     Function: TinyGraphicsStateGuardian::texgen_simple
03198 //       Access: Private, Static
03199 //  Description: Extracts a simple 2-d texture coordinate pair from
03200 //               the vertex data, without applying any texture matrix.
03201 ////////////////////////////////////////////////////////////////////
03202 void TinyGraphicsStateGuardian::
03203 texgen_simple(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
03204   // No need to transform, so just extract as two-component.
03205   const LVecBase2 &d = tcdata._r1.get_data2();
03206   result.v[0] = d[0];
03207   result.v[1] = d[1];
03208 }
03209 
03210 ////////////////////////////////////////////////////////////////////
03211 //     Function: TinyGraphicsStateGuardian::texgen_simple
03212 //       Access: Private, Static
03213 //  Description: Extracts a simple 2-d texture coordinate pair from
03214 //               the vertex data, and then applies a texture matrix.
03215 ////////////////////////////////////////////////////////////////////
03216 void TinyGraphicsStateGuardian::
03217 texgen_texmat(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
03218   // Transform texcoords as a four-component vector for most generality.
03219   LVecBase4 d = tcdata._r1.get_data4() * tcdata._mat;
03220   result.v[0] = d[0] / d[3];
03221   result.v[1] = d[1] / d[3];
03222 }
03223 
03224 ////////////////////////////////////////////////////////////////////
03225 //     Function: TinyGraphicsStateGuardian::texgen_sphere_map
03226 //       Access: Private, Static
03227 //  Description: Computes appropriate sphere map texture coordinates
03228 //               based on the eye normal coordinates.
03229 ////////////////////////////////////////////////////////////////////
03230 void TinyGraphicsStateGuardian::
03231 texgen_sphere_map(V2 &result, TinyGraphicsStateGuardian::TexCoordData &tcdata) {
03232   // Get the normal and point in eye coordinates.
03233   LVector3 n = tcdata._mat.xform_vec(tcdata._r1.get_data3());
03234   LVector3 u = tcdata._mat.xform_point(tcdata._r2.get_data3());
03235 
03236   // Normalize the vectors.
03237   n.normalize();
03238   u.normalize();
03239 
03240   // Compute the reflection vector.
03241   LVector3 r = u - n * dot(n, u) * 2.0f;
03242   
03243   // compute the denominator, m.
03244   PN_stdfloat m = 2.0f * csqrt(r[0] * r[0] + r[1] * r[1] + (r[2] + 1.0f) * (r[2] + 1.0f));
03245 
03246   // Now we can compute the s and t coordinates.
03247   result.v[0] = r[0] / m + 0.5f;
03248   result.v[1] = r[1] / m + 0.5f;
03249 
03250   /*
03251   cerr << "n = " << n << " u = " << u << "\n"
03252        << "  r = " << r << "\n"
03253        << "  m = " << m << ", result = " << result.v[0] << " " << result.v[1]
03254        << "\n";
03255   */
03256 }
 All Classes Functions Variables Enumerations