Panda3D

multitexReducer.cxx

00001 // Filename: multitexReducer.cxx
00002 // Created by:  drose (30Nov04)
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 "multitexReducer.h"
00016 #include "pandaNode.h"
00017 #include "geomNode.h"
00018 #include "geom.h"
00019 #include "geomTransformer.h"
00020 #include "accumulatedAttribs.h"
00021 #include "sceneGraphReducer.h"
00022 #include "renderState.h"
00023 #include "transformState.h"
00024 #include "graphicsOutput.h"
00025 #include "displayRegion.h"
00026 #include "camera.h"
00027 #include "orthographicLens.h"
00028 #include "cardMaker.h"
00029 #include "colorAttrib.h"
00030 #include "colorScaleAttrib.h"
00031 #include "colorBlendAttrib.h"
00032 #include "alphaTestAttrib.h"
00033 #include "textureAttrib.h"
00034 #include "config_grutil.h"
00035 #include "config_gobj.h"
00036 #include "dcast.h"
00037 #include "geom.h"
00038 #include "geomVertexWriter.h"
00039 #include "geomVertexReader.h"
00040 
00041 ////////////////////////////////////////////////////////////////////
00042 //     Function: MultitexReducer::Constructor
00043 //       Access: Published
00044 //  Description: 
00045 ////////////////////////////////////////////////////////////////////
00046 MultitexReducer::
00047 MultitexReducer() {
00048   _target_stage = TextureStage::get_default();
00049   _use_geom = false;
00050   _allow_tex_mat = false;
00051 }
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: MultitexReducer::Destructor
00055 //       Access: Published
00056 //  Description: 
00057 ////////////////////////////////////////////////////////////////////
00058 MultitexReducer::
00059 ~MultitexReducer() {
00060 }
00061 
00062 ////////////////////////////////////////////////////////////////////
00063 //     Function: MultitexReducer::clear
00064 //       Access: Published
00065 //  Description: Removes the record of nodes that were previously
00066 //               discovered by scan().
00067 ////////////////////////////////////////////////////////////////////
00068 void MultitexReducer::
00069 clear() {
00070   _stages.clear();
00071   _geom_node_list.clear();
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: MultitexReducer::scan
00076 //       Access: Published
00077 //  Description: Starts scanning the hierarchy beginning at the
00078 //               indicated node.  Any GeomNodes discovered in the
00079 //               hierarchy with multitexture will be added to internal
00080 //               structures in the MultitexReducer so that a future
00081 //               call to flatten() will operate on all of these at
00082 //               once.
00083 //
00084 //               The indicated transform and state are the state
00085 //               inherited from the node's ancestors; any multitexture
00086 //               operations will be accumulated from the indicated
00087 //               starting state.
00088 ////////////////////////////////////////////////////////////////////
00089 void MultitexReducer::
00090 scan(PandaNode *node, const RenderState *state, const TransformState *transform) {
00091   if (grutil_cat.is_debug()) {
00092     grutil_cat.debug()
00093       << "scan(" << *node << ", " << *state << ", " << *transform << ")\n";
00094   }
00095 
00096   CPT(RenderState) next_state = state->compose(node->get_state());
00097   CPT(TransformState) next_transform = transform->compose(node->get_transform());
00098 
00099   // We must turn off any textures we come across in the scan()
00100   // operation, since the flattened texture will be applied to the
00101   // Geoms after the flatten() operation, and we don't want to still
00102   // have a multitexture specified.
00103   node->set_state(node->get_state()->remove_attrib(TextureAttrib::get_class_slot()));
00104 
00105   if (node->is_geom_node()) {
00106     scan_geom_node(DCAST(GeomNode, node), next_state, next_transform);
00107   }
00108 
00109   PandaNode::Children cr = node->get_children();
00110   int num_children = cr.get_num_children();
00111   for (int i = 0; i < num_children; i++) {
00112     scan(cr.get_child(i), next_state, next_transform);
00113   }
00114 }
00115 
00116 ////////////////////////////////////////////////////////////////////
00117 //     Function: MultitexReducer::set_target
00118 //       Access: Published
00119 //  Description: Specifies the target TextureStage (and InternalName)
00120 //               that will be left on each multitexture node after the
00121 //               flatten operation has completed.
00122 ////////////////////////////////////////////////////////////////////
00123 void MultitexReducer::
00124 set_target(TextureStage *stage) {
00125   _target_stage = stage;
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: MultitexReducer::set_use_geom
00130 //       Access: Published
00131 //  Description: Indicates whether the actual geometry will be used to
00132 //               generate the textures.  
00133 //
00134 //               If this is set to true, the geometry discovered by
00135 //               scan() will be used to generate the textures, which
00136 //               allows for the vertex and polygon colors to be made
00137 //               part of the texture itself (and makes the M_decal
00138 //               multitexture mode more reliable).  However, this only
00139 //               works if the geometry does not contain multiple
00140 //               different polygons that map to the same UV range.
00141 //
00142 //               If this is set to false (the default), a plain flat
00143 //               card will be used to generate the textures, which is
00144 //               more robust in general, but the resulting texture
00145 //               will not include vertex colors and M_decal won't work
00146 //               properly.
00147 //
00148 //               Note that in case multiple sets of texture
00149 //               coordinates are in effect, then the additional sets
00150 //               will always use the geometry anyway regardless of the
00151 //               setting of this flag (but this will not affect vertex
00152 //               color).
00153 ////////////////////////////////////////////////////////////////////
00154 void MultitexReducer::
00155 set_use_geom(bool use_geom) {
00156   _use_geom = use_geom;
00157 }
00158 
00159 ////////////////////////////////////////////////////////////////////
00160 //     Function: MultitexReducer::set_allow_tex_mat
00161 //       Access: Published
00162 //  Description: Indicates whether the resulting texture should be
00163 //               expected to be animated beyond its current range via
00164 //               a texture matrix (true), or whether the current range
00165 //               of texture coordinates will be sufficient forever
00166 //               (false).
00167 //
00168 //               If this is set to true, then the entire texture image
00169 //               must be generated, in the assumption that the user
00170 //               may animate the texture around on the surface after
00171 //               it has been composed.
00172 //
00173 //               If this is set to false (the default), then only the
00174 //               portion of the texture image which is actually in use
00175 //               must be generated, which may be a significant savings
00176 //               in texture memory.
00177 ////////////////////////////////////////////////////////////////////
00178 void MultitexReducer::
00179 set_allow_tex_mat(bool allow_tex_mat) {
00180   _allow_tex_mat = allow_tex_mat;
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: MultitexReducer::flatten
00185 //       Access: Published
00186 //  Description: Actually performs the reducing operations on the
00187 //               nodes that were previously scanned.
00188 //
00189 //               A window that can be used to create texture buffers
00190 //               suitable for rendering this geometry must be
00191 //               supplied.  This specifies the particular GSG that
00192 //               will be used to composite the textures.
00193 ////////////////////////////////////////////////////////////////////
00194 void MultitexReducer::
00195 flatten(GraphicsOutput *window) {
00196   if (grutil_cat.is_debug()) {
00197     grutil_cat.debug()
00198       << "Beginning flatten operation\n";
00199     Stages::const_iterator mi;
00200     for (mi = _stages.begin(); mi != _stages.end(); ++mi) {
00201       const StageList &stage_list = (*mi).first;
00202       const GeomList &geom_list = (*mi).second;
00203       grutil_cat.debug(false)
00204         << "stage_list for:";
00205       for (GeomList::const_iterator gi = geom_list.begin();
00206            gi != geom_list.end(); 
00207            ++gi) {
00208         const GeomInfo &geom_info = (*gi);
00209         grutil_cat.debug(false)
00210           << " (" << geom_info._geom_node->get_name() << " g" 
00211           << geom_info._index << ")";
00212       }
00213       grutil_cat.debug(false) << ":\n";
00214 
00215       StageList::const_iterator si;
00216       for (si = stage_list.begin(); si != stage_list.end(); ++si) {
00217         const StageInfo &stage_info = (*si);
00218         grutil_cat.debug(false)
00219           << "  " << *stage_info._stage << " " << *stage_info._tex
00220           << " " << *stage_info._tex_mat << "\n";
00221       }
00222     }
00223   }
00224   Stages::const_iterator mi;
00225   for (mi = _stages.begin(); mi != _stages.end(); ++mi) {
00226     const StageList &stage_list = (*mi).first;
00227     const GeomList &geom_list = (*mi).second;
00228 
00229     //determine whether this texture needs a white or transparent background
00230     bool use_transparent_bg = false;
00231     if(stage_list.size() > 0) {
00232       if(stage_list[0]._stage->get_mode() == TextureStage::M_decal)
00233         use_transparent_bg = true;
00234       else
00235         use_transparent_bg = false;
00236     }
00237     grutil_cat.debug(false) << "use transparent bg = " << use_transparent_bg << "\n";
00238 
00239 
00240 
00241     // Create an offscreen buffer in which to render the new texture.
00242 
00243     // Start by choosing a model TextureStage to determine the new
00244     // texture's properties.
00245     const StageInfo &model_stage = stage_list[choose_model_stage(stage_list)];
00246 
00247     Texture *model_tex = model_stage._tex;
00248     int aniso_degree = model_tex->get_anisotropic_degree();
00249     Texture::FilterType minfilter = model_tex->get_minfilter();
00250     Texture::FilterType magfilter = model_tex->get_magfilter();
00251 
00252     // What is the UV range of the model stage?
00253     TexCoordf min_uv, max_uv;
00254     determine_uv_range(min_uv, max_uv, model_stage, geom_list);
00255 
00256     // Maybe we only use a small portion of the texture, or maybe we
00257     // need to repeat the texture several times.
00258     LVecBase2f uv_scale;
00259     LVecBase2f uv_trans;
00260     get_uv_scale(uv_scale, uv_trans, min_uv, max_uv);
00261 
00262     // Also, if there is now a scale on the UV's (in conjunction with
00263     // whatever texture matrix might be applied on the model stage),
00264     // we may be able to adjust the image size accordingly, to keep
00265     // the pixels at about the same scale--but we have to keep it to a
00266     // power of 2.
00267     int x_size;
00268     int y_size;
00269     choose_texture_size(x_size, y_size, model_stage, uv_scale,
00270                         window);
00271 
00272     static int multitex_id = 1;
00273     ostringstream multitex_name_strm;
00274     multitex_name_strm << "multitex" << multitex_id;
00275     multitex_id++;
00276 
00277     GraphicsOutput *buffer = window->make_texture_buffer
00278       (multitex_name_strm.str(), x_size, y_size, NULL, false);
00279     buffer->set_one_shot(true);
00280     Texture *tex = buffer->get_texture();
00281     tex->set_anisotropic_degree(aniso_degree);
00282     tex->set_minfilter(minfilter);
00283     tex->set_magfilter(magfilter);
00284 
00285     // Set up the offscreen buffer to render 0,0 to 1,1.  This will be
00286     // the whole texture, but nothing outside the texture.
00287     DisplayRegion *dr = buffer->make_display_region();
00288     PT(Camera) cam_node = new Camera("multitexCam");
00289     PT(Lens) lens = new OrthographicLens();
00290     lens->set_film_size(1.0f, 1.0f);
00291     lens->set_film_offset(0.5f, 0.5f);
00292     lens->set_near_far(-1000.0f, 1000.0f);
00293     lens->set_view_mat(LMatrix4f(uv_scale[0], 0.0f, 0.0, 0.0f,
00294                                  0.0f, 1.0f, 0.0, 0.0f,
00295                                  0.0f, 0.0f, uv_scale[1], 0.0f,
00296                                  uv_trans[0], 0.0f, uv_trans[1], 1.0f));
00297     cam_node->set_lens(lens);
00298 
00299     // Create a root node for the buffer's scene graph, and set up
00300     // some appropriate properties for it.
00301     NodePath render("buffer");
00302     render.set_bin("unsorted", 0);
00303     render.set_depth_test(false);
00304     render.set_depth_write(false);
00305     render.set_two_sided(1);
00306 
00307     NodePath cam = render.attach_new_node(cam_node);
00308     dr->set_camera(cam);
00309 
00310     // If the geometry has vertex color and M_decal is in use, we must
00311     // render with use_geom in effect.  Otherwise we need not (and we
00312     // might prefer not to).
00313     bool force_use_geom = _use_geom;
00314     bool bake_in_color = _use_geom;
00315     Colorf geom_color(1.0f, 1.0f, 1.0f, 1.0f);
00316     
00317     //override the base color in the transparent pass down case.
00318     if(use_transparent_bg)
00319       geom_color = Colorf(0.0f,0.0f,0.0f,0.0f);
00320 
00321     if (!force_use_geom) {
00322       bool uses_decal = scan_decal(stage_list);
00323       if (uses_decal) {
00324         // If we have M_decal, we need to bake in the flat color
00325         // even if there is no vertex color.
00326         bake_in_color = true;
00327         
00328         /*
00329         int num_colors = 0;
00330         scan_color(geom_list, geom_color, num_colors);
00331 
00332         if (num_colors > 1) {
00333           // But if there is also vertex color, then we need to render
00334           // with the geometry.
00335           force_use_geom = true;
00336           }*/
00337       }
00338     }
00339 
00340     if (!force_use_geom) {
00341       // Put one plain white (or flat-colored) card in the background
00342       // for the first texture layer to apply onto.
00343       
00344       CardMaker cm("background");
00345       cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
00346       if (bake_in_color) {
00347         cm.set_color(geom_color);
00348       }
00349       render.attach_new_node(cm.generate());
00350 
00351     } else {
00352       // Put a vertex-colored model of the geometry in the background
00353       // for the first texture layer to apply only.
00354       nassertv(bake_in_color);
00355       PT(GeomNode) geom_node = new GeomNode("background");
00356       transfer_geom(geom_node, NULL, geom_list, true);
00357       
00358       render.attach_new_node(geom_node);
00359     }
00360 
00361     StageList::const_iterator si;
00362     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
00363       const StageInfo &stage_info = (*si);
00364 
00365       make_texture_layer(render, stage_info, geom_list, 
00366                            min_uv, max_uv, force_use_geom, use_transparent_bg);
00367     }
00368 
00369     // Now modify the geometry to apply the new texture, instead of
00370     // the old multitexture.
00371     CPT(RenderAttrib) new_ta = DCAST(TextureAttrib, TextureAttrib::make())->
00372       add_on_stage(_target_stage, tex);
00373 
00374     GeomList::const_iterator gi;
00375     for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00376       const GeomInfo &geom_info = (*gi);
00377       
00378       CPT(RenderState) geom_state = 
00379         geom_info._geom_node->get_geom_state(geom_info._index);
00380       int override = geom_info._geom_net_state->get_override(TextureAttrib::get_class_slot());
00381       geom_state = geom_state->add_attrib(new_ta, override);
00382 
00383       if (bake_in_color) {
00384         // If we have baked the color into the texture, we have to be
00385         // sure to disable coloring on the new fragment.
00386         geom_state = geom_state->add_attrib(ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 1.0f)));
00387 
00388         // And we invent a ColorScaleAttrib to undo the effect of any
00389         // color scale we're getting from above.  This is not the same
00390         // thing as a ColorScaleAttrib::make_off(), since that would
00391         // prohibit any future changes to the color scale.
00392         const RenderAttrib *attrib = 
00393           geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
00394 
00395         if (attrib != (const RenderAttrib *)NULL) {
00396           geom_state = geom_state->add_attrib
00397             (attrib->invert_compose(ColorScaleAttrib::make_identity()));
00398         }
00399       }
00400 
00401       // Determine what tex matrix should be on the Geom.
00402       CPT(TransformState) tex_mat = TransformState::make_identity();
00403 
00404       const RenderAttrib *ra = geom_info._state->get_attrib(TexMatrixAttrib::get_class_slot());
00405       if (ra != (const RenderAttrib *)NULL) {
00406         // There is a texture matrix inherited from above; put an
00407         // inverse matrix on the Geom to compensate.
00408         const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, ra);
00409         CPT(TransformState) tex_mat = tma->get_transform(_target_stage);
00410       }
00411 
00412       tex_mat = tex_mat->compose(TransformState::make_pos_hpr_scale
00413                                  (LVecBase3f(uv_trans[0], uv_trans[1], 0.0f),
00414                                   LVecBase3f(0.0f, 0.0f, 0.0f),
00415                                   LVecBase3f(uv_scale[0], uv_scale[1], 1.0f)));
00416 
00417       if (tex_mat->is_identity()) {
00418         // There should be no texture matrix on the Geom.
00419         geom_state = geom_state->remove_attrib(TexMatrixAttrib::get_class_slot());
00420       } else {
00421         // The texture matrix should be as computed.
00422         CPT(RenderAttrib) new_tma = TexMatrixAttrib::make
00423           (_target_stage, tex_mat->invert_compose(TransformState::make_identity()));
00424         geom_state = geom_state->add_attrib(new_tma);
00425       }
00426 
00427 
00428       geom_info._geom_node->set_geom_state(geom_info._index, geom_state);
00429     }
00430   }
00431 
00432   // Now that we've copied all of the geometry and applied texture
00433   // matrices, flatten out those texture matrices where possible.
00434   GeomTransformer transformer;
00435 
00436   GeomNodeList::const_iterator gni;
00437   for (gni = _geom_node_list.begin(); gni != _geom_node_list.end(); ++gni) {
00438     const GeomNodeInfo &geom_node_info = (*gni);
00439     AccumulatedAttribs attribs;
00440     attribs._texture = 
00441       geom_node_info._state->get_attrib(TextureAttrib::get_class_slot());
00442     geom_node_info._geom_node->apply_attribs_to_vertices
00443       (attribs, SceneGraphReducer::TT_tex_matrix, transformer);
00444   }
00445 }
00446 
00447 ////////////////////////////////////////////////////////////////////
00448 //     Function: MultitexReducer::scan_geom_node
00449 //       Access: Private
00450 //  Description: Adds the Geoms in the indicated GeomNode to the
00451 //               internal database of multitexture elements.
00452 ////////////////////////////////////////////////////////////////////
00453 void MultitexReducer::
00454 scan_geom_node(GeomNode *node, const RenderState *state, 
00455                const TransformState *transform) {
00456   if (grutil_cat.is_debug()) {
00457     grutil_cat.debug()
00458       << "scan_geom_node(" << *node << ", " << *state << ", "
00459       << *transform << ")\n";
00460   }
00461 
00462   _geom_node_list.push_back(GeomNodeInfo(state, node));
00463 
00464   int num_geoms = node->get_num_geoms();
00465   for (int gi = 0; gi < num_geoms; gi++) {
00466     CPT(RenderState) geom_net_state = 
00467       state->compose(node->get_geom_state(gi));
00468 
00469     if (grutil_cat.is_debug()) {
00470       grutil_cat.debug()
00471         << "geom " << gi << " net_state =\n";
00472       geom_net_state->write(cerr, 2);
00473     }
00474 
00475     // Get out the net TextureAttrib and TexMatrixAttrib from the state.
00476     const RenderAttrib *attrib;
00477     const TextureAttrib *ta = NULL;
00478 
00479     attrib = geom_net_state->get_attrib(TextureAttrib::get_class_slot());
00480     if (attrib != (const RenderAttrib *)NULL) {
00481       ta = DCAST(TextureAttrib, attrib);
00482     }
00483 
00484     if (ta == (TextureAttrib *)NULL) {
00485       // No texture should be on the Geom.
00486       CPT(RenderState) geom_state = node->get_geom_state(gi);
00487       geom_state = geom_state->remove_attrib(TextureAttrib::get_class_slot());
00488       node->set_geom_state(gi, geom_state);
00489 
00490     } else if (ta->get_num_on_stages() < 2) {
00491       // Just a single texture on the Geom; we don't really need to do
00492       // anything to flatten the textures, then.  But we should ensure
00493       // that the correct TextureAttrib is applied to the Geom.
00494       int override = geom_net_state->get_override(TextureAttrib::get_class_slot());
00495       CPT(RenderState) geom_state = node->get_geom_state(gi);
00496       geom_state = geom_state->add_attrib(ta, override);
00497       node->set_geom_state(gi, geom_state);
00498 
00499     } else {
00500       // Ok, we have multitexture.  Record the Geom.
00501       CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
00502       attrib = geom_net_state->get_attrib(TexMatrixAttrib::get_class_slot());
00503       if (attrib != (const RenderAttrib *)NULL) {
00504         tma = DCAST(TexMatrixAttrib, attrib);
00505       }
00506       
00507       StageList stage_list;
00508       
00509       int num_stages = ta->get_num_on_stages();
00510       for (int si = 0; si < num_stages; si++) {
00511         TextureStage *stage = ta->get_on_stage(si);
00512         Texture *tex = ta->get_on_texture(stage);
00513         if (tex->get_x_size() != 0 && tex->get_y_size() != 0) {
00514           stage_list.push_back(StageInfo(stage, ta, tma));
00515           
00516         } else {
00517           grutil_cat.info()
00518             << "Ignoring invalid texture stage " << stage->get_name() << "\n";
00519         }
00520       }
00521       
00522       if (stage_list.size() >= 2) {
00523         record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
00524       }
00525     }
00526   }
00527 }
00528 
00529 ////////////////////////////////////////////////////////////////////
00530 //     Function: MultitexReducer::record_stage_list
00531 //       Access: Private
00532 //  Description: Adds the record of this one Geom and its associated
00533 //               StageList.
00534 ////////////////////////////////////////////////////////////////////
00535 void MultitexReducer::
00536 record_stage_list(const MultitexReducer::StageList &stage_list, 
00537                   const MultitexReducer::GeomInfo &geom_info) {
00538   if (grutil_cat.is_debug()) {
00539     grutil_cat.debug()
00540       << "record_stage_list for " << geom_info._geom_node->get_name() << " g" 
00541       << geom_info._index << ":\n";
00542     StageList::const_iterator si;
00543     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
00544       const StageInfo &stage_info = (*si);
00545       grutil_cat.debug(false)
00546         << "  " << *stage_info._stage << " " << *stage_info._tex
00547         << " " << *stage_info._tex_mat << "\n";
00548     }
00549   }
00550 
00551   _stages[stage_list].push_back(geom_info);
00552 }
00553 
00554 ////////////////////////////////////////////////////////////////////
00555 //     Function: MultitexReducer::choose_model_stage
00556 //       Access: Private
00557 //  Description: Chooses one of the TextureStages in the stage_list to
00558 //               serve as the model to determine the size and
00559 //               properties of the resulting texture.
00560 ////////////////////////////////////////////////////////////////////
00561 size_t MultitexReducer::
00562 choose_model_stage(const MultitexReducer::StageList &stage_list) const {
00563   for (size_t si = 0; si < stage_list.size(); si++) {
00564     const StageInfo &stage_info = stage_list[si];
00565     if (stage_info._stage == _target_stage) {
00566       // If we find the target stage, use that.
00567       return si;
00568     }
00569   }
00570 
00571   // If none of the stages are the target stage, use the bottom image.
00572   return 0;
00573 }
00574 
00575 ////////////////////////////////////////////////////////////////////
00576 //     Function: MultitexReducer::determine_uv_range
00577 //       Access: Private
00578 //  Description: Determines what the effective UV range for the
00579 //               indicated texture is across its geoms.  Returns true
00580 //               if any UV's are found, false otherwise.
00581 ////////////////////////////////////////////////////////////////////
00582 bool MultitexReducer::
00583 determine_uv_range(TexCoordf &min_uv, TexCoordf &max_uv,
00584                    const MultitexReducer::StageInfo &model_stage,
00585                    const MultitexReducer::GeomList &geom_list) const {
00586   const InternalName *model_name = model_stage._stage->get_texcoord_name();
00587   bool got_any = false;
00588 
00589   GeomList::const_iterator gi;
00590   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00591     const GeomInfo &geom_info = (*gi);
00592     
00593     PT(Geom) geom = 
00594       geom_info._geom_node->get_geom(geom_info._index)->make_copy();
00595 
00596     CPT(GeomVertexData) vdata = geom->get_vertex_data();
00597     CPT(GeomVertexFormat) format = vdata->get_format();
00598     if (format->has_column(model_name)) {
00599       GeomVertexReader texcoord(vdata, model_name);
00600 
00601       if (!texcoord.is_at_end()) {
00602         const LVecBase2f &uv = texcoord.get_data2f();
00603         if (!got_any) {
00604           min_uv = max_uv = uv;
00605           got_any = true;
00606           
00607         } else {
00608           min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
00609           max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
00610         }
00611 
00612         while (!texcoord.is_at_end()) {
00613           const LVecBase2f &uv = texcoord.get_data2f();
00614           min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
00615           max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
00616         }
00617       }
00618     }
00619   }
00620 
00621   if (!got_any) {
00622     min_uv.set(0.0f, 0.0f);
00623     max_uv.set(1.0f, 1.0f);
00624   }
00625 
00626   return got_any;
00627 }
00628 
00629 ////////////////////////////////////////////////////////////////////
00630 //     Function: MultitexReducer::get_uv_scale
00631 //       Access: Private
00632 //  Description: Chooses an appropriate transform to apply to all of
00633 //               the UV's on the generated texture, based on the
00634 //               coverage of the model stage.  If only a portion of
00635 //               the model stage is used, we scale the UV's up to zoom
00636 //               into that one portion; on the other hand, if the
00637 //               texture repeats many times, we scale the UV's down to
00638 //               to include all of the repeating image.
00639 ////////////////////////////////////////////////////////////////////
00640 void MultitexReducer::
00641 get_uv_scale(LVecBase2f &uv_scale, LVecBase2f &uv_trans,
00642              const TexCoordf &min_uv, const TexCoordf &max_uv) const {
00643   if (max_uv[0] != min_uv[0]) {
00644     uv_scale[0] = (max_uv[0] - min_uv[0]);
00645   } else {
00646     uv_scale[0] = 1.0f;
00647   }
00648 
00649   if (max_uv[1] != min_uv[1]) {
00650     uv_scale[1] = (max_uv[1] - min_uv[1]);
00651   } else {
00652     uv_scale[1] = 1.0f;
00653   }
00654 
00655   uv_trans[0] = (min_uv[0] + max_uv[0]) / 2.0f - uv_scale[0] * 0.5f;
00656   uv_trans[1] = (min_uv[1] + max_uv[1]) / 2.0f - uv_scale[1] * 0.5f;
00657 }
00658 
00659 ////////////////////////////////////////////////////////////////////
00660 //     Function: MultitexReducer::choose_texture_size
00661 //       Access: Private
00662 //  Description: Chooses an appropriate size to make the new texture,
00663 //               based on the size of the original model stage's
00664 //               texture, and the scale applied to the UV's.
00665 ////////////////////////////////////////////////////////////////////
00666 void MultitexReducer::
00667 choose_texture_size(int &x_size, int &y_size, 
00668                     const MultitexReducer::StageInfo &model_stage, 
00669                     const LVecBase2f &uv_scale,
00670                     GraphicsOutput *window) const {
00671   Texture *model_tex = model_stage._tex;
00672   
00673   // Start with the same size as the model texture.
00674   x_size = model_tex->get_x_size();
00675   y_size = model_tex->get_y_size();
00676 
00677   // But we might be looking at just a subset of that texture (|scale| <
00678   // 1) or a superset of the texture (|scale| > 1).  In this case, we
00679   // should adjust the pixel size accordingly, although we have to
00680   // keep it to a power of 2.
00681 
00682   LVecBase3f inherited_scale = model_stage._tex_mat->get_scale();
00683   
00684   float u_scale = cabs(inherited_scale[0]) * uv_scale[0];
00685   if (u_scale != 0.0f) {
00686     while (u_scale >= 2.0f) {
00687       x_size *= 2;
00688       u_scale *= 0.5f;
00689     }
00690     while (u_scale <= 0.5f && x_size > 0) {
00691       x_size /= 2;
00692       u_scale *= 2.0f;
00693     }
00694   }
00695 
00696   float v_scale = cabs(inherited_scale[1]) * uv_scale[1];
00697   if (v_scale != 0.0f) {
00698     while (v_scale >= 2.0f) {
00699       y_size *= 2;
00700       v_scale *= 0.5f;
00701     }
00702     while (v_scale <= 0.5f && y_size > 0) {
00703       y_size /= 2;
00704       v_scale *= 2.0f;
00705     }
00706   }
00707 
00708   if (x_size == 0 || y_size == 0) {
00709     grutil_cat.warning()
00710       << "Texture size " << model_tex->get_x_size() << " " 
00711       << model_tex->get_y_size() << " with scale " 
00712       << model_stage._tex_mat->get_scale() << ", reduced to size "
00713       << x_size << " " << y_size << "; constraining to 1 1.\n";
00714     x_size = 1;
00715     y_size = 1;
00716   }
00717 
00718   // Constrain the x_size and y_size to the max_texture_dimension.
00719   if (max_texture_dimension > 0) {
00720     x_size = min(x_size, (int)max_texture_dimension);
00721     y_size = min(y_size, (int)max_texture_dimension);
00722   }
00723 
00724   // Finally, make sure the new sizes fit within the window, so we can
00725   // use a parasite buffer.
00726   int win_x_size = window->get_x_size();
00727   if (win_x_size != 0 && x_size > win_x_size) {
00728     x_size /= 2;
00729     while (x_size > win_x_size) {
00730       x_size /= 2;
00731     }
00732   }
00733 
00734   int win_y_size = window->get_y_size();
00735   if (win_y_size != 0 && y_size > win_y_size) {
00736     y_size /= 2;
00737     while (y_size > win_y_size) {
00738       y_size /= 2;
00739     }
00740   }
00741 }
00742 
00743 ////////////////////////////////////////////////////////////////////
00744 //     Function: MultitexReducer::make_texture_layer
00745 //       Access: Private
00746 //  Description: Creates geometry to render the texture into the
00747 //               offscreen buffer using the same effects that were
00748 //               requested by its multitexture specification.
00749 ////////////////////////////////////////////////////////////////////
00750 void MultitexReducer::
00751 make_texture_layer(const NodePath &render, 
00752                    const MultitexReducer::StageInfo &stage_info, 
00753                    const MultitexReducer::GeomList &geom_list,
00754                    const TexCoordf &min_uv, const TexCoordf &max_uv,
00755                    bool force_use_geom, bool transparent_base) {
00756   CPT(RenderAttrib) cba;
00757 
00758   switch (stage_info._stage->get_mode()) {
00759   case TextureStage::M_normal:
00760   case TextureStage::M_normal_height:
00761   case TextureStage::M_glow:
00762   case TextureStage::M_gloss:
00763   case TextureStage::M_height:
00764   case TextureStage::M_selector:
00765     // Don't know what to do with these funny modes.  We should
00766     // probably raise an exception or something.  Fall through for
00767     // now.
00768 
00769   case TextureStage::M_modulate_glow:
00770   case TextureStage::M_modulate_gloss:
00771   case TextureStage::M_modulate:
00772     cba = ColorBlendAttrib::make
00773       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
00774        ColorBlendAttrib::O_zero);
00775     break;
00776 
00777   case TextureStage::M_decal:
00778     if(transparent_base) {
00779       cba = AlphaTestAttrib::make
00780         (AlphaTestAttrib::M_greater, 0.0f);
00781     } else {
00782       cba = ColorBlendAttrib::make
00783         (ColorBlendAttrib::M_add, ColorBlendAttrib::O_incoming_alpha,
00784          ColorBlendAttrib::O_one_minus_incoming_alpha);      
00785     }
00786     break;
00787 
00788   case TextureStage::M_blend:
00789     cba = ColorBlendAttrib::make
00790       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00791        ColorBlendAttrib::O_one_minus_incoming_color,
00792        stage_info._stage->get_color());
00793     break;
00794 
00795   case TextureStage::M_replace:
00796     cba = ColorBlendAttrib::make_off();
00797     break;
00798 
00799   case TextureStage::M_add:
00800     cba = ColorBlendAttrib::make
00801       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_one,
00802        ColorBlendAttrib::O_one);
00803     break;
00804 
00805   case TextureStage::M_combine:
00806     // We only support certain modes of M_combine.
00807     switch (stage_info._stage->get_combine_rgb_mode()) {
00808     case TextureStage::CM_modulate:
00809       {
00810         TextureStage::CombineSource source0 = stage_info._stage->get_combine_rgb_source0();
00811         TextureStage::CombineOperand operand0 = stage_info._stage->get_combine_rgb_operand0();
00812         TextureStage::CombineSource source1 = stage_info._stage->get_combine_rgb_source1();
00813         TextureStage::CombineOperand operand1 = stage_info._stage->get_combine_rgb_operand1();
00814         // Since modulate doesn't care about order, let's establish
00815         // the convention that the lowest-numbered source 
00816         // operand is in slot 0 (just for purposes of comparison).
00817         if (source1 < source0) {
00818           source0 = stage_info._stage->get_combine_rgb_source1();
00819           operand0 = stage_info._stage->get_combine_rgb_operand1();
00820           source1 = stage_info._stage->get_combine_rgb_source0();
00821           operand1 = stage_info._stage->get_combine_rgb_operand0();
00822         }
00823         
00824         if (source0 == TextureStage::CS_primary_color &&
00825             source1 == TextureStage::CS_previous) {
00826           // This is just a trick to re-apply the vertex (lighting)
00827           // color on the top of the texture stack.  We can ignore it,
00828           // since the flattened texture will do this anyway.
00829           return;
00830           
00831         } else if (source0 == TextureStage::CS_texture &&
00832                    source1 == TextureStage::CS_constant) {
00833           // Scaling the texture by a flat color.
00834           cba = ColorBlendAttrib::make
00835             (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00836              ColorBlendAttrib::O_zero, stage_info._stage->get_color());
00837           
00838         } else if (source0 == TextureStage::CS_texture &&
00839                    source1 == TextureStage::CS_previous) {
00840           // Just an ordinary modulate.
00841           cba = ColorBlendAttrib::make
00842             (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
00843              ColorBlendAttrib::O_zero);
00844           
00845         } else {
00846           // Some other kind of modulate; we don't support it.
00847           return;
00848         }
00849       }
00850       break;
00851 
00852     default:
00853       // Ignore this stage; we don't support it.
00854       return;
00855     }
00856     break;
00857 
00858   case TextureStage::M_blend_color_scale:
00859     // TODO: make a distinction between this and M_blend.
00860     cba = ColorBlendAttrib::make
00861       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00862        ColorBlendAttrib::O_one_minus_incoming_color,
00863        stage_info._stage->get_color());
00864     break;
00865   }
00866 
00867   NodePath geom;
00868 
00869   if (!force_use_geom && stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
00870     // If this TextureStage uses the target texcoords, we can just
00871     // generate a simple card the fills the entire buffer.
00872     CardMaker cm(stage_info._tex->get_name());
00873     cm.set_uv_range(min_uv, max_uv);
00874     cm.set_has_uvs(true);
00875     cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
00876     
00877     geom = render.attach_new_node(cm.generate());
00878 
00879   } else {
00880     // If this TextureStage uses some other texcoords (or if use_geom
00881     // is true), we have to generate geometry that maps the texcoords
00882     // to the target space.  This will work only for very simple cases
00883     // where the geometry is not too extensive and doesn't repeat over
00884     // the same UV's.
00885     PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
00886     transfer_geom(geom_node, stage_info._stage->get_texcoord_name(), 
00887                   geom_list, false);
00888     
00889     geom = render.attach_new_node(geom_node);
00890 
00891     geom.set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
00892   }
00893 
00894   if (!stage_info._tex_mat->is_identity()) {
00895     geom.set_tex_transform(TextureStage::get_default(), stage_info._tex_mat);
00896   }
00897 
00898   geom.set_texture(stage_info._tex);
00899   geom.node()->set_attrib(cba);
00900 }
00901 
00902 ////////////////////////////////////////////////////////////////////
00903 //     Function: MultitexReducer::transfer_geom
00904 //       Access: Private
00905 //  Description: Copy the vertices from the indicated geom_list,
00906 //               mapping the vertex coordinates so that the geometry
00907 //               will render the appropriate distortion on the texture
00908 //               to map UV's from the specified set of texture
00909 //               coordinates to the target set.
00910 ////////////////////////////////////////////////////////////////////
00911 void MultitexReducer::
00912 transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
00913               const MultitexReducer::GeomList &geom_list,
00914               bool preserve_color) {
00915   Thread *current_thread = Thread::get_current_thread();
00916   GeomList::const_iterator gi;
00917   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00918     const GeomInfo &geom_info = (*gi);
00919     const Geom *orig_geom = geom_info._geom_node->get_geom(geom_info._index);
00920 
00921     // Copy the Geom.  This actually performs just a pointer copy of
00922     // the original GeomVertexData and other associated structures.
00923     PT(Geom) geom = orig_geom->make_copy();
00924 
00925     // Ensure that any vertex animation has been applied.
00926     geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread));
00927 
00928     // Now get a modifiable pointer to the vertex data in the new
00929     // Geom.  This will actually perform a deep copy of the vertex
00930     // data.
00931     PT(GeomVertexData) vdata = geom->modify_vertex_data();
00932     vdata->set_usage_hint(Geom::UH_stream);
00933     
00934     if (vdata->has_column(_target_stage->get_texcoord_name())) {
00935       GeomVertexWriter vertex(vdata, InternalName::get_vertex(), current_thread);
00936       GeomVertexReader texcoord(vdata, _target_stage->get_texcoord_name(), current_thread);
00937       
00938       while (!texcoord.is_at_end()) {
00939         const LVecBase2f &tc = texcoord.get_data2f();
00940         vertex.set_data3f(tc[0], 0.0f, tc[1]);
00941       }
00942     }
00943     
00944     if (texcoord_name != (const InternalName *)NULL &&
00945         texcoord_name != InternalName::get_texcoord()) {
00946       // Copy the texture coordinates from the indicated name over
00947       // to the default name.
00948       const GeomVertexColumn *column = 
00949         vdata->get_format()->get_column(texcoord_name);
00950       if (column != (const GeomVertexColumn *)NULL) {
00951         vdata = vdata->replace_column
00952           (InternalName::get_texcoord(), column->get_num_components(),
00953            column->get_numeric_type(), column->get_contents());
00954         geom->set_vertex_data(vdata);
00955         
00956         GeomVertexReader from(vdata, texcoord_name, current_thread);
00957         GeomVertexWriter to(vdata, InternalName::get_texcoord(), current_thread);
00958         while (!from.is_at_end()) {
00959           to.add_data2f(from.get_data2f());
00960         }
00961       }
00962     }
00963     
00964     CPT(RenderState) geom_state = RenderState::make_empty();
00965     if (preserve_color) {
00966       // Be sure to preserve whatever colors are on the geom.
00967       const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
00968       if (ca != (const RenderAttrib *)NULL) {
00969         geom_state = geom_state->add_attrib(ca);
00970       }
00971       const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
00972       if (csa != (const RenderAttrib *)NULL) {
00973         geom_state = geom_state->add_attrib(csa);
00974       }
00975     }
00976     
00977     geom_node->add_geom(geom, geom_state);
00978   }
00979 }
00980 
00981 ////////////////////////////////////////////////////////////////////
00982 //     Function: MultitexReducer::scan_color
00983 //       Access: Private
00984 //  Description: Checks all the geoms in the list to see if they all
00985 //               use flat color, or if there is per-vertex color in
00986 //               use.
00987 //
00988 //               Assumption: num_colors = 0 on entry.  On exit,
00989 //               num_colors = 1 if there is exactly one color in use,
00990 //               or 2 if there is more than one color in use.  If
00991 //               num_colors = 1, then geom_color is filled in with the
00992 //               color in use.
00993 ////////////////////////////////////////////////////////////////////
00994 void MultitexReducer::
00995 scan_color(const MultitexReducer::GeomList &geom_list, Colorf &geom_color, 
00996            int &num_colors) const {
00997   GeomList::const_iterator gi;
00998   for (gi = geom_list.begin(); gi != geom_list.end() && num_colors < 2; ++gi) {
00999     const GeomInfo &geom_info = (*gi);
01000     
01001     Colorf flat_color;
01002     bool has_flat_color = false;
01003     bool has_vertex_color = false;
01004 
01005     Colorf color_scale(1.0f, 1.0f, 1.0f, 1.0f);
01006     const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
01007     if (csa != (const RenderAttrib *)NULL) {
01008       const ColorScaleAttrib *a = DCAST(ColorScaleAttrib, csa);
01009       if (a->has_scale()) {
01010         color_scale = a->get_scale();
01011       }
01012     }
01013 
01014     ColorAttrib::Type color_type = ColorAttrib::T_vertex;
01015     const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
01016     if (ca != (const RenderAttrib *)NULL) {
01017       color_type = DCAST(ColorAttrib, ca)->get_color_type();
01018     }
01019 
01020     if (color_type == ColorAttrib::T_flat) {
01021       // This geom has a flat color attrib, which overrides the vertices.
01022       flat_color = DCAST(ColorAttrib, ca)->get_color();
01023       has_flat_color = true;
01024 
01025     } else if (color_type == ColorAttrib::T_vertex) {
01026       // This geom gets its color from its vertices.
01027       const Geom *geom = geom_info._geom_node->get_geom(geom_info._index);
01028       if (geom->get_vertex_data()->has_column(InternalName::get_color())) {
01029         // This geom has per-vertex color.  Assume the colors in the
01030         // table are actually different from each other.
01031         has_vertex_color = true;
01032       }
01033     }
01034 
01035     if (has_vertex_color) {
01036       num_colors = 2;
01037 
01038     } else if (has_flat_color) {
01039       flat_color.set(flat_color[0] * color_scale[0],
01040                      flat_color[1] * color_scale[1],
01041                      flat_color[2] * color_scale[2],
01042                      flat_color[3] * color_scale[3]);
01043 
01044       if (num_colors == 0) {
01045         num_colors = 1;
01046         geom_color = flat_color;
01047 
01048       } else if (!flat_color.almost_equal(geom_color)) {
01049         // Too bad; there are multiple colors.
01050         num_colors = 2;
01051       }
01052     }
01053   }
01054 }
01055 
01056 ////////////////////////////////////////////////////////////////////
01057 //     Function: MultitexReducer::scan_decal
01058 //       Access: Private
01059 //  Description: Checks all the stages in the list to see if any of
01060 //               them apply a texture via M_decal.  Returns true if
01061 //               so, false otherwise.
01062 ////////////////////////////////////////////////////////////////////
01063 bool MultitexReducer::
01064 scan_decal(const MultitexReducer::StageList &stage_list) const {
01065   StageList::const_iterator si;
01066   for (si = stage_list.begin(); si != stage_list.end(); ++si) {
01067     const StageInfo &stage_info = (*si);
01068 
01069     if (stage_info._stage->get_mode() == TextureStage::M_decal) {
01070       return true;
01071     }
01072   }
01073 
01074   return false;
01075 }
01076 
01077 
01078 ////////////////////////////////////////////////////////////////////
01079 //     Function: MultitexReducer::StageInfo::Constructor
01080 //       Access: Public
01081 //  Description: 
01082 ////////////////////////////////////////////////////////////////////
01083 MultitexReducer::StageInfo::
01084 StageInfo(TextureStage *stage, const TextureAttrib *ta, 
01085           const TexMatrixAttrib *tma) :
01086   _stage(stage),
01087   _tex_mat(TransformState::make_identity())
01088 {
01089   _tex = ta->get_on_texture(_stage);
01090   if (tma->has_stage(stage)) {
01091     _tex_mat = tma->get_transform(stage);
01092   }
01093 }
01094 
 All Classes Functions Variables Enumerations