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     LTexCoord 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     LVecBase2 uv_scale;
00259     LVecBase2 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 
00280     // TODO: this no longer automatically deletes the buffer.  We need
00281     // to take care of this explicitly now.
00282     buffer->set_one_shot(true);
00283 
00284     Texture *tex = buffer->get_texture();
00285     tex->set_anisotropic_degree(aniso_degree);
00286     tex->set_minfilter(minfilter);
00287     tex->set_magfilter(magfilter);
00288 
00289     // Set up the offscreen buffer to render 0,0 to 1,1.  This will be
00290     // the whole texture, but nothing outside the texture.
00291     DisplayRegion *dr = buffer->make_display_region();
00292     PT(Camera) cam_node = new Camera("multitexCam");
00293     PT(Lens) lens = new OrthographicLens();
00294     lens->set_film_size(1.0f, 1.0f);
00295     lens->set_film_offset(0.5f, 0.5f);
00296     lens->set_near_far(-1000.0f, 1000.0f);
00297     lens->set_view_mat(LMatrix4(uv_scale[0], 0.0f, 0.0, 0.0f,
00298                                  0.0f, 1.0f, 0.0, 0.0f,
00299                                  0.0f, 0.0f, uv_scale[1], 0.0f,
00300                                  uv_trans[0], 0.0f, uv_trans[1], 1.0f));
00301     cam_node->set_lens(lens);
00302 
00303     // Create a root node for the buffer's scene graph, and set up
00304     // some appropriate properties for it.
00305     NodePath render("buffer");
00306     render.set_bin("unsorted", 0);
00307     render.set_depth_test(false);
00308     render.set_depth_write(false);
00309     render.set_two_sided(1);
00310 
00311     NodePath cam = render.attach_new_node(cam_node);
00312     dr->set_camera(cam);
00313 
00314     // If the geometry has vertex color and M_decal is in use, we must
00315     // render with use_geom in effect.  Otherwise we need not (and we
00316     // might prefer not to).
00317     bool force_use_geom = _use_geom;
00318     bool bake_in_color = _use_geom;
00319     LColor geom_color(1.0f, 1.0f, 1.0f, 1.0f);
00320     
00321     //override the base color in the transparent pass down case.
00322     if(use_transparent_bg)
00323       geom_color = LColor(0.0f,0.0f,0.0f,0.0f);
00324 
00325     if (!force_use_geom) {
00326       bool uses_decal = scan_decal(stage_list);
00327       if (uses_decal) {
00328         // If we have M_decal, we need to bake in the flat color
00329         // even if there is no vertex color.
00330         bake_in_color = true;
00331         
00332         /*
00333         int num_colors = 0;
00334         scan_color(geom_list, geom_color, num_colors);
00335 
00336         if (num_colors > 1) {
00337           // But if there is also vertex color, then we need to render
00338           // with the geometry.
00339           force_use_geom = true;
00340           }*/
00341       }
00342     }
00343 
00344     if (!force_use_geom) {
00345       // Put one plain white (or flat-colored) card in the background
00346       // for the first texture layer to apply onto.
00347       
00348       CardMaker cm("background");
00349       cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
00350       if (bake_in_color) {
00351         cm.set_color(geom_color);
00352       }
00353       render.attach_new_node(cm.generate());
00354 
00355     } else {
00356       // Put a vertex-colored model of the geometry in the background
00357       // for the first texture layer to apply only.
00358       nassertv(bake_in_color);
00359       PT(GeomNode) geom_node = new GeomNode("background");
00360       transfer_geom(geom_node, NULL, geom_list, true);
00361       
00362       render.attach_new_node(geom_node);
00363     }
00364 
00365     StageList::const_iterator si;
00366     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
00367       const StageInfo &stage_info = (*si);
00368 
00369       make_texture_layer(render, stage_info, geom_list, 
00370                            min_uv, max_uv, force_use_geom, use_transparent_bg);
00371     }
00372 
00373     // Now modify the geometry to apply the new texture, instead of
00374     // the old multitexture.
00375     CPT(RenderAttrib) new_ta = DCAST(TextureAttrib, TextureAttrib::make())->
00376       add_on_stage(_target_stage, tex);
00377 
00378     GeomList::const_iterator gi;
00379     for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00380       const GeomInfo &geom_info = (*gi);
00381       
00382       CPT(RenderState) geom_state = 
00383         geom_info._geom_node->get_geom_state(geom_info._index);
00384       int override = geom_info._geom_net_state->get_override(TextureAttrib::get_class_slot());
00385       geom_state = geom_state->add_attrib(new_ta, override);
00386 
00387       if (bake_in_color) {
00388         // If we have baked the color into the texture, we have to be
00389         // sure to disable coloring on the new fragment.
00390         geom_state = geom_state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)));
00391 
00392         // And we invent a ColorScaleAttrib to undo the effect of any
00393         // color scale we're getting from above.  This is not the same
00394         // thing as a ColorScaleAttrib::make_off(), since that would
00395         // prohibit any future changes to the color scale.
00396         const RenderAttrib *attrib = 
00397           geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
00398 
00399         if (attrib != (const RenderAttrib *)NULL) {
00400           geom_state = geom_state->add_attrib
00401             (attrib->invert_compose(ColorScaleAttrib::make_identity()));
00402         }
00403       }
00404 
00405       // Determine what tex matrix should be on the Geom.
00406       CPT(TransformState) tex_mat = TransformState::make_identity();
00407 
00408       const RenderAttrib *ra = geom_info._state->get_attrib(TexMatrixAttrib::get_class_slot());
00409       if (ra != (const RenderAttrib *)NULL) {
00410         // There is a texture matrix inherited from above; put an
00411         // inverse matrix on the Geom to compensate.
00412         const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, ra);
00413         CPT(TransformState) tex_mat = tma->get_transform(_target_stage);
00414       }
00415 
00416       tex_mat = tex_mat->compose(TransformState::make_pos_hpr_scale
00417                                  (LVecBase3(uv_trans[0], uv_trans[1], 0.0f),
00418                                   LVecBase3(0.0f, 0.0f, 0.0f),
00419                                   LVecBase3(uv_scale[0], uv_scale[1], 1.0f)));
00420 
00421       if (tex_mat->is_identity()) {
00422         // There should be no texture matrix on the Geom.
00423         geom_state = geom_state->remove_attrib(TexMatrixAttrib::get_class_slot());
00424       } else {
00425         // The texture matrix should be as computed.
00426         CPT(RenderAttrib) new_tma = TexMatrixAttrib::make
00427           (_target_stage, tex_mat->invert_compose(TransformState::make_identity()));
00428         geom_state = geom_state->add_attrib(new_tma);
00429       }
00430 
00431 
00432       geom_info._geom_node->set_geom_state(geom_info._index, geom_state);
00433     }
00434   }
00435 
00436   // Now that we've copied all of the geometry and applied texture
00437   // matrices, flatten out those texture matrices where possible.
00438   GeomTransformer transformer;
00439 
00440   GeomNodeList::const_iterator gni;
00441   for (gni = _geom_node_list.begin(); gni != _geom_node_list.end(); ++gni) {
00442     const GeomNodeInfo &geom_node_info = (*gni);
00443     AccumulatedAttribs attribs;
00444     attribs._texture = 
00445       geom_node_info._state->get_attrib(TextureAttrib::get_class_slot());
00446     geom_node_info._geom_node->apply_attribs_to_vertices
00447       (attribs, SceneGraphReducer::TT_tex_matrix, transformer);
00448   }
00449 }
00450 
00451 ////////////////////////////////////////////////////////////////////
00452 //     Function: MultitexReducer::scan_geom_node
00453 //       Access: Private
00454 //  Description: Adds the Geoms in the indicated GeomNode to the
00455 //               internal database of multitexture elements.
00456 ////////////////////////////////////////////////////////////////////
00457 void MultitexReducer::
00458 scan_geom_node(GeomNode *node, const RenderState *state, 
00459                const TransformState *transform) {
00460   if (grutil_cat.is_debug()) {
00461     grutil_cat.debug()
00462       << "scan_geom_node(" << *node << ", " << *state << ", "
00463       << *transform << ")\n";
00464   }
00465 
00466   _geom_node_list.push_back(GeomNodeInfo(state, node));
00467 
00468   int num_geoms = node->get_num_geoms();
00469   for (int gi = 0; gi < num_geoms; gi++) {
00470     CPT(RenderState) geom_net_state = 
00471       state->compose(node->get_geom_state(gi));
00472 
00473     if (grutil_cat.is_debug()) {
00474       grutil_cat.debug()
00475         << "geom " << gi << " net_state =\n";
00476       geom_net_state->write(cerr, 2);
00477     }
00478 
00479     // Get out the net TextureAttrib and TexMatrixAttrib from the state.
00480     const RenderAttrib *attrib;
00481     const TextureAttrib *ta = NULL;
00482 
00483     attrib = geom_net_state->get_attrib(TextureAttrib::get_class_slot());
00484     if (attrib != (const RenderAttrib *)NULL) {
00485       ta = DCAST(TextureAttrib, attrib);
00486     }
00487 
00488     if (ta == (TextureAttrib *)NULL) {
00489       // No texture should be on the Geom.
00490       CPT(RenderState) geom_state = node->get_geom_state(gi);
00491       geom_state = geom_state->remove_attrib(TextureAttrib::get_class_slot());
00492       node->set_geom_state(gi, geom_state);
00493 
00494     } else if (ta->get_num_on_stages() < 2) {
00495       // Just a single texture on the Geom; we don't really need to do
00496       // anything to flatten the textures, then.  But we should ensure
00497       // that the correct TextureAttrib is applied to the Geom.
00498       int override = geom_net_state->get_override(TextureAttrib::get_class_slot());
00499       CPT(RenderState) geom_state = node->get_geom_state(gi);
00500       geom_state = geom_state->add_attrib(ta, override);
00501       node->set_geom_state(gi, geom_state);
00502 
00503     } else {
00504       // Ok, we have multitexture.  Record the Geom.
00505       CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
00506       attrib = geom_net_state->get_attrib(TexMatrixAttrib::get_class_slot());
00507       if (attrib != (const RenderAttrib *)NULL) {
00508         tma = DCAST(TexMatrixAttrib, attrib);
00509       }
00510       
00511       StageList stage_list;
00512       
00513       int num_stages = ta->get_num_on_stages();
00514       for (int si = 0; si < num_stages; si++) {
00515         TextureStage *stage = ta->get_on_stage(si);
00516         Texture *tex = ta->get_on_texture(stage);
00517         if (tex->get_x_size() != 0 && tex->get_y_size() != 0) {
00518           stage_list.push_back(StageInfo(stage, ta, tma));
00519           
00520         } else {
00521           grutil_cat.info()
00522             << "Ignoring invalid texture stage " << stage->get_name() << "\n";
00523         }
00524       }
00525       
00526       if (stage_list.size() >= 2) {
00527         record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
00528       }
00529     }
00530   }
00531 }
00532 
00533 ////////////////////////////////////////////////////////////////////
00534 //     Function: MultitexReducer::record_stage_list
00535 //       Access: Private
00536 //  Description: Adds the record of this one Geom and its associated
00537 //               StageList.
00538 ////////////////////////////////////////////////////////////////////
00539 void MultitexReducer::
00540 record_stage_list(const MultitexReducer::StageList &stage_list, 
00541                   const MultitexReducer::GeomInfo &geom_info) {
00542   if (grutil_cat.is_debug()) {
00543     grutil_cat.debug()
00544       << "record_stage_list for " << geom_info._geom_node->get_name() << " g" 
00545       << geom_info._index << ":\n";
00546     StageList::const_iterator si;
00547     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
00548       const StageInfo &stage_info = (*si);
00549       grutil_cat.debug(false)
00550         << "  " << *stage_info._stage << " " << *stage_info._tex
00551         << " " << *stage_info._tex_mat << "\n";
00552     }
00553   }
00554 
00555   _stages[stage_list].push_back(geom_info);
00556 }
00557 
00558 ////////////////////////////////////////////////////////////////////
00559 //     Function: MultitexReducer::choose_model_stage
00560 //       Access: Private
00561 //  Description: Chooses one of the TextureStages in the stage_list to
00562 //               serve as the model to determine the size and
00563 //               properties of the resulting texture.
00564 ////////////////////////////////////////////////////////////////////
00565 size_t MultitexReducer::
00566 choose_model_stage(const MultitexReducer::StageList &stage_list) const {
00567   for (size_t si = 0; si < stage_list.size(); si++) {
00568     const StageInfo &stage_info = stage_list[si];
00569     if (stage_info._stage == _target_stage) {
00570       // If we find the target stage, use that.
00571       return si;
00572     }
00573   }
00574 
00575   // If none of the stages are the target stage, use the bottom image.
00576   return 0;
00577 }
00578 
00579 ////////////////////////////////////////////////////////////////////
00580 //     Function: MultitexReducer::determine_uv_range
00581 //       Access: Private
00582 //  Description: Determines what the effective UV range for the
00583 //               indicated texture is across its geoms.  Returns true
00584 //               if any UV's are found, false otherwise.
00585 ////////////////////////////////////////////////////////////////////
00586 bool MultitexReducer::
00587 determine_uv_range(LTexCoord &min_uv, LTexCoord &max_uv,
00588                    const MultitexReducer::StageInfo &model_stage,
00589                    const MultitexReducer::GeomList &geom_list) const {
00590   const InternalName *model_name = model_stage._stage->get_texcoord_name();
00591   bool got_any = false;
00592 
00593   GeomList::const_iterator gi;
00594   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00595     const GeomInfo &geom_info = (*gi);
00596     
00597     PT(Geom) geom = 
00598       geom_info._geom_node->get_geom(geom_info._index)->make_copy();
00599 
00600     CPT(GeomVertexData) vdata = geom->get_vertex_data();
00601     CPT(GeomVertexFormat) format = vdata->get_format();
00602     if (format->has_column(model_name)) {
00603       GeomVertexReader texcoord(vdata, model_name);
00604 
00605       if (!texcoord.is_at_end()) {
00606         const LVecBase2 &uv = texcoord.get_data2();
00607         if (!got_any) {
00608           min_uv = max_uv = uv;
00609           got_any = true;
00610           
00611         } else {
00612           min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
00613           max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
00614         }
00615 
00616         while (!texcoord.is_at_end()) {
00617           const LVecBase2 &uv = texcoord.get_data2();
00618           min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
00619           max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
00620         }
00621       }
00622     }
00623   }
00624 
00625   if (!got_any) {
00626     min_uv.set(0.0f, 0.0f);
00627     max_uv.set(1.0f, 1.0f);
00628   }
00629 
00630   return got_any;
00631 }
00632 
00633 ////////////////////////////////////////////////////////////////////
00634 //     Function: MultitexReducer::get_uv_scale
00635 //       Access: Private
00636 //  Description: Chooses an appropriate transform to apply to all of
00637 //               the UV's on the generated texture, based on the
00638 //               coverage of the model stage.  If only a portion of
00639 //               the model stage is used, we scale the UV's up to zoom
00640 //               into that one portion; on the other hand, if the
00641 //               texture repeats many times, we scale the UV's down to
00642 //               to include all of the repeating image.
00643 ////////////////////////////////////////////////////////////////////
00644 void MultitexReducer::
00645 get_uv_scale(LVecBase2 &uv_scale, LVecBase2 &uv_trans,
00646              const LTexCoord &min_uv, const LTexCoord &max_uv) const {
00647   if (max_uv[0] != min_uv[0]) {
00648     uv_scale[0] = (max_uv[0] - min_uv[0]);
00649   } else {
00650     uv_scale[0] = 1.0f;
00651   }
00652 
00653   if (max_uv[1] != min_uv[1]) {
00654     uv_scale[1] = (max_uv[1] - min_uv[1]);
00655   } else {
00656     uv_scale[1] = 1.0f;
00657   }
00658 
00659   uv_trans[0] = (min_uv[0] + max_uv[0]) / 2.0f - uv_scale[0] * 0.5f;
00660   uv_trans[1] = (min_uv[1] + max_uv[1]) / 2.0f - uv_scale[1] * 0.5f;
00661 }
00662 
00663 ////////////////////////////////////////////////////////////////////
00664 //     Function: MultitexReducer::choose_texture_size
00665 //       Access: Private
00666 //  Description: Chooses an appropriate size to make the new texture,
00667 //               based on the size of the original model stage's
00668 //               texture, and the scale applied to the UV's.
00669 ////////////////////////////////////////////////////////////////////
00670 void MultitexReducer::
00671 choose_texture_size(int &x_size, int &y_size, 
00672                     const MultitexReducer::StageInfo &model_stage, 
00673                     const LVecBase2 &uv_scale,
00674                     GraphicsOutput *window) const {
00675   Texture *model_tex = model_stage._tex;
00676   
00677   // Start with the same size as the model texture.
00678   x_size = model_tex->get_x_size();
00679   y_size = model_tex->get_y_size();
00680 
00681   // But we might be looking at just a subset of that texture (|scale| <
00682   // 1) or a superset of the texture (|scale| > 1).  In this case, we
00683   // should adjust the pixel size accordingly, although we have to
00684   // keep it to a power of 2.
00685 
00686   LVecBase3 inherited_scale = model_stage._tex_mat->get_scale();
00687   
00688   PN_stdfloat u_scale = cabs(inherited_scale[0]) * uv_scale[0];
00689   if (u_scale != 0.0f) {
00690     while (u_scale >= 2.0f) {
00691       x_size *= 2;
00692       u_scale *= 0.5f;
00693     }
00694     while (u_scale <= 0.5f && x_size > 0) {
00695       x_size /= 2;
00696       u_scale *= 2.0f;
00697     }
00698   }
00699 
00700   PN_stdfloat v_scale = cabs(inherited_scale[1]) * uv_scale[1];
00701   if (v_scale != 0.0f) {
00702     while (v_scale >= 2.0f) {
00703       y_size *= 2;
00704       v_scale *= 0.5f;
00705     }
00706     while (v_scale <= 0.5f && y_size > 0) {
00707       y_size /= 2;
00708       v_scale *= 2.0f;
00709     }
00710   }
00711 
00712   if (x_size == 0 || y_size == 0) {
00713     grutil_cat.warning()
00714       << "Texture size " << model_tex->get_x_size() << " " 
00715       << model_tex->get_y_size() << " with scale " 
00716       << model_stage._tex_mat->get_scale() << ", reduced to size "
00717       << x_size << " " << y_size << "; constraining to 1 1.\n";
00718     x_size = 1;
00719     y_size = 1;
00720   }
00721 
00722   // Constrain the x_size and y_size to the max_texture_dimension.
00723   if (max_texture_dimension > 0) {
00724     x_size = min(x_size, (int)max_texture_dimension);
00725     y_size = min(y_size, (int)max_texture_dimension);
00726   }
00727 
00728   // Finally, make sure the new sizes fit within the window, so we can
00729   // use a parasite buffer.
00730   int win_x_size = window->get_x_size();
00731   if (win_x_size != 0 && x_size > win_x_size) {
00732     x_size /= 2;
00733     while (x_size > win_x_size) {
00734       x_size /= 2;
00735     }
00736   }
00737 
00738   int win_y_size = window->get_y_size();
00739   if (win_y_size != 0 && y_size > win_y_size) {
00740     y_size /= 2;
00741     while (y_size > win_y_size) {
00742       y_size /= 2;
00743     }
00744   }
00745 }
00746 
00747 ////////////////////////////////////////////////////////////////////
00748 //     Function: MultitexReducer::make_texture_layer
00749 //       Access: Private
00750 //  Description: Creates geometry to render the texture into the
00751 //               offscreen buffer using the same effects that were
00752 //               requested by its multitexture specification.
00753 ////////////////////////////////////////////////////////////////////
00754 void MultitexReducer::
00755 make_texture_layer(const NodePath &render, 
00756                    const MultitexReducer::StageInfo &stage_info, 
00757                    const MultitexReducer::GeomList &geom_list,
00758                    const LTexCoord &min_uv, const LTexCoord &max_uv,
00759                    bool force_use_geom, bool transparent_base) {
00760   CPT(RenderAttrib) cba;
00761 
00762   switch (stage_info._stage->get_mode()) {
00763   case TextureStage::M_normal:
00764   case TextureStage::M_normal_height:
00765   case TextureStage::M_glow:
00766   case TextureStage::M_gloss:
00767   case TextureStage::M_height:
00768   case TextureStage::M_selector:
00769   case TextureStage::M_normal_gloss:
00770     // Don't know what to do with these funny modes.  We should
00771     // probably raise an exception or something.  Fall through for
00772     // now.
00773 
00774   case TextureStage::M_modulate_glow:
00775   case TextureStage::M_modulate_gloss:
00776   case TextureStage::M_modulate:
00777     cba = ColorBlendAttrib::make
00778       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
00779        ColorBlendAttrib::O_zero);
00780     break;
00781 
00782   case TextureStage::M_decal:
00783     if(transparent_base) {
00784       cba = AlphaTestAttrib::make
00785         (AlphaTestAttrib::M_greater, 0.0f);
00786     } else {
00787       cba = ColorBlendAttrib::make
00788         (ColorBlendAttrib::M_add, ColorBlendAttrib::O_incoming_alpha,
00789          ColorBlendAttrib::O_one_minus_incoming_alpha);      
00790     }
00791     break;
00792 
00793   case TextureStage::M_blend:
00794     cba = ColorBlendAttrib::make
00795       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00796        ColorBlendAttrib::O_one_minus_incoming_color,
00797        stage_info._stage->get_color());
00798     break;
00799 
00800   case TextureStage::M_replace:
00801     cba = ColorBlendAttrib::make_off();
00802     break;
00803 
00804   case TextureStage::M_add:
00805     cba = ColorBlendAttrib::make
00806       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_one,
00807        ColorBlendAttrib::O_one);
00808     break;
00809 
00810   case TextureStage::M_combine:
00811     // We only support certain modes of M_combine.
00812     switch (stage_info._stage->get_combine_rgb_mode()) {
00813     case TextureStage::CM_modulate:
00814       {
00815         TextureStage::CombineSource source0 = stage_info._stage->get_combine_rgb_source0();
00816         TextureStage::CombineOperand operand0 = stage_info._stage->get_combine_rgb_operand0();
00817         TextureStage::CombineSource source1 = stage_info._stage->get_combine_rgb_source1();
00818         TextureStage::CombineOperand operand1 = stage_info._stage->get_combine_rgb_operand1();
00819         // Since modulate doesn't care about order, let's establish
00820         // the convention that the lowest-numbered source 
00821         // operand is in slot 0 (just for purposes of comparison).
00822         if (source1 < source0) {
00823           source0 = stage_info._stage->get_combine_rgb_source1();
00824           operand0 = stage_info._stage->get_combine_rgb_operand1();
00825           source1 = stage_info._stage->get_combine_rgb_source0();
00826           operand1 = stage_info._stage->get_combine_rgb_operand0();
00827         }
00828         
00829         if (source0 == TextureStage::CS_primary_color &&
00830             source1 == TextureStage::CS_previous) {
00831           // This is just a trick to re-apply the vertex (lighting)
00832           // color on the top of the texture stack.  We can ignore it,
00833           // since the flattened texture will do this anyway.
00834           return;
00835           
00836         } else if (source0 == TextureStage::CS_texture &&
00837                    source1 == TextureStage::CS_constant) {
00838           // Scaling the texture by a flat color.
00839           cba = ColorBlendAttrib::make
00840             (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00841              ColorBlendAttrib::O_zero, stage_info._stage->get_color());
00842           
00843         } else if (source0 == TextureStage::CS_texture &&
00844                    source1 == TextureStage::CS_previous) {
00845           // Just an ordinary modulate.
00846           cba = ColorBlendAttrib::make
00847             (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
00848              ColorBlendAttrib::O_zero);
00849           
00850         } else {
00851           // Some other kind of modulate; we don't support it.
00852           return;
00853         }
00854       }
00855       break;
00856 
00857     default:
00858       // Ignore this stage; we don't support it.
00859       return;
00860     }
00861     break;
00862 
00863   case TextureStage::M_blend_color_scale:
00864     // TODO: make a distinction between this and M_blend.
00865     cba = ColorBlendAttrib::make
00866       (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
00867        ColorBlendAttrib::O_one_minus_incoming_color,
00868        stage_info._stage->get_color());
00869     break;
00870   }
00871 
00872   NodePath geom;
00873 
00874   if (!force_use_geom && stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
00875     // If this TextureStage uses the target texcoords, we can just
00876     // generate a simple card the fills the entire buffer.
00877     CardMaker cm(stage_info._tex->get_name());
00878     cm.set_uv_range(min_uv, max_uv);
00879     cm.set_has_uvs(true);
00880     cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
00881     
00882     geom = render.attach_new_node(cm.generate());
00883 
00884   } else {
00885     // If this TextureStage uses some other texcoords (or if use_geom
00886     // is true), we have to generate geometry that maps the texcoords
00887     // to the target space.  This will work only for very simple cases
00888     // where the geometry is not too extensive and doesn't repeat over
00889     // the same UV's.
00890     PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
00891     transfer_geom(geom_node, stage_info._stage->get_texcoord_name(), 
00892                   geom_list, false);
00893     
00894     geom = render.attach_new_node(geom_node);
00895 
00896     geom.set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
00897   }
00898 
00899   if (!stage_info._tex_mat->is_identity()) {
00900     geom.set_tex_transform(TextureStage::get_default(), stage_info._tex_mat);
00901   }
00902 
00903   geom.set_texture(stage_info._tex);
00904   geom.node()->set_attrib(cba);
00905 }
00906 
00907 ////////////////////////////////////////////////////////////////////
00908 //     Function: MultitexReducer::transfer_geom
00909 //       Access: Private
00910 //  Description: Copy the vertices from the indicated geom_list,
00911 //               mapping the vertex coordinates so that the geometry
00912 //               will render the appropriate distortion on the texture
00913 //               to map UV's from the specified set of texture
00914 //               coordinates to the target set.
00915 ////////////////////////////////////////////////////////////////////
00916 void MultitexReducer::
00917 transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
00918               const MultitexReducer::GeomList &geom_list,
00919               bool preserve_color) {
00920   Thread *current_thread = Thread::get_current_thread();
00921   GeomList::const_iterator gi;
00922   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
00923     const GeomInfo &geom_info = (*gi);
00924     const Geom *orig_geom = geom_info._geom_node->get_geom(geom_info._index);
00925 
00926     // Copy the Geom.  This actually performs just a pointer copy of
00927     // the original GeomVertexData and other associated structures.
00928     PT(Geom) geom = orig_geom->make_copy();
00929 
00930     // Ensure that any vertex animation has been applied.
00931     geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread));
00932 
00933     // Now get a modifiable pointer to the vertex data in the new
00934     // Geom.  This will actually perform a deep copy of the vertex
00935     // data.
00936     PT(GeomVertexData) vdata = geom->modify_vertex_data();
00937     vdata->set_usage_hint(Geom::UH_stream);
00938     
00939     if (vdata->has_column(_target_stage->get_texcoord_name())) {
00940       GeomVertexWriter vertex(vdata, InternalName::get_vertex(), current_thread);
00941       GeomVertexReader texcoord(vdata, _target_stage->get_texcoord_name(), current_thread);
00942       
00943       while (!texcoord.is_at_end()) {
00944         const LVecBase2 &tc = texcoord.get_data2();
00945         vertex.set_data3(tc[0], 0.0f, tc[1]);
00946       }
00947     }
00948     
00949     if (texcoord_name != (const InternalName *)NULL &&
00950         texcoord_name != InternalName::get_texcoord()) {
00951       // Copy the texture coordinates from the indicated name over
00952       // to the default name.
00953       const GeomVertexColumn *column = 
00954         vdata->get_format()->get_column(texcoord_name);
00955       if (column != (const GeomVertexColumn *)NULL) {
00956         vdata = vdata->replace_column
00957           (InternalName::get_texcoord(), column->get_num_components(),
00958            column->get_numeric_type(), column->get_contents());
00959         geom->set_vertex_data(vdata);
00960         
00961         GeomVertexReader from(vdata, texcoord_name, current_thread);
00962         GeomVertexWriter to(vdata, InternalName::get_texcoord(), current_thread);
00963         while (!from.is_at_end()) {
00964           to.add_data2(from.get_data2());
00965         }
00966       }
00967     }
00968     
00969     CPT(RenderState) geom_state = RenderState::make_empty();
00970     if (preserve_color) {
00971       // Be sure to preserve whatever colors are on the geom.
00972       const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
00973       if (ca != (const RenderAttrib *)NULL) {
00974         geom_state = geom_state->add_attrib(ca);
00975       }
00976       const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
00977       if (csa != (const RenderAttrib *)NULL) {
00978         geom_state = geom_state->add_attrib(csa);
00979       }
00980     }
00981     
00982     geom_node->add_geom(geom, geom_state);
00983   }
00984 }
00985 
00986 ////////////////////////////////////////////////////////////////////
00987 //     Function: MultitexReducer::scan_color
00988 //       Access: Private
00989 //  Description: Checks all the geoms in the list to see if they all
00990 //               use flat color, or if there is per-vertex color in
00991 //               use.
00992 //
00993 //               Assumption: num_colors = 0 on entry.  On exit,
00994 //               num_colors = 1 if there is exactly one color in use,
00995 //               or 2 if there is more than one color in use.  If
00996 //               num_colors = 1, then geom_color is filled in with the
00997 //               color in use.
00998 ////////////////////////////////////////////////////////////////////
00999 void MultitexReducer::
01000 scan_color(const MultitexReducer::GeomList &geom_list, LColor &geom_color, 
01001            int &num_colors) const {
01002   GeomList::const_iterator gi;
01003   for (gi = geom_list.begin(); gi != geom_list.end() && num_colors < 2; ++gi) {
01004     const GeomInfo &geom_info = (*gi);
01005     
01006     LColor flat_color;
01007     bool has_flat_color = false;
01008     bool has_vertex_color = false;
01009 
01010     LColor color_scale(1.0f, 1.0f, 1.0f, 1.0f);
01011     const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
01012     if (csa != (const RenderAttrib *)NULL) {
01013       const ColorScaleAttrib *a = DCAST(ColorScaleAttrib, csa);
01014       if (a->has_scale()) {
01015         color_scale = a->get_scale();
01016       }
01017     }
01018 
01019     ColorAttrib::Type color_type = ColorAttrib::T_vertex;
01020     const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
01021     if (ca != (const RenderAttrib *)NULL) {
01022       color_type = DCAST(ColorAttrib, ca)->get_color_type();
01023     }
01024 
01025     if (color_type == ColorAttrib::T_flat) {
01026       // This geom has a flat color attrib, which overrides the vertices.
01027       flat_color = DCAST(ColorAttrib, ca)->get_color();
01028       has_flat_color = true;
01029 
01030     } else if (color_type == ColorAttrib::T_vertex) {
01031       // This geom gets its color from its vertices.
01032       const Geom *geom = geom_info._geom_node->get_geom(geom_info._index);
01033       if (geom->get_vertex_data()->has_column(InternalName::get_color())) {
01034         // This geom has per-vertex color.  Assume the colors in the
01035         // table are actually different from each other.
01036         has_vertex_color = true;
01037       }
01038     }
01039 
01040     if (has_vertex_color) {
01041       num_colors = 2;
01042 
01043     } else if (has_flat_color) {
01044       flat_color.set(flat_color[0] * color_scale[0],
01045                      flat_color[1] * color_scale[1],
01046                      flat_color[2] * color_scale[2],
01047                      flat_color[3] * color_scale[3]);
01048 
01049       if (num_colors == 0) {
01050         num_colors = 1;
01051         geom_color = flat_color;
01052 
01053       } else if (!flat_color.almost_equal(geom_color)) {
01054         // Too bad; there are multiple colors.
01055         num_colors = 2;
01056       }
01057     }
01058   }
01059 }
01060 
01061 ////////////////////////////////////////////////////////////////////
01062 //     Function: MultitexReducer::scan_decal
01063 //       Access: Private
01064 //  Description: Checks all the stages in the list to see if any of
01065 //               them apply a texture via M_decal.  Returns true if
01066 //               so, false otherwise.
01067 ////////////////////////////////////////////////////////////////////
01068 bool MultitexReducer::
01069 scan_decal(const MultitexReducer::StageList &stage_list) const {
01070   StageList::const_iterator si;
01071   for (si = stage_list.begin(); si != stage_list.end(); ++si) {
01072     const StageInfo &stage_info = (*si);
01073 
01074     if (stage_info._stage->get_mode() == TextureStage::M_decal) {
01075       return true;
01076     }
01077   }
01078 
01079   return false;
01080 }
01081 
01082 
01083 ////////////////////////////////////////////////////////////////////
01084 //     Function: MultitexReducer::StageInfo::Constructor
01085 //       Access: Public
01086 //  Description: 
01087 ////////////////////////////////////////////////////////////////////
01088 MultitexReducer::StageInfo::
01089 StageInfo(TextureStage *stage, const TextureAttrib *ta, 
01090           const TexMatrixAttrib *tma) :
01091   _stage(stage),
01092   _tex_mat(TransformState::make_identity())
01093 {
01094   _tex = ta->get_on_texture(_stage);
01095   if (tma->has_stage(stage)) {
01096     _tex_mat = tma->get_transform(stage);
01097   }
01098 }
01099 
 All Classes Functions Variables Enumerations