Panda3D
|
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