Panda3D

geomTransformer.cxx

00001 // Filename: geomTransformer.cxx
00002 // Created by:  drose (14Mar02)
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 "geomTransformer.h"
00016 #include "sceneGraphReducer.h"
00017 #include "geomNode.h"
00018 #include "geom.h"
00019 #include "geomVertexRewriter.h"
00020 #include "renderState.h"
00021 #include "transformTable.h"
00022 #include "transformBlendTable.h"
00023 #include "sliderTable.h"
00024 #include "pStatCollector.h"
00025 #include "pStatTimer.h"
00026 #include "vector_int.h"
00027 #include "userVertexTransform.h"
00028 #include "geomMunger.h"
00029 #include "texture.h"
00030 #include "texturePeeker.h"
00031 #include "config_pgraph.h"
00032 
00033 PStatCollector GeomTransformer::_apply_vertex_collector("*:Flatten:apply:vertex");
00034 PStatCollector GeomTransformer::_apply_texcoord_collector("*:Flatten:apply:texcoord");
00035 PStatCollector GeomTransformer::_apply_set_color_collector("*:Flatten:apply:set color");
00036 PStatCollector GeomTransformer::_apply_scale_color_collector("*:Flatten:apply:scale color");
00037 PStatCollector GeomTransformer::_apply_texture_color_collector("*:Flatten:apply:texture color");
00038 PStatCollector GeomTransformer::_apply_set_format_collector("*:Flatten:apply:set format");
00039 
00040 TypeHandle GeomTransformer::NewCollectedData::_type_handle;
00041 
00042 ////////////////////////////////////////////////////////////////////
00043 //     Function: GeomTransformer::Constructor
00044 //       Access: Public
00045 //  Description:
00046 ////////////////////////////////////////////////////////////////////
00047 GeomTransformer::
00048 GeomTransformer() :
00049   // The default value here comes from the Config file.
00050   _max_collect_vertices(max_collect_vertices)
00051 {
00052 }
00053 
00054 ////////////////////////////////////////////////////////////////////
00055 //     Function: GeomTransformer::Copy Constructor
00056 //       Access: Public
00057 //  Description:
00058 ////////////////////////////////////////////////////////////////////
00059 GeomTransformer::
00060 GeomTransformer(const GeomTransformer &copy) :
00061   _max_collect_vertices(copy._max_collect_vertices)
00062 {
00063 }
00064 
00065 ////////////////////////////////////////////////////////////////////
00066 //     Function: GeomTransformer::Destructor
00067 //       Access: Public
00068 //  Description:
00069 ////////////////////////////////////////////////////////////////////
00070 GeomTransformer::
00071 ~GeomTransformer() {
00072   finish_collect(false);
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: GeomTransformer::register_vertices
00077 //       Access: Public
00078 //  Description: Records the association of the Geom with its
00079 //               GeomVertexData, for the purpose of later removing
00080 //               unused vertices.
00081 ////////////////////////////////////////////////////////////////////
00082 void GeomTransformer::
00083 register_vertices(Geom *geom, bool might_have_unused) {
00084   VertexDataAssoc &assoc = _vdata_assoc[geom->get_vertex_data()];
00085   assoc._geoms.push_back(geom);
00086   if (might_have_unused) {
00087     assoc._might_have_unused = true;
00088   }
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: GeomTransformer::register_vertices
00093 //       Access: Public
00094 //  Description: Records the association of the Geom with its
00095 //               GeomVertexData, for the purpose of later removing
00096 //               unused vertices.
00097 ////////////////////////////////////////////////////////////////////
00098 void GeomTransformer::
00099 register_vertices(GeomNode *node, bool might_have_unused) {
00100   Thread *current_thread = Thread::get_current_thread();
00101   OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
00102     GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
00103     GeomNode::GeomList::iterator gi;
00104     PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00105     for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00106       GeomNode::GeomEntry &entry = (*gi);
00107       PT(Geom) geom = entry._geom.get_write_pointer();
00108       register_vertices(geom, might_have_unused);
00109     }
00110   }
00111   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
00112 }
00113 
00114 ////////////////////////////////////////////////////////////////////
00115 //     Function: GeomTransformer::transform_vertices
00116 //       Access: Public
00117 //  Description: Transforms the vertices and the normals in the
00118 //               indicated Geom by the indicated matrix.  Returns true
00119 //               if the Geom was changed, false otherwise.
00120 ////////////////////////////////////////////////////////////////////
00121 bool GeomTransformer::
00122 transform_vertices(Geom *geom, const LMatrix4f &mat) {
00123   PStatTimer timer(_apply_vertex_collector);
00124 
00125   nassertr(geom != (Geom *)NULL, false);
00126   SourceVertices sv;
00127   sv._mat = mat;
00128   sv._vertex_data = geom->get_vertex_data();
00129   
00130   NewVertexData &new_data = _vertices[sv];
00131   if (new_data._vdata.is_null()) {
00132     // We have not yet converted these vertices.  Do so now.
00133     PT(GeomVertexData) new_vdata = new GeomVertexData(*sv._vertex_data);
00134     CPT(GeomVertexFormat) format = new_vdata->get_format();
00135     
00136     int ci;
00137     for (ci = 0; ci < format->get_num_points(); ci++) {
00138       GeomVertexRewriter data(new_vdata, format->get_point(ci));
00139       
00140       while (!data.is_at_end()) {
00141         const LPoint3f &point = data.get_data3f();
00142         data.set_data3f(point * mat);
00143       }
00144     }
00145     for (ci = 0; ci < format->get_num_vectors(); ci++) {
00146       GeomVertexRewriter data(new_vdata, format->get_vector(ci));
00147       
00148       while (!data.is_at_end()) {
00149         const LVector3f &vector = data.get_data3f();
00150         data.set_data3f(normalize(vector * mat));
00151       }
00152     }
00153     new_data._vdata = new_vdata;
00154   }
00155   
00156   geom->set_vertex_data(new_data._vdata);
00157   if (sv._vertex_data->get_ref_count() > 1) {
00158     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00159     _vdata_assoc[sv._vertex_data]._might_have_unused = true;
00160   }
00161 
00162   return true;
00163 }
00164 
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: GeomTransformer::transform_vertices
00168 //       Access: Public
00169 //  Description: Transforms the vertices and the normals in all of the
00170 //               Geoms within the indicated GeomNode by the indicated
00171 //               matrix.  Does not destructively change Geoms;
00172 //               instead, a copy will be made of each Geom to be
00173 //               changed, in case multiple GeomNodes reference the
00174 //               same Geom. Returns true if the GeomNode was changed,
00175 //               false otherwise.
00176 ////////////////////////////////////////////////////////////////////
00177 bool GeomTransformer::
00178 transform_vertices(GeomNode *node, const LMatrix4f &mat) {
00179   bool any_changed = false;
00180 
00181   Thread *current_thread = Thread::get_current_thread();
00182   OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
00183     GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
00184     GeomNode::GeomList::iterator gi;
00185     PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00186     for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00187       GeomNode::GeomEntry &entry = (*gi);
00188       PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00189       if (transform_vertices(new_geom, mat)) {
00190         entry._geom = new_geom;
00191         any_changed = true;
00192       }
00193     }
00194   }
00195   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
00196 
00197   if (any_changed) {
00198     node->mark_internal_bounds_stale();
00199   }
00200 
00201   return any_changed;
00202 }
00203 
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: GeomTransformer::transform_texcoords
00207 //       Access: Public
00208 //  Description: Transforms the texture coordinates in the indicated
00209 //               Geom by the indicated matrix.  Returns true if the
00210 //               Geom was changed, false otherwise.
00211 ////////////////////////////////////////////////////////////////////
00212 bool GeomTransformer::
00213 transform_texcoords(Geom *geom, const InternalName *from_name, 
00214                     InternalName *to_name, const LMatrix4f &mat) {
00215   PStatTimer timer(_apply_texcoord_collector);
00216 
00217   nassertr(geom != (Geom *)NULL, false);
00218 
00219   SourceTexCoords st;
00220   st._mat = mat;
00221   st._from = from_name;
00222   st._to = to_name;
00223   st._vertex_data = geom->get_vertex_data();
00224   
00225   NewVertexData &new_data = _texcoords[st];
00226   if (new_data._vdata.is_null()) {
00227     if (!st._vertex_data->has_column(from_name)) {
00228       // No from_name column; no change.
00229       return false;
00230     }
00231 
00232     PT(GeomVertexData) new_vdata;
00233     
00234     // We have not yet converted these texcoords.  Do so now.
00235     if (st._vertex_data->has_column(to_name)) {
00236       new_vdata = new GeomVertexData(*st._vertex_data);
00237     } else {
00238       const GeomVertexColumn *old_column = 
00239         st._vertex_data->get_format()->get_column(from_name);
00240       new_vdata = st._vertex_data->replace_column
00241         (to_name, old_column->get_num_components(),
00242          old_column->get_numeric_type(),
00243          old_column->get_contents());
00244     }
00245     
00246     CPT(GeomVertexFormat) format = new_vdata->get_format();
00247     
00248     GeomVertexWriter tdata(new_vdata, to_name);
00249     GeomVertexReader fdata(new_vdata, from_name);
00250     
00251     while (!fdata.is_at_end()) {
00252       const LPoint4f &coord = fdata.get_data4f();
00253       tdata.set_data4f(coord * mat);
00254     }
00255     new_data._vdata = new_vdata;
00256   }
00257   
00258   geom->set_vertex_data(new_data._vdata);
00259   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00260   if (st._vertex_data->get_ref_count() > 1) {
00261     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00262     _vdata_assoc[st._vertex_data]._might_have_unused = true;
00263   }
00264 
00265   return true;
00266 }
00267 
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: GeomTransformer::transform_texcoords
00271 //       Access: Public
00272 //  Description: Transforms the texture coordinates in all of the
00273 //               Geoms within the indicated GeomNode by the indicated
00274 //               matrix.  Does not destructively change Geoms;
00275 //               instead, a copy will be made of each Geom to be
00276 //               changed, in case multiple GeomNodes reference the
00277 //               same Geom. Returns true if the GeomNode was changed,
00278 //               false otherwise.
00279 ////////////////////////////////////////////////////////////////////
00280 bool GeomTransformer::
00281 transform_texcoords(GeomNode *node, const InternalName *from_name,
00282                     InternalName *to_name, const LMatrix4f &mat) {
00283   bool any_changed = false;
00284 
00285   GeomNode::CDWriter cdata(node->_cycler);
00286   GeomNode::GeomList::iterator gi;
00287   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00288   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00289     GeomNode::GeomEntry &entry = (*gi);
00290     PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00291     if (transform_texcoords(new_geom, from_name, to_name, mat)) {
00292       entry._geom = new_geom;
00293       any_changed = true;
00294     }
00295   }
00296 
00297   return any_changed;
00298 }
00299 
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: GeomTransformer::set_color
00303 //       Access: Public
00304 //  Description: Overrides the color indicated within the Geom with
00305 //               the given replacement color.  Returns true if the
00306 //               Geom was changed, false otherwise.
00307 ////////////////////////////////////////////////////////////////////
00308 bool GeomTransformer::
00309 set_color(Geom *geom, const Colorf &color) {
00310   PStatTimer timer(_apply_set_color_collector);
00311 
00312   SourceColors sc;
00313   sc._color = color;
00314   sc._vertex_data = geom->get_vertex_data();
00315   
00316   NewVertexData &new_data = _fcolors[sc];
00317   if (new_data._vdata.is_null()) {
00318     // We have not yet converted these colors.  Do so now.
00319     if (sc._vertex_data->has_column(InternalName::get_color())) {
00320       new_data._vdata = sc._vertex_data->set_color(color);
00321     } else {
00322       new_data._vdata = sc._vertex_data->set_color
00323         (color, 1, Geom::NT_packed_dabc, Geom::C_color);
00324     }
00325   }
00326   
00327   geom->set_vertex_data(new_data._vdata);
00328   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00329   if (sc._vertex_data->get_ref_count() > 1) {
00330     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00331     _vdata_assoc[sc._vertex_data]._might_have_unused = true;
00332   }
00333 
00334   return true;
00335 }
00336 
00337 
00338 ////////////////////////////////////////////////////////////////////
00339 //     Function: GeomTransformer::set_color
00340 //       Access: Public
00341 //  Description: Overrides the color indicated within the GeomNode
00342 //               with the given replacement color.  Returns true if
00343 //               any Geom in the GeomNode was changed, false
00344 //               otherwise.
00345 ////////////////////////////////////////////////////////////////////
00346 bool GeomTransformer::
00347 set_color(GeomNode *node, const Colorf &color) {
00348   bool any_changed = false;
00349 
00350   GeomNode::CDWriter cdata(node->_cycler);
00351   GeomNode::GeomList::iterator gi;
00352   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00353   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00354     GeomNode::GeomEntry &entry = (*gi);
00355     PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00356     if (set_color(new_geom, color)) {
00357       entry._geom = new_geom;
00358       any_changed = true;
00359     }
00360   }
00361 
00362   return any_changed;
00363 }
00364 
00365 ////////////////////////////////////////////////////////////////////
00366 //     Function: GeomTransformer::transform_colors
00367 //       Access: Public
00368 //  Description: Transforms the colors in the indicated Geom by the
00369 //               indicated scale.  Returns true if the Geom was
00370 //               changed, false otherwise.
00371 ////////////////////////////////////////////////////////////////////
00372 bool GeomTransformer::
00373 transform_colors(Geom *geom, const LVecBase4f &scale) {
00374   PStatTimer timer(_apply_scale_color_collector);
00375 
00376   nassertr(geom != (Geom *)NULL, false);
00377 
00378   SourceColors sc;
00379   sc._color = scale;
00380   sc._vertex_data = geom->get_vertex_data();
00381   
00382   NewVertexData &new_data = _tcolors[sc];
00383   if (new_data._vdata.is_null()) {
00384     // We have not yet converted these colors.  Do so now.
00385     if (sc._vertex_data->has_column(InternalName::get_color())) {
00386       new_data._vdata = sc._vertex_data->scale_color(scale);
00387     } else {
00388       new_data._vdata = sc._vertex_data->set_color
00389         (scale, 1, Geom::NT_packed_dabc, Geom::C_color);
00390     }
00391   }
00392   
00393   geom->set_vertex_data(new_data._vdata);
00394   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00395   if (sc._vertex_data->get_ref_count() > 1) {
00396     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00397     _vdata_assoc[sc._vertex_data]._might_have_unused = true;
00398   }
00399 
00400   return true;
00401 }
00402 
00403 
00404 ////////////////////////////////////////////////////////////////////
00405 //     Function: GeomTransformer::transform_colors
00406 //       Access: Public
00407 //  Description: Transforms the colors in all of the Geoms within the
00408 //               indicated GeomNode by the indicated scale.  Does
00409 //               not destructively change Geoms; instead, a copy will
00410 //               be made of each Geom to be changed, in case multiple
00411 //               GeomNodes reference the same Geom. Returns true if
00412 //               the GeomNode was changed, false otherwise.
00413 ////////////////////////////////////////////////////////////////////
00414 bool GeomTransformer::
00415 transform_colors(GeomNode *node, const LVecBase4f &scale) {
00416   bool any_changed = false;
00417 
00418   GeomNode::CDWriter cdata(node->_cycler);
00419   GeomNode::GeomList::iterator gi;
00420   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00421   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00422     GeomNode::GeomEntry &entry = (*gi);
00423     PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00424     if (transform_colors(new_geom, scale)) {
00425       entry._geom = new_geom;
00426       any_changed = true;
00427     }
00428   }
00429 
00430   return any_changed;
00431 }
00432 
00433 
00434 ////////////////////////////////////////////////////////////////////
00435 //     Function: GeomTransformer::apply_texture_colors
00436 //       Access: Public
00437 //  Description: Removes textures from Geoms by applying the texture
00438 //               colors to the vertices.  
00439 //
00440 //               See apply_texure_colors(GeomNode *, RenderState *).
00441 ////////////////////////////////////////////////////////////////////
00442 bool GeomTransformer::
00443 apply_texture_colors(Geom *geom, TextureStage *ts, Texture *tex, 
00444                      const TexMatrixAttrib *tma, const Colorf &base_color,
00445                      bool keep_vertex_color) {
00446   PStatTimer timer(_apply_texture_color_collector);
00447 
00448   nassertr(geom != (Geom *)NULL, false);
00449 
00450   PT(TexturePeeker) peeker = tex->peek();
00451   if (peeker == (TexturePeeker *)NULL) {
00452     return false;
00453   }
00454 
00455   if (peeker->get_x_size() == 1 && 
00456       peeker->get_y_size() == 1 && 
00457       peeker->get_z_size() == 1) {
00458     // If it's just a one-pixel texture (e.g. a simple ram image),
00459     // don't bother scanning the UV's.  Just extract the color and
00460     // apply it.
00461     Colorf color;
00462     peeker->lookup(color, 0.0f, 0.0f);
00463     color.set(color[0] * base_color[0],
00464               color[1] * base_color[1],
00465               color[2] * base_color[2],
00466               color[3] * base_color[3]);
00467     if (keep_vertex_color) {
00468       return transform_colors(geom, color);
00469     } else {
00470       return set_color(geom, color);
00471     }
00472   }
00473 
00474   bool got_mat = false;
00475   LMatrix4f mat = LMatrix4f::ident_mat();
00476   if (tma != (TexMatrixAttrib *)NULL && tma->has_stage(ts)) {
00477     mat = tma->get_mat(ts);
00478     got_mat = !mat.almost_equal(LMatrix4f::ident_mat());
00479   }
00480 
00481   // This version of the code just applied one overall flat color to
00482   // the entire mesh.  Turned out not to be good enough.  Instead,
00483   // we'll look up each vertex in the texture map and apply the
00484   // nearest color to the vertex.
00485   /*
00486   // Scan the UV's to get the used range.  This is particularly
00487   // necessary for palettized textures.
00488 
00489   LPoint3f min_point, max_point;
00490   bool found_any = false;
00491   geom->calc_tight_bounds(min_point, max_point, found_any,
00492                           geom->get_vertex_data(),
00493                           got_mat, mat,
00494                           ts->get_texcoord_name(),
00495                           Thread::get_current_thread());
00496   if (found_any) {
00497     // Now use that UV range to determine the overall color of the
00498     // geom's texture.
00499     Colorf color;
00500     peeker->filter_rect(color, 
00501                         min_point[0], min_point[1], min_point[2],
00502                         max_point[0], max_point[1], max_point[2]);
00503     color.set(color[0] * base_color[0],
00504               color[1] * base_color[1],
00505               color[2] * base_color[2],
00506               color[3] * base_color[3]);
00507     if (keep_vertex_color) {
00508       return transform_colors(geom, color);
00509     } else {
00510       return set_color(geom, color);
00511     }
00512   }
00513 
00514   return false;
00515   */
00516 
00517   SourceTextureColors stc;
00518   stc._ts = ts;
00519   stc._tex = tex;
00520   stc._tma = tma;
00521   stc._base_color = base_color;
00522   stc._keep_vertex_color = keep_vertex_color;
00523   stc._vertex_data = geom->get_vertex_data();
00524   
00525   NewVertexData &new_data = _tex_colors[stc];
00526   if (new_data._vdata.is_null()) {
00527     // We have not yet applied these texture colors.  Do so now.
00528 
00529     PT(GeomVertexData) vdata;
00530 
00531     // Make sure the vdata has a color column.
00532     if (stc._vertex_data->has_column(InternalName::get_color())) {
00533       vdata = new GeomVertexData(*stc._vertex_data);
00534     } else {
00535       // Create a color column where there wasn't one before.
00536       vdata = new GeomVertexData(*stc._vertex_data->set_color
00537                                  (Colorf(1.0f, 1.0f, 1.0f, 1.0f), 1, Geom::NT_packed_dabc, Geom::C_color));
00538       keep_vertex_color = false;
00539     }
00540     
00541     // Check whether it has 2-d or 3-d texture coordinates.
00542     bool tex3d = false;
00543     const GeomVertexColumn *column = vdata->get_format()->get_column(ts->get_texcoord_name());
00544     if (column == (GeomVertexColumn *)NULL) {
00545       return false;
00546     }
00547     if (column->get_num_components() >= 3) {
00548       tex3d = true;
00549     }
00550     
00551     // Now walk through the vertices and apply each color from the
00552     // texture as we go.
00553     if (keep_vertex_color) {
00554       // We want to modulate the existing vertex color.
00555       GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
00556       GeomVertexRewriter gcolor(vdata, InternalName::get_color());
00557       
00558       if (got_mat || tex3d) {
00559         while (!gtexcoord.is_at_end()) {
00560           TexCoord3f p = gtexcoord.get_data3f();
00561           Colorf c = gcolor.get_data4f();
00562           p = p * mat;
00563           Colorf color;
00564           peeker->lookup(color, p[0], p[1], p[2]);
00565           color.set(color[0] * base_color[0] * c[0],
00566                     color[1] * base_color[1] * c[1],
00567                     color[2] * base_color[2] * c[2],
00568                     color[3] * base_color[3] * c[3]);
00569           gcolor.set_data4f(color);
00570         }
00571       } else {
00572         while (!gtexcoord.is_at_end()) {
00573           TexCoordf p = gtexcoord.get_data2f();
00574           Colorf c = gcolor.get_data4f();
00575           Colorf color;
00576           peeker->lookup(color, p[0], p[1]);
00577           color.set(color[0] * base_color[0] * c[0],
00578                     color[1] * base_color[1] * c[1],
00579                     color[2] * base_color[2] * c[2],
00580                     color[3] * base_color[3] * c[3]);
00581           gcolor.set_data4f(color);
00582         }
00583       }
00584     } else {
00585       // We want to replace any existing vertex color.
00586       GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
00587       GeomVertexWriter gcolor(vdata, InternalName::get_color());
00588       
00589       if (got_mat || tex3d) {
00590         while (!gtexcoord.is_at_end()) {
00591           TexCoord3f p = gtexcoord.get_data3f();
00592           p = p * mat;
00593           Colorf color;
00594           peeker->lookup(color, p[0], p[1], p[2]);
00595           color.set(color[0] * base_color[0],
00596                     color[1] * base_color[1],
00597                     color[2] * base_color[2],
00598                     color[3] * base_color[3]);
00599           gcolor.set_data4f(color);
00600         }
00601       } else {
00602         while (!gtexcoord.is_at_end()) {
00603           TexCoordf p = gtexcoord.get_data2f();
00604           Colorf color;
00605           peeker->lookup(color, p[0], p[1]);
00606           color.set(color[0] * base_color[0],
00607                     color[1] * base_color[1],
00608                     color[2] * base_color[2],
00609                     color[3] * base_color[3]);
00610           gcolor.set_data4f(color);
00611         }
00612       }
00613     }
00614 
00615     new_data._vdata = vdata;
00616   }
00617 
00618   geom->set_vertex_data(new_data._vdata);
00619   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00620   if (stc._vertex_data->get_ref_count() > 1) {
00621     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00622     _vdata_assoc[stc._vertex_data]._might_have_unused = true;
00623   }
00624 
00625   return true;
00626 }
00627 
00628 ////////////////////////////////////////////////////////////////////
00629 //     Function: GeomTransformer::apply_texture_colors
00630 //       Access: Public
00631 //  Description: Removes textures from Geoms by applying the texture
00632 //               colors to the vertices.  This is primarily useful to
00633 //               simplify a low-LOD model.
00634 //
00635 //               Only the bottommost texture is used (if there is more
00636 //               than one), and it is applied as if it were
00637 //               M_modulate, and WM_repeat, regardless of its actual
00638 //               settings.  If the texture has a simple_ram_image,
00639 //               this may be used if the main image isn't resident.
00640 //
00641 //               After this call, there will be no texturing specified
00642 //               on the GeomNode level.  Of course, there might still
00643 //               be texturing inherited from above.
00644 ////////////////////////////////////////////////////////////////////
00645 bool GeomTransformer::
00646 apply_texture_colors(GeomNode *node, const RenderState *state) {
00647   bool any_changed = false;
00648 
00649   GeomNode::CDWriter cdata(node->_cycler);
00650   GeomNode::GeomList::iterator gi;
00651   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00652   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00653     GeomNode::GeomEntry &entry = (*gi);
00654     CPT(RenderState) geom_state = state->compose(entry._state);
00655 
00656     const TextureAttrib *ta = DCAST(TextureAttrib, geom_state->get_attrib(TextureAttrib::get_class_slot()));
00657     if (ta != (TextureAttrib *)NULL) {
00658       CPT(TextureAttrib) ta2 = ta->filter_to_max(1);
00659       if (ta2->get_num_on_stages() > 0) {
00660         TextureStage *ts = ta2->get_on_stage(0);
00661         Texture *tex = ta2->get_on_texture(ts);
00662         const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, geom_state->get_attrib(TexMatrixAttrib::get_class_slot()));
00663 
00664         const ColorAttrib *ca = DCAST(ColorAttrib, geom_state->get_attrib(ColorAttrib::get_class_slot()));
00665         Colorf base_color(1.0f, 1.0f, 1.0f, 1.0f);
00666         bool keep_vertex_color = true;
00667         if (ca != (ColorAttrib *)NULL && ca->get_color_type() == ColorAttrib::T_flat) {
00668           base_color = ca->get_color();
00669           keep_vertex_color = false;
00670         }
00671 
00672         PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00673         if (apply_texture_colors(new_geom, ts, tex, tma, base_color, keep_vertex_color)) {
00674           entry._geom = new_geom;
00675           any_changed = true;
00676 
00677           if (new_geom->get_vertex_data()->has_column(InternalName::get_color())) {
00678             // Ensure we have a ColorAttrib::make_vertex() attrib.
00679             CPT(RenderState) color_state = entry._state->set_attrib(ColorAttrib::make_vertex());
00680             if (entry._state != color_state) {
00681               entry._state = color_state;
00682               any_changed = true;
00683             }
00684           }
00685         }
00686 
00687         // Also remove any texture references from the GeomState.
00688         CPT(RenderState) no_tex_state = entry._state->remove_attrib(TextureAttrib::get_class_slot());
00689         if (entry._state != no_tex_state) {
00690           entry._state = no_tex_state;
00691           any_changed = true;
00692         }
00693       }
00694     }
00695   }
00696 
00697   return any_changed;
00698 }
00699 
00700 ////////////////////////////////////////////////////////////////////
00701 //     Function: GeomTransformer::apply_state
00702 //       Access: Public
00703 //  Description: Applies the indicated render state to all the of
00704 //               Geoms.  Returns true if the GeomNode was changed,
00705 //               false otherwise.
00706 ////////////////////////////////////////////////////////////////////
00707 bool GeomTransformer::
00708 apply_state(GeomNode *node, const RenderState *state) {
00709   bool any_changed = false;
00710 
00711   GeomNode::CDWriter cdata(node->_cycler);
00712   GeomNode::GeomList::iterator gi;
00713   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00714   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00715     GeomNode::GeomEntry &entry = (*gi);
00716     CPT(RenderState) new_state = state->compose(entry._state);
00717     if (entry._state != new_state) {
00718       entry._state = new_state;
00719       any_changed = true;
00720     }
00721   }
00722 
00723   return any_changed;
00724 }
00725 
00726 ////////////////////////////////////////////////////////////////////
00727 //     Function: GeomTransformer::set_format
00728 //       Access: Public
00729 //  Description: Changes the GeomVertexData of the indicated Geom to
00730 //               use the specified format.
00731 ////////////////////////////////////////////////////////////////////
00732 bool GeomTransformer::
00733 set_format(Geom *geom, const GeomVertexFormat *new_format) {
00734   PStatTimer timer(_apply_set_format_collector);
00735 
00736   nassertr(geom != (Geom *)NULL, false);
00737 
00738   SourceFormat sf;
00739   sf._format = new_format;
00740   sf._vertex_data = geom->get_vertex_data();
00741   
00742   NewVertexData &new_data = _format[sf];
00743   if (new_data._vdata.is_null()) {
00744     if (sf._vertex_data->get_format() == new_format) {
00745       // No change.
00746       return false;
00747     }
00748 
00749     // We have not yet converted this vertex data.  Do so now.
00750     PT(GeomVertexData) new_vdata = new GeomVertexData(*sf._vertex_data);
00751     new_vdata->set_format(new_format);
00752     new_data._vdata = new_vdata;
00753   }
00754   
00755   geom->set_vertex_data(new_data._vdata);
00756   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00757   if (sf._vertex_data->get_ref_count() > 1) {
00758     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00759     _vdata_assoc[sf._vertex_data]._might_have_unused = true;
00760   }
00761 
00762   return true;
00763 }
00764 
00765 ////////////////////////////////////////////////////////////////////
00766 //     Function: GeomTransformer::remove_column
00767 //       Access: Public
00768 //  Description: Removes the named column from the vertex data in the
00769 //               Geom.  Returns true if the Geom was changed, false
00770 //               otherwise.
00771 ////////////////////////////////////////////////////////////////////
00772 bool GeomTransformer::
00773 remove_column(Geom *geom, const InternalName *column) {
00774   CPT(GeomVertexFormat) format = geom->get_vertex_data()->get_format();
00775   if (!format->has_column(column)) {
00776     return false;
00777   }
00778 
00779   PT(GeomVertexFormat) new_format = new GeomVertexFormat(*format);
00780   new_format->remove_column(column);
00781   new_format->pack_columns();
00782   format = GeomVertexFormat::register_format(new_format);
00783 
00784   return set_format(geom, format);
00785 }
00786 
00787 
00788 ////////////////////////////////////////////////////////////////////
00789 //     Function: GeomTransformer::remove_column
00790 //       Access: Public
00791 //  Description: Removes the named column from the vertex datas within
00792 //               the GeomNode.  Returns true if the GeomNode was
00793 //               changed, false otherwise.
00794 ////////////////////////////////////////////////////////////////////
00795 bool GeomTransformer::
00796 remove_column(GeomNode *node, const InternalName *column) {
00797   bool any_changed = false;
00798 
00799   GeomNode::CDWriter cdata(node->_cycler);
00800   GeomNode::GeomList::iterator gi;
00801   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00802   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
00803     GeomNode::GeomEntry &entry = (*gi);
00804     PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00805     if (remove_column(new_geom, column)) {
00806       entry._geom = new_geom;
00807       any_changed = true;
00808     }
00809   }
00810 
00811   return any_changed;
00812 }
00813 
00814 ////////////////////////////////////////////////////////////////////
00815 //     Function: GeomTransformer::make_compatible_state
00816 //       Access: Public
00817 //  Description: Checks if the different geoms in the GeomNode have
00818 //               different RenderStates.  If so, tries to make the 
00819 //               RenderStates the same.  It does this by
00820 //               canonicalizing the ColorAttribs, and in the future,
00821 //               possibly other attribs.
00822 ////////////////////////////////////////////////////////////////////
00823 bool GeomTransformer::
00824 make_compatible_state(GeomNode *node) {
00825   if (node->get_num_geoms() < 2) {
00826     return false;
00827   }
00828   
00829   GeomNode::CDWriter cdata(node->_cycler);
00830   GeomNode::GeomList::iterator gi;
00831   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
00832 
00833   // For each geom, calculate a canonicalized RenderState, and
00834   // classify all the geoms according to that.  By "canonicalize"
00835   // here, we simply mean removing the ColorAttrib.
00836   
00837   typedef pmap <CPT(RenderState), pvector<int> > StateTable;
00838   StateTable state_table;
00839   
00840   for (int i = 0; i < (int)geoms->size(); i++) {
00841     GeomNode::GeomEntry &entry = (*geoms)[i];
00842     CPT(RenderState) canon = entry._state->remove_attrib(ColorAttrib::get_class_slot());
00843     state_table[canon].push_back(i);
00844   }
00845 
00846   // For each group of geoms, check for mismatch.
00847   
00848   bool any_changed = false;
00849   StateTable::iterator si;
00850   for (si = state_table.begin(); si != state_table.end(); si++) {
00851     
00852     // If the geoms in the group already have the same RenderStates,
00853     // then nothing needs to be done to this group.
00854     
00855     const pvector<int> &indices = (*si).second;
00856     bool mismatch = false;
00857     for (int i = 1; i < (int)indices.size(); i++) {
00858       if ((*geoms)[indices[i]]._state != (*geoms)[indices[0]]._state) {
00859         mismatch = true;
00860         break;
00861       }
00862     }
00863     if (!mismatch) {
00864       continue;
00865     }
00866     
00867     // The geoms do not have the same RenderState, but they could,
00868     // since their canonicalized states are the same.  Canonicalize
00869     // them, by applying the colors to the vertices.
00870     
00871     const RenderState *canon_state = (*si).first;
00872     for (int i = 0; i < (int)indices.size(); i++) {
00873       GeomNode::GeomEntry &entry = (*geoms)[indices[i]];
00874       const RenderAttrib *ra = entry._state->get_attrib_def(ColorAttrib::get_class_slot());
00875       const ColorAttrib *ca = DCAST(ColorAttrib, ra);
00876       if (ca->get_color_type() == ColorAttrib::T_vertex) {
00877         // All we need to do is ensure that the geom has a color column.
00878         if (!entry._geom.get_read_pointer()->get_vertex_data()->has_column(InternalName::get_color())) {
00879           PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00880           if (set_color(new_geom, Colorf(1,1,1,1))) {
00881             entry._geom = new_geom;
00882           }
00883         }
00884       } else {
00885         // A flat color (or "off", which is white).  Set the vertices
00886         // to the indicated flat color.
00887         Colorf c = ca->get_color();
00888         PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
00889         if (set_color(new_geom, c)) {
00890           entry._geom = new_geom;
00891         }
00892       }
00893       entry._state = canon_state->add_attrib(ColorAttrib::make_vertex());
00894       any_changed = true;
00895     }
00896   }
00897   
00898   return any_changed;
00899 }
00900 
00901 ////////////////////////////////////////////////////////////////////
00902 //     Function: GeomTransformer::reverse_normals
00903 //       Access: Public
00904 //  Description: Reverses the lighting normals on the vertex data, if
00905 //               any.  Returns true if the Geom was changed, false
00906 //               otherwise.
00907 ////////////////////////////////////////////////////////////////////
00908 bool GeomTransformer::
00909 reverse_normals(Geom *geom) {
00910   nassertr(geom != (Geom *)NULL, false);
00911   CPT(GeomVertexData) orig_data = geom->get_vertex_data();
00912   NewVertexData &new_data = _reversed_normals[orig_data];
00913   if (new_data._vdata.is_null()) {
00914     new_data._vdata = orig_data->reverse_normals();
00915   }
00916 
00917   if (new_data._vdata == orig_data) {
00918     // No change.
00919     return false;
00920   }
00921 
00922   geom->set_vertex_data(new_data._vdata);
00923   VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
00924   if (orig_data->get_ref_count() > 1) {
00925     _vdata_assoc[new_data._vdata]._might_have_unused = true;
00926     _vdata_assoc[orig_data]._might_have_unused = true;
00927   }
00928 
00929   return true;
00930 }
00931 
00932 ////////////////////////////////////////////////////////////////////
00933 //     Function: GeomTransformer::doubleside
00934 //       Access: Public
00935 //  Description: Duplicates triangles in this GeomNode so that each
00936 //               triangle is back-to-back with another triangle facing
00937 //               in the opposite direction.  If the geometry has
00938 //               vertex normals, this will also duplicate and reverse
00939 //               the normals, so that lighting will work correctly
00940 //               from both sides.  Note that calling this when the
00941 //               geometry is already doublesided (with back-to-back
00942 //               polygons) will result in multiple redundant coplanar
00943 //               polygons.
00944 //
00945 //               Also see CullFaceAttrib, which can enable rendering
00946 //               of both sides of a triangle without having to
00947 //               duplicate it (but which doesn't necessarily work in
00948 //               the presence of lighting).
00949 //
00950 //               Returns true if any Geoms are modified, false
00951 //               otherwise.
00952 ////////////////////////////////////////////////////////////////////
00953 bool GeomTransformer::
00954 doubleside(GeomNode *node) {
00955   int num_geoms = node->get_num_geoms();
00956   for (int i = 0; i < num_geoms; ++i) {
00957     CPT(Geom) orig_geom = node->get_geom(i);
00958     bool has_normals = (orig_geom->get_vertex_data()->has_column(InternalName::get_normal()));
00959     if (has_normals) {
00960       // If the geometry has normals, we have to duplicate it to
00961       // reverse the normals on the duplicate copy.
00962       PT(Geom) new_geom = orig_geom->reverse();
00963       reverse_normals(new_geom);
00964       node->add_geom(new_geom, node->get_geom_state(i));
00965       
00966     } else {
00967       // If there are no normals, we can just doubleside it in
00968       // place.  This is preferable because we can share vertices.
00969       orig_geom.clear();
00970       node->modify_geom(i)->doubleside_in_place();
00971     }
00972   }
00973   
00974   return (num_geoms != 0);
00975 }
00976 
00977 
00978 ////////////////////////////////////////////////////////////////////
00979 //     Function: GeomTransformer::reverse
00980 //       Access: Public
00981 //  Description: Reverses the winding order of triangles in this
00982 //               GeomNode so that each triangle is facing in the
00983 //               opposite direction.  If the geometry has vertex
00984 //               normals, this will also reverse the normals, so that
00985 //               lighting will work correctly.
00986 //
00987 //               Also see CullFaceAttrib, which can effectively change
00988 //               the facing of a triangle having to modify its
00989 //               vertices (but which doesn't necessarily work in the
00990 //               presence of lighting).
00991 //
00992 //               Returns true if any Geoms are modified, false
00993 //               otherwise.
00994 ////////////////////////////////////////////////////////////////////
00995 bool GeomTransformer::
00996 reverse(GeomNode *node) {
00997   int num_geoms = node->get_num_geoms();
00998   for (int i = 0; i < num_geoms; ++i) {
00999     PT(Geom) geom = node->modify_geom(i);
01000     geom->reverse_in_place();
01001     reverse_normals(geom);
01002   }
01003   
01004   return (num_geoms != 0);
01005 }
01006 
01007 ////////////////////////////////////////////////////////////////////
01008 //     Function: GeomTransformer::finish_apply
01009 //       Access: Public
01010 //  Description: Should be called after performing any
01011 //               operations--particularly
01012 //               PandaNode::apply_attribs_to_vertices()--that might
01013 //               result in new GeomVertexData objects being duplicated
01014 //               and modified.  This walks through those newly
01015 //               duplicated objects and ensures that redundant unused
01016 //               vertices have not been created, removing them if they
01017 //               have.
01018 ////////////////////////////////////////////////////////////////////
01019 void GeomTransformer::
01020 finish_apply() {
01021   VertexDataAssocMap::iterator vi;
01022   for (vi = _vdata_assoc.begin(); vi != _vdata_assoc.end(); ++vi) {
01023     const GeomVertexData *vdata = (*vi).first;
01024     VertexDataAssoc &assoc = (*vi).second;
01025     if (assoc._might_have_unused) {
01026       assoc.remove_unused_vertices(vdata);
01027     }
01028   }
01029   _vdata_assoc.clear();
01030 
01031   _texcoords.clear();
01032   _fcolors.clear();
01033   _tcolors.clear();
01034   _format.clear();
01035   _reversed_normals.clear();
01036 }
01037   
01038 ////////////////////////////////////////////////////////////////////
01039 //     Function: GeomTransformer::collect_vertex_data
01040 //       Access: Public
01041 //  Description: Collects together GeomVertexDatas from different
01042 //               geoms into one big (or several big) GeomVertexDatas.
01043 //               Returns the number of unique GeomVertexDatas created.
01044 //
01045 //               If format_only is true, this only makes
01046 //               GeomVertexFormats compatible; it does not otherwise
01047 //               combine vertices.
01048 //
01049 //               You should follow this up with a call to
01050 //               finish_collect(), but you probably don't want to call
01051 //               this method directly anyway.  Call
01052 //               SceneGraphReducer::collect_vertex_data() instead.
01053 ////////////////////////////////////////////////////////////////////
01054 int GeomTransformer::
01055 collect_vertex_data(Geom *geom, int collect_bits, bool format_only) {
01056   CPT(GeomVertexData) vdata = geom->get_vertex_data();
01057   if (vdata->get_num_rows() > _max_collect_vertices) {
01058     // Don't even bother.
01059     return 0;
01060   }
01061 
01062   CPT(GeomVertexFormat) format = vdata->get_format();
01063 
01064   NewCollectedKey key;
01065   if ((collect_bits & SceneGraphReducer::CVD_name) != 0) {
01066     key._name = vdata->get_name();
01067   }
01068   if ((collect_bits & SceneGraphReducer::CVD_format) != 0) {
01069     key._format = format;
01070   }
01071   if ((collect_bits & SceneGraphReducer::CVD_usage_hint) != 0) {
01072     key._usage_hint = vdata->get_usage_hint();
01073   } else {
01074     key._usage_hint = Geom::UH_unspecified;
01075   }
01076   if ((collect_bits & SceneGraphReducer::CVD_animation_type) != 0) {
01077     key._animation_type = format->get_animation().get_animation_type();
01078   } else {
01079     key._animation_type = Geom::AT_none;
01080   }
01081 
01082   AlreadyCollectedMap::const_iterator ai;
01083   ai = _already_collected_map.find(vdata);
01084   if (ai != _already_collected_map.end()) {
01085     // We've previously collected this vertex data; reuse it.
01086     const AlreadyCollectedData &acd = (*ai).second;
01087     SourceGeom source_geom;
01088     source_geom._geom = geom;
01089     source_geom._vertex_offset = acd._vertex_offset;
01090     acd._ncd->_source_geoms.push_back(source_geom);
01091     return 0;
01092   }
01093 
01094   // We haven't collected this vertex data yet; associate it with a
01095   // new data.
01096   NewCollectedMap::iterator ni = _new_collected_map.find(key);
01097   NewCollectedData *ncd;
01098   if (ni != _new_collected_map.end()) {
01099     ncd = (*ni).second;
01100 
01101   } else {
01102     // We haven't encountered a compatible GeomVertexData before.
01103     // Create a new entry.
01104     ncd = new NewCollectedData(vdata);
01105     _new_collected_list.push_back(ncd);
01106     _new_collected_map[key] = ncd;
01107   }
01108 
01109   if (ncd->_new_format != format) {
01110     ncd->_new_format = format->get_union_format(ncd->_new_format);
01111   }
01112 
01113   int this_num_vertices = vdata->get_num_rows();
01114 
01115   if (!format_only &&
01116       ncd->_num_vertices + this_num_vertices > _max_collect_vertices) {
01117     // Whoa, hold the phone!  Too many vertices going into this one
01118     // GeomVertexData object; we'd better start over.
01119     ncd = new NewCollectedData(vdata);
01120     _new_collected_list.push_back(ncd);
01121     _new_collected_map[key] = ncd;
01122   }
01123 
01124   int vertex_offset = ncd->_num_vertices;
01125 
01126   AlreadyCollectedData &acd = _already_collected_map[vdata];
01127   acd._ncd = ncd;
01128   acd._vertex_offset = vertex_offset;
01129 
01130   SourceGeom source_geom;
01131   source_geom._geom = geom;
01132   source_geom._vertex_offset = vertex_offset;
01133   ncd->_source_geoms.push_back(source_geom);
01134   
01135   SourceData source_data;
01136   source_data._vdata = vdata;
01137   source_data._num_vertices = this_num_vertices;
01138 
01139   ncd->_source_datas.push_back(source_data);
01140   ncd->_num_vertices += this_num_vertices;
01141 
01142   return 0;
01143 }
01144 
01145 
01146 ////////////////////////////////////////////////////////////////////
01147 //     Function: GeomTransformer::collect_vertex_data
01148 //       Access: Public
01149 //  Description: Collects together individual GeomVertexData
01150 //               structures that share the same format into one big
01151 //               GeomVertexData structure.  This is intended to
01152 //               minimize context switches on the graphics card.
01153 //
01154 //               If format_only is true, this only makes
01155 //               GeomVertexFormats compatible; it does not otherwise
01156 //               combine vertices.
01157 //
01158 //               You should follow this up with a call to
01159 //               finish_collect(), but you probably don't want to call
01160 //               this method directly anyway.  Call
01161 //               SceneGraphReducer::collect_vertex_data() instead.
01162 ////////////////////////////////////////////////////////////////////
01163 int GeomTransformer::
01164 collect_vertex_data(GeomNode *node, int collect_bits, bool format_only) {
01165   int num_adjusted = 0;
01166   GeomTransformer *dynamic = NULL;
01167 
01168   GeomNode::CDWriter cdata(node->_cycler);
01169   GeomNode::GeomList::iterator gi;
01170   PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
01171   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
01172     GeomNode::GeomEntry &entry = (*gi);
01173     PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
01174     entry._geom = new_geom;
01175 
01176     if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&
01177         new_geom->get_vertex_data()->get_usage_hint() < Geom::UH_static) {
01178       // This one has some dynamic properties.  Collect it
01179       // independently of the outside world.
01180       if (dynamic == (GeomTransformer *)NULL) {
01181         dynamic = new GeomTransformer(*this);
01182       }
01183       num_adjusted += dynamic->collect_vertex_data(new_geom, collect_bits, format_only);
01184       
01185     } else {
01186       num_adjusted += collect_vertex_data(new_geom, collect_bits, format_only);
01187     }
01188   }
01189 
01190   if (dynamic != (GeomTransformer *)NULL) {
01191     num_adjusted += dynamic->finish_collect(format_only);
01192     delete dynamic;
01193   }
01194 
01195   return num_adjusted;
01196 }
01197 
01198 ////////////////////////////////////////////////////////////////////
01199 //     Function: GeomTransformer::finish_collect
01200 //       Access: Public
01201 //  Description: This should be called after a call to
01202 //               collect_vertex_data() to finalize the changes and
01203 //               apply them to the vertices in the graph.  If this is
01204 //               not called, it will be called automatically by the
01205 //               GeomTransformer destructor.
01206 //
01207 //               If format_only is true, this returns the number of
01208 //               GeomVertexDatas modified to use a new format.  If
01209 //               false, it returns the number of GeomVertexDatas
01210 //               created.
01211 ////////////////////////////////////////////////////////////////////
01212 int GeomTransformer::
01213 finish_collect(bool format_only) {
01214   int num_adjusted = 0;
01215 
01216   NewCollectedList::iterator nci;
01217   for (nci = _new_collected_list.begin(); 
01218        nci != _new_collected_list.end();
01219        ++nci) {
01220     NewCollectedData *ncd = (*nci);
01221     if (format_only) {
01222       num_adjusted += ncd->apply_format_only_changes();
01223     } else {
01224       num_adjusted += ncd->apply_collect_changes();
01225     }
01226     delete ncd;
01227   }
01228 
01229   _new_collected_list.clear();
01230   _new_collected_map.clear();
01231   _already_collected_map.clear();
01232 
01233   return num_adjusted;
01234 }
01235 
01236 ////////////////////////////////////////////////////////////////////
01237 //     Function: GeomTransformer::premunge_geom
01238 //       Access: Public
01239 //  Description: Uses the indicated munger to premunge the given Geom
01240 //               to optimize it for eventual rendering.  See
01241 //               SceneGraphReducer::premunge().
01242 ////////////////////////////////////////////////////////////////////
01243 PT(Geom) GeomTransformer::
01244 premunge_geom(const Geom *geom, GeomMunger *munger) {
01245   // This method had been originally provided to cache the result for
01246   // a particular geom/munger and vdata/munger combination, similar to
01247   // the way other GeomTransformer methods work.  On reflection, this
01248   // additional caching is not necessary, since the GeomVertexFormat
01249   // does its own caching, and there's no danger of that cache filling
01250   // up during the span of one frame.
01251 
01252   CPT(GeomVertexData) vdata = geom->get_vertex_data();
01253   vdata = munger->premunge_data(vdata);
01254   CPT(Geom) pgeom = geom;
01255   munger->premunge_geom(pgeom, vdata);
01256 
01257   PT(Geom) geom_copy = pgeom->make_copy();
01258   geom_copy->set_vertex_data(vdata);
01259 
01260   return geom_copy;
01261 }
01262 
01263 ////////////////////////////////////////////////////////////////////
01264 //     Function: GeomTransformer::NewCollectedData::Constructor
01265 //       Access: Public
01266 //  Description: 
01267 ////////////////////////////////////////////////////////////////////
01268 GeomTransformer::NewCollectedData::
01269 NewCollectedData(const GeomVertexData *source_data) {
01270   _new_format = source_data->get_format();
01271   _vdata_name = source_data->get_name();
01272   _usage_hint = source_data->get_usage_hint();
01273   _num_vertices = 0;
01274 }
01275 
01276 ////////////////////////////////////////////////////////////////////
01277 //     Function: GeomTransformer::NewCollectedData::apply_format_only_changes
01278 //       Access: Public
01279 //  Description: Actually adjusts the GeomVertexDatas found in a
01280 //               collect_vertex_data() format-only call to have the
01281 //               same vertex format.  Returns the number of vdatas
01282 //               modified.
01283 ////////////////////////////////////////////////////////////////////
01284 int GeomTransformer::NewCollectedData::
01285 apply_format_only_changes() {
01286   int num_modified = 0;
01287 
01288   // We probably don't need to use a map, since
01289   // GeomVertexData::convert_to() already caches its result, but we do
01290   // it anyway just in case there's danger of overflowing the cache.
01291   // What the heck, it's easy to do.
01292   typedef pmap< CPT(GeomVertexData), CPT(GeomVertexData) > VDataMap;
01293   VDataMap vdata_map;
01294 
01295   SourceGeoms::iterator sgi;
01296   for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
01297     SourceGeom &sg = (*sgi);
01298     CPT(GeomVertexData) orig_data = sg._geom->get_vertex_data();
01299 
01300     if (orig_data->get_format() != _new_format) {
01301       VDataMap::iterator mi = vdata_map.find(orig_data);
01302       if (mi != vdata_map.end()) {
01303         // Already modified this vdata.
01304         sg._geom->set_vertex_data((*mi).second);
01305 
01306       } else {
01307         // Modify this vdata to the new format.
01308         CPT(GeomVertexData) new_data = orig_data->convert_to(_new_format);
01309         vdata_map[orig_data] = new_data;
01310         ++num_modified;
01311 
01312         sg._geom->set_vertex_data(new_data);
01313       }
01314     }
01315   }
01316 
01317   return num_modified;
01318 }
01319 
01320 ////////////////////////////////////////////////////////////////////
01321 //     Function: GeomTransformer::NewCollectedData::apply_collect_changes
01322 //       Access: Public
01323 //  Description: Actually combines all of the vertex datas found in a
01324 //               previous call to collect_vertex_data().
01325 ////////////////////////////////////////////////////////////////////
01326 int GeomTransformer::NewCollectedData::
01327 apply_collect_changes() {
01328   if (_num_vertices == 0) {
01329     return 0;
01330   }
01331 
01332   _new_data =
01333     new GeomVertexData(_vdata_name, _new_format, _usage_hint);
01334 
01335   _new_data->unclean_set_num_rows(_num_vertices);
01336 
01337   // Copy each source data into the new GeomVertexData, one at a time.
01338   int vertex_offset = 0;
01339   SourceDatas::iterator sdi;
01340   for (sdi = _source_datas.begin(); sdi != _source_datas.end(); ++sdi) {
01341     SourceData &sd = (*sdi);
01342     CPT(GeomVertexData) vdata = sd._vdata;
01343 
01344     if (_new_format != vdata->get_format()) {
01345       // Convert (non-destructively) the current Geom's vertex
01346       // data to the new format, so we can just blindly append the
01347       // vertices to _new_data, within append_vdata().
01348       vdata = vdata->convert_to(_new_format);
01349     }
01350 
01351     append_vdata(vdata, vertex_offset);
01352     vertex_offset += sd._num_vertices;
01353   }
01354 
01355   nassertr(vertex_offset == _num_vertices, 0);
01356 
01357   if (_new_btable != (TransformBlendTable *)NULL) {
01358     _new_btable->set_rows(_new_btable_rows);
01359     _new_data->set_transform_blend_table(_new_btable);
01360   }
01361 
01362   update_geoms();
01363 
01364   _new_data.clear();
01365   _new_btable.clear();
01366   _new_btable_rows.clear();
01367 
01368   return 1;
01369 }
01370 
01371 ////////////////////////////////////////////////////////////////////
01372 //     Function: GeomTransformer::NewCollectedData::append_vdata
01373 //       Access: Public
01374 //  Description: Appends the vertices from the indicated source
01375 //               GeomVertexData to the end of the working data.
01376 ////////////////////////////////////////////////////////////////////
01377 void GeomTransformer::NewCollectedData::
01378 append_vdata(const GeomVertexData *vdata, int vertex_offset) {
01379   for (int i = 0; i < vdata->get_num_arrays(); ++i) {
01380     PT(GeomVertexArrayData) new_array = _new_data->modify_array(i);
01381     CPT(GeomVertexArrayData) old_array = vdata->get_array(i);
01382     int stride = _new_format->get_array(i)->get_stride();
01383     int start_byte = vertex_offset * stride;
01384     int copy_bytes = old_array->get_data_size_bytes();
01385     nassertv(start_byte + copy_bytes <= new_array->get_data_size_bytes());
01386     
01387     new_array->modify_handle()->copy_subdata_from
01388       (start_byte, copy_bytes, 
01389        old_array->get_handle(), 0, copy_bytes);
01390   }
01391 
01392   // Also, copy the animation data (if any).  This means combining
01393   // transform and/or slider tables, and might therefore mean
01394   // remapping transform indices in the vertices.  Each of these has a
01395   // slightly different way to handle the remapping, because they have
01396   // slightly different kinds of data.
01397   
01398   if (vdata->get_transform_table() != (TransformTable *)NULL ||
01399       _new_data->get_transform_table() != (TransformTable *)NULL) {
01400     // The TransformTable.
01401     CPT(TransformTable) old_table;
01402     if (vdata->get_transform_table() != (TransformTable *)NULL) {
01403       old_table = vdata->get_transform_table();
01404     } else {
01405       PT(TransformTable) temp_table = new TransformTable;
01406       // There's an implicit identity transform for all nodes.
01407       PT(VertexTransform) identity_transform = new UserVertexTransform("identity");
01408       temp_table->add_transform(identity_transform);
01409       old_table = TransformTable::register_table(temp_table);
01410     }
01411     
01412     // First, build a mapping of the transforms we already have in the
01413     // current table.  We must do this because the TransformTable
01414     // doesn't automatically unquify index numbers for us (it doesn't
01415     // store an index).
01416     typedef pmap<const VertexTransform *, int> AddedTransforms;
01417     AddedTransforms added_transforms;
01418     
01419     int num_old_transforms = old_table->get_num_transforms();
01420     for (int i = 0; i < num_old_transforms; i++) {
01421       added_transforms[old_table->get_transform(i)] = i;
01422     }
01423     
01424     // Now create a new table.  We have to create a new table instead
01425     // of modifying the existing one, since a registered
01426     // TransformTable cannot be modified.
01427     PT(TransformTable) new_table;
01428     if (_new_data->get_transform_table() != (TransformTable *)NULL) {
01429       new_table = new TransformTable(*_new_data->get_transform_table());
01430     } else {
01431       new_table = new TransformTable;
01432     }
01433     
01434     // Now walk through the old table and copy over its transforms.
01435     // We will build up an IndexMap of old index numbers to new index
01436     // numbers while we go, which we can use to modify the vertices.
01437     IndexMap transform_map;
01438     
01439     int num_transforms = old_table->get_num_transforms();
01440     transform_map.reserve(num_transforms);
01441     for (int ti = 0; ti < num_transforms; ++ti) {
01442       const VertexTransform *transform = old_table->get_transform(ti);
01443       AddedTransforms::iterator ai = added_transforms.find(transform);
01444       if (ai != added_transforms.end()) {
01445         // Already got this one in the table.
01446         transform_map.push_back((*ai).second);
01447       } else {
01448         // This is a new one.
01449         int tj = new_table->add_transform(transform);
01450         transform_map.push_back(tj);
01451         added_transforms[transform] = tj;
01452       }
01453     }
01454     _new_data->set_transform_table(TransformTable::register_table(new_table));
01455 
01456     // And now modify the vertices to update the indices to their new
01457     // values in the new table.  This requires a nested loop, since
01458     // each column of transform_index might define multiple index
01459     // values.
01460     GeomVertexRewriter index(_new_data, InternalName::get_transform_index());
01461     if (index.has_column()) {
01462       int num_values = index.get_column()->get_num_values();
01463       int num_rows = vdata->get_num_rows();
01464       int new_index[4];
01465       
01466       index.set_row(vertex_offset);
01467       for (int ci = 0; ci < num_rows; ++ci) {
01468         const int *orig_index = index.get_data4i();
01469         for (int i = 0; i < num_values; i++) {
01470           nassertv(orig_index[i] >= 0 && orig_index[i] < (int)transform_map.size());
01471           new_index[i] = transform_map[orig_index[i]];
01472         }
01473         index.set_data4i(new_index);
01474       }
01475     }
01476   }
01477 
01478   if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) {
01479     // The TransformBlendTable.  This one is the easiest, because we
01480     // can modify it directly, and it will uniquify blend objects for
01481     // us.
01482 
01483     // We have few special optimizations to handle the
01484     // TransformBlendTable, since it's a very common case and
01485     // therefore worth spending a bit of effort to optimize deeply.
01486     
01487     CPT(TransformBlendTable) old_btable = vdata->get_transform_blend_table();
01488     
01489     if (_new_btable == (TransformBlendTable *)NULL) {
01490       _new_btable = new TransformBlendTable;
01491       _new_btable->add_blend(TransformBlend());
01492     }
01493 
01494     SparseArray new_rows = old_btable->get_rows();
01495     new_rows <<= vertex_offset;
01496     _new_btable_rows |= new_rows;
01497 
01498     // We still need to build up the IndexMap.
01499     IndexMap blend_map;
01500 
01501     int num_blends = old_btable->get_num_blends();
01502     blend_map.reserve(num_blends);
01503     for (int bi = 0; bi < num_blends; ++bi) {
01504       int bj = _new_btable->add_blend(old_btable->get_blend(bi));
01505       blend_map.push_back(bj);
01506     }
01507 
01508     // Modify the indices.  This is simpler than the transform_index,
01509     // above, because each column of transform_blend may only define
01510     // one index value.
01511     GeomVertexRewriter index(_new_data, InternalName::get_transform_blend());
01512     if (index.has_column()) {
01513       int num_rows = vdata->get_num_rows();
01514       index.set_row(vertex_offset);
01515 
01516       for (int ci = 0; ci < num_rows; ++ci) {
01517         int orig_index = index.get_data1i();
01518         nassertv(orig_index >= 0 && orig_index < (int)blend_map.size());
01519         int new_index = blend_map[orig_index];
01520         index.set_data1i(new_index);
01521       }
01522     }
01523   }
01524   
01525   if (vdata->get_slider_table() != (SliderTable *)NULL) {
01526     // The SliderTable.  This one requires making a copy, like the
01527     // TransformTable (since it can't be modified once registered
01528     // either), but at least it uniquifies sliders added to it.  Also,
01529     // it doesn't require indexing into it, so we don't have to build
01530     // an IndexMap to modify the vertices with.
01531     const SliderTable *old_sliders = vdata->get_slider_table();
01532     PT(SliderTable) new_sliders;
01533     if (_new_data->get_slider_table() != (SliderTable *)NULL) {
01534       new_sliders = new SliderTable(*_new_data->get_slider_table());
01535     } else {
01536       new_sliders = new SliderTable;
01537     }
01538     int num_sliders = old_sliders->get_num_sliders();
01539     for (int si = 0; si < num_sliders; ++si) {
01540       SparseArray new_rows = old_sliders->get_slider_rows(si);
01541       new_rows <<= vertex_offset;
01542       new_sliders->add_slider(old_sliders->get_slider(si), new_rows);
01543     }
01544     _new_data->set_slider_table(SliderTable::register_table(new_sliders));
01545   }
01546 }
01547 
01548 ////////////////////////////////////////////////////////////////////
01549 //     Function: GeomTransformer::NewCollectedData::update_geoms
01550 //       Access: Public
01551 //  Description: Updates all of the source Geoms to reference the new
01552 //               vertex data.
01553 ////////////////////////////////////////////////////////////////////
01554 void GeomTransformer::NewCollectedData::
01555 update_geoms() {
01556   SourceGeoms::iterator sgi;
01557   for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
01558     SourceGeom &sg = (*sgi);
01559     sg._geom->offset_vertices(_new_data, sg._vertex_offset);
01560   }
01561 }
01562 
01563 ////////////////////////////////////////////////////////////////////
01564 //     Function: GeomTransformer::VertexDataAssoc::remove_unused_vertices
01565 //       Access: Public
01566 //  Description: 
01567 ////////////////////////////////////////////////////////////////////
01568 void GeomTransformer::VertexDataAssoc::
01569 remove_unused_vertices(const GeomVertexData *vdata) {
01570   if (_geoms.empty()) {
01571     // Trivial case.
01572     return;
01573   }
01574 
01575   PT(Thread) current_thread = Thread::get_current_thread();
01576 
01577   BitArray referenced_vertices;
01578   bool any_referenced = false;
01579   GeomList::iterator gi;
01580   for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
01581     Geom *geom = (*gi);
01582     if (geom->get_vertex_data() != vdata) {
01583       continue;
01584     }
01585 
01586     any_referenced = true;
01587     int num_primitives = geom->get_num_primitives();
01588     for (int i = 0; i < num_primitives; ++i) {
01589       CPT(GeomPrimitive) prim = geom->get_primitive(i);
01590 
01591       GeomPrimitivePipelineReader reader(prim, current_thread);
01592       int num_vertices = reader.get_num_vertices();
01593       for (int vi = 0; vi < num_vertices; ++vi) {
01594         referenced_vertices.set_bit(reader.get_vertex(vi));
01595       }
01596     }
01597   }
01598 
01599   if (!any_referenced) {
01600     return;
01601   }
01602 
01603   int num_vertices = vdata->get_num_rows();
01604   int new_num_vertices = referenced_vertices.get_num_on_bits();
01605   if (num_vertices <= new_num_vertices) {
01606     // All vertices are used.
01607     nassertv(num_vertices == new_num_vertices);
01608     return;
01609   }
01610 
01611   // Remap the vertices.
01612   int *remap_array = (int *)alloca(sizeof(int) * num_vertices);
01613   int new_index = 0;
01614   int index;
01615   int next_index = 0;
01616   for (index = 0; index < num_vertices; ++index) {
01617     if (referenced_vertices.get_bit(index)) {
01618       while (next_index <= index) {
01619         remap_array[next_index] = new_index;
01620         ++next_index;
01621       }
01622       ++new_index;
01623     }
01624   }
01625   while (next_index < num_vertices) {
01626     remap_array[next_index] = new_num_vertices - 1;
01627     ++next_index;
01628   }
01629 
01630   // Now recopy the actual vertex data, one array at a time.
01631   PT(GeomVertexData) new_vdata = new GeomVertexData(*vdata);
01632   new_vdata->unclean_set_num_rows(new_num_vertices);
01633 
01634   int num_arrays = vdata->get_num_arrays();
01635   nassertv(num_arrays == new_vdata->get_num_arrays());
01636 
01637   GeomVertexDataPipelineReader reader(vdata, current_thread);
01638   reader.check_array_readers();
01639   GeomVertexDataPipelineWriter writer(new_vdata, true, current_thread);
01640   writer.check_array_writers();
01641 
01642   for (int a = 0; a < num_arrays; ++a) {
01643     const GeomVertexArrayDataHandle *array_reader = reader.get_array_reader(a);
01644     GeomVertexArrayDataHandle *array_writer = writer.get_array_writer(a);
01645 
01646     int stride = array_reader->get_array_format()->get_stride();
01647     nassertv(stride == array_writer->get_array_format()->get_stride());
01648 
01649     int new_index = 0;
01650     int index;
01651     for (index = 0; index < num_vertices; ++index) {
01652       if (referenced_vertices.get_bit(index)) {
01653         array_writer->copy_subdata_from(new_index * stride, stride,
01654                                         array_reader,
01655                                         index * stride, stride);
01656         ++new_index;
01657       }
01658     }
01659   }
01660 
01661   // Update the subranges in the TransformBlendTable, if any.
01662   PT(TransformBlendTable) tbtable = new_vdata->modify_transform_blend_table();
01663   if (!tbtable.is_null()) {
01664     const SparseArray &rows = tbtable->get_rows();
01665     SparseArray new_rows;
01666     int num_subranges = rows.get_num_subranges();
01667     for (int si = 0; si < num_subranges; ++si) {
01668       int from = rows.get_subrange_begin(si);
01669       int to = rows.get_subrange_end(si);
01670       nassertv(from >= 0 && from < num_vertices && to > from && to <= num_vertices);
01671       int new_from = remap_array[from];
01672       int new_to = remap_array[to - 1] + 1;
01673       nassertv(new_from >= 0 && new_from < new_num_vertices && new_to >= new_from && new_to <= new_num_vertices);
01674       new_rows.set_range(new_from, new_to - new_from);
01675     }
01676     tbtable->set_rows(new_rows);
01677   }
01678 
01679   // Finally, reindex the Geoms.
01680   for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
01681     Geom *geom = (*gi);
01682     if (geom->get_vertex_data() != vdata) {
01683       continue;
01684     }
01685 
01686     int num_primitives = geom->get_num_primitives();
01687     for (int i = 0; i < num_primitives; ++i) {
01688       PT(GeomPrimitive) prim = geom->modify_primitive(i);
01689       prim->make_indexed();
01690       PT(GeomVertexArrayData) vertices = prim->modify_vertices();
01691       GeomVertexRewriter rewriter(vertices, 0, current_thread);
01692 
01693       while (!rewriter.is_at_end()) {
01694         index = rewriter.get_data1i();
01695         nassertv(index >= 0 && index < num_vertices);
01696         new_index = remap_array[index];
01697         nassertv(new_index >= 0 && new_index < new_num_vertices);
01698         rewriter.set_data1i(new_index);
01699       }
01700     }
01701 
01702     geom->set_vertex_data(new_vdata);
01703   }
01704 }
 All Classes Functions Variables Enumerations