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