Panda3D
|
00001 // Filename: textureImage.cxx 00002 // Created by: drose (29Nov00) 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 "textureImage.h" 00016 #include "sourceTextureImage.h" 00017 #include "destTextureImage.h" 00018 #include "eggFile.h" 00019 #include "paletteGroup.h" 00020 #include "paletteImage.h" 00021 #include "texturePlacement.h" 00022 #include "filenameUnifier.h" 00023 #include "string_utils.h" 00024 #include "indent.h" 00025 #include "datagram.h" 00026 #include "datagramIterator.h" 00027 #include "bamReader.h" 00028 #include "bamWriter.h" 00029 #include "pnmFileType.h" 00030 #include "indirectCompareNames.h" 00031 #include "pvector.h" 00032 00033 TypeHandle TextureImage::_type_handle; 00034 00035 //////////////////////////////////////////////////////////////////// 00036 // Function: TextureImage::Constructor 00037 // Access: Public 00038 // Description: 00039 //////////////////////////////////////////////////////////////////// 00040 TextureImage:: 00041 TextureImage() { 00042 _preferred_source = (SourceTextureImage *)NULL; 00043 _read_source_image = false; 00044 _allow_release_source_image = true; 00045 _is_surprise = true; 00046 _ever_read_image = false; 00047 _forced_grayscale = false; 00048 _alpha_bits = 0; 00049 _mid_pixel_ratio = 0.0; 00050 _is_cutout = false; 00051 _alpha_mode = EggRenderMode::AM_unspecified; 00052 _txa_wrap_u = EggTexture::WM_unspecified; 00053 _txa_wrap_v = EggTexture::WM_unspecified; 00054 _texture_named = false; 00055 _got_txa_file = false; 00056 } 00057 00058 //////////////////////////////////////////////////////////////////// 00059 // Function: TextureImage::note_egg_file 00060 // Access: Public 00061 // Description: Records that a particular egg file references this 00062 // texture. This is essential to know when deciding how 00063 // to assign the TextureImage to the various 00064 // PaletteGroups. 00065 //////////////////////////////////////////////////////////////////// 00066 void TextureImage:: 00067 note_egg_file(EggFile *egg_file) { 00068 nassertv(!egg_file->get_complete_groups().empty()); 00069 _egg_files.insert(egg_file); 00070 } 00071 00072 //////////////////////////////////////////////////////////////////// 00073 // Function: TextureImage::assign_groups 00074 // Access: Public 00075 // Description: Assigns the texture to all of the PaletteGroups the 00076 // various egg files that use it need. Attempts to 00077 // choose the minimum set of PaletteGroups that 00078 // satisfies all of the egg files. 00079 //////////////////////////////////////////////////////////////////// 00080 void TextureImage:: 00081 assign_groups() { 00082 if (_egg_files.empty()) { 00083 // If we're not referenced by any egg files any more, assign us to 00084 // no groups. 00085 PaletteGroups empty; 00086 assign_to_groups(empty); 00087 return; 00088 } 00089 00090 PaletteGroups definitely_in; 00091 00092 // First, we need to eliminate from consideration all the egg files 00093 // that are already taken care of by the user's explicit group 00094 // assignments for this texture. 00095 WorkingEggs needed_eggs; 00096 00097 if (_explicitly_assigned_groups.empty()) { 00098 // If we have no explicit group assignments, we must consider all 00099 // the egg files. 00100 copy(_egg_files.begin(), _egg_files.end(), back_inserter(needed_eggs)); 00101 00102 } else { 00103 // Otherwise, we only need to consider the egg files that don't 00104 // have any groups in common with our explicit assignments. 00105 00106 EggFiles::const_iterator ei; 00107 for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) { 00108 PaletteGroups intersect; 00109 intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups()); 00110 if (!intersect.empty()) { 00111 // This egg file is satisfied by one of the texture's explicit 00112 // assignments. 00113 00114 // We must use at least one of the explicitly-assigned groups 00115 // that satisfied the egg file. We don't need to use all of 00116 // them, however, and we choose the first one arbitrarily. 00117 definitely_in.insert(*intersect.begin()); 00118 00119 } else { 00120 // This egg file was not satisfied by any of the texture's 00121 // explicit assignments. Therefore, we'll need to choose some 00122 // additional group to assign the texture to, to make the egg 00123 // file happy. Defer this a bit. 00124 needed_eggs.push_back(*ei); 00125 } 00126 } 00127 } 00128 00129 while (!needed_eggs.empty()) { 00130 // We need to know the complete set of groups that we need to 00131 // consider adding the texture to. This is the union of all the egg 00132 // files' requested groups. 00133 PaletteGroups total; 00134 WorkingEggs::const_iterator ei; 00135 for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) { 00136 total.make_union(total, (*ei)->get_complete_groups()); 00137 } 00138 00139 // We don't count the "null" group for texture assignment. 00140 total.remove_null(); 00141 if (total.empty()) { 00142 break; 00143 } 00144 00145 // Now, find the group that will satisfy the most egg files. If 00146 // two groups satisfy the same number of egg files, choose (a) the 00147 // most specific one, i.e. with the lowest dirname_level, or the 00148 // lowest dependency_level if the dirname_levels are equal, and 00149 // (b) the one that has the fewest egg files sharing it. 00150 PaletteGroups::iterator gi = total.begin(); 00151 PaletteGroup *best = (*gi); 00152 int best_egg_count = compute_egg_count(best, needed_eggs); 00153 ++gi; 00154 while (gi != total.end()) { 00155 PaletteGroup *group = (*gi); 00156 00157 // Do we prefer this group to our current 'best'? 00158 bool prefer_group = false; 00159 int group_egg_count = compute_egg_count(group, needed_eggs); 00160 if (group_egg_count != best_egg_count) { 00161 prefer_group = (group_egg_count > best_egg_count); 00162 00163 } else { 00164 prefer_group = group->is_preferred_over(*best); 00165 } 00166 00167 if (prefer_group) { 00168 best = group; 00169 best_egg_count = group_egg_count; 00170 } 00171 ++gi; 00172 } 00173 00174 // Okay, now we've picked the best group. Eliminate all the eggs 00175 // from consideration that are satisfied by this group, and repeat. 00176 definitely_in.insert(best); 00177 00178 WorkingEggs next_needed_eggs; 00179 for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) { 00180 if ((*ei)->get_complete_groups().count(best) == 0) { 00181 // This one wasn't eliminated. 00182 next_needed_eggs.push_back(*ei); 00183 } 00184 } 00185 needed_eggs.swap(next_needed_eggs); 00186 } 00187 00188 // Finally, now that we've computed the set of groups we need to 00189 // assign the texture to, we need to reconcile this with the set of 00190 // groups we've assigned the texture to previously. 00191 assign_to_groups(definitely_in); 00192 } 00193 00194 //////////////////////////////////////////////////////////////////// 00195 // Function: TextureImage::get_groups 00196 // Access: Public 00197 // Description: Once assign_groups() has been called, this returns 00198 // the actual set of groups the TextureImage has been 00199 // assigned to. 00200 //////////////////////////////////////////////////////////////////// 00201 const PaletteGroups &TextureImage:: 00202 get_groups() const { 00203 return _actual_assigned_groups; 00204 } 00205 00206 //////////////////////////////////////////////////////////////////// 00207 // Function: TextureImage::get_placement 00208 // Access: Public 00209 // Description: Gets the TexturePlacement object which represents the 00210 // assignment of this texture to the indicated group. 00211 // If the texture has not been assigned to the indicated 00212 // group, returns NULL. 00213 //////////////////////////////////////////////////////////////////// 00214 TexturePlacement *TextureImage:: 00215 get_placement(PaletteGroup *group) const { 00216 Placement::const_iterator pi; 00217 pi = _placement.find(group); 00218 if (pi == _placement.end()) { 00219 return (TexturePlacement *)NULL; 00220 } 00221 00222 return (*pi).second; 00223 } 00224 00225 //////////////////////////////////////////////////////////////////// 00226 // Function: TextureImage::force_replace 00227 // Access: Public 00228 // Description: Removes the texture from any PaletteImages it is 00229 // assigned to, but does not remove it from the groups. 00230 // It will be re-placed within each group when 00231 // PaletteGroup::place_all() is called. 00232 //////////////////////////////////////////////////////////////////// 00233 void TextureImage:: 00234 force_replace() { 00235 Placement::iterator pi; 00236 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00237 (*pi).second->force_replace(); 00238 } 00239 } 00240 00241 //////////////////////////////////////////////////////////////////// 00242 // Function: TextureImage::mark_eggs_stale 00243 // Access: Public 00244 // Description: Marks all the egg files that reference this texture 00245 // stale. Should be called only when the texture 00246 // properties change in some catastrophic way that will 00247 // require every egg file referencing it to be 00248 // regenerated, even if it is not palettized. 00249 //////////////////////////////////////////////////////////////////// 00250 void TextureImage:: 00251 mark_eggs_stale() { 00252 Placement::iterator pi; 00253 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00254 (*pi).second->mark_eggs_stale(); 00255 } 00256 } 00257 00258 //////////////////////////////////////////////////////////////////// 00259 // Function: TextureImage::mark_texture_named 00260 // Access: Public 00261 // Description: Indicates that this particular texture has been named 00262 // by the user for processing this session, normally by 00263 // listing an egg file on the command line that 00264 // references it. 00265 //////////////////////////////////////////////////////////////////// 00266 void TextureImage:: 00267 mark_texture_named() { 00268 _texture_named = true; 00269 } 00270 00271 //////////////////////////////////////////////////////////////////// 00272 // Function: TextureImage::is_texture_named 00273 // Access: Public 00274 // Description: Returns true if this particular texture has been 00275 // named by the user for procession this session, for 00276 // instance by listing an egg file on the command line 00277 // that references it. 00278 //////////////////////////////////////////////////////////////////// 00279 bool TextureImage:: 00280 is_texture_named() const { 00281 return _texture_named; 00282 } 00283 00284 //////////////////////////////////////////////////////////////////// 00285 // Function: TextureImage::pre_txa_file 00286 // Access: Public 00287 // Description: Updates any internal state prior to reading the .txa 00288 // file. 00289 //////////////////////////////////////////////////////////////////// 00290 void TextureImage:: 00291 pre_txa_file() { 00292 // Save our current properties, so we can note if they change. 00293 _pre_txa_properties = _properties; 00294 00295 // Get our properties from the actual image for this texture. It's 00296 // possible the .txa file will update them further. 00297 SourceTextureImage *source = get_preferred_source(); 00298 if (source != (SourceTextureImage *)NULL) { 00299 _properties = source->get_properties(); 00300 } 00301 00302 _pre_txa_alpha_mode = _alpha_mode; 00303 _alpha_mode = EggRenderMode::AM_unspecified; 00304 00305 _request.pre_txa_file(); 00306 _is_surprise = true; 00307 } 00308 00309 //////////////////////////////////////////////////////////////////// 00310 // Function: TextureImage::post_txa_file 00311 // Access: Public 00312 // Description: Once the .txa file has been read and the TextureImage 00313 // matched against it, considers applying the requested 00314 // size change. Updates the TextureImage's size with 00315 // the size the texture ought to be, if this can be 00316 // determined. 00317 //////////////////////////////////////////////////////////////////// 00318 void TextureImage:: 00319 post_txa_file() { 00320 _got_txa_file = true; 00321 00322 // First, get the actual size of the texture. 00323 SourceTextureImage *source = get_preferred_source(); 00324 if (source != (SourceTextureImage *)NULL) { 00325 if (source->get_size()) { 00326 _size_known = true; 00327 _x_size = source->get_x_size(); 00328 _y_size = source->get_y_size(); 00329 _properties.set_num_channels(source->get_num_channels()); 00330 } 00331 } 00332 00333 // Now update this with a particularly requested size. 00334 if (_request._got_size) { 00335 _size_known = true; 00336 _x_size = _request._x_size; 00337 _y_size = _request._y_size; 00338 } 00339 00340 if (_txa_wrap_u != _request._wrap_u || 00341 _txa_wrap_v != _request._wrap_v) { 00342 _txa_wrap_u = _request._wrap_u; 00343 _txa_wrap_v = _request._wrap_v; 00344 00345 // If the explicit wrap mode changes, we may need to regenerate 00346 // the egg files, and/or refill the palettes. 00347 mark_eggs_stale(); 00348 00349 Placement::iterator pi; 00350 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00351 TexturePlacement *placement = (*pi).second; 00352 placement->mark_unfilled(); 00353 } 00354 } 00355 00356 if (_properties.has_num_channels() && !_request._keep_format) { 00357 int num_channels = _properties.get_num_channels(); 00358 // Examine the image to determine if we can downgrade the number 00359 // of channels, for instance from color to grayscale. 00360 if (num_channels == 3 || num_channels == 4) { 00361 consider_grayscale(); 00362 } 00363 00364 // Also consider the alpha properties, and whether we should 00365 // downgrade from alpha to non-alpha. 00366 if (num_channels == 2 || num_channels == 4) { 00367 consider_alpha(); 00368 } 00369 } 00370 00371 // However, if we got an explicit request for channels, honor that. 00372 if (_request._got_num_channels) { 00373 _properties.set_num_channels(_request._num_channels); 00374 } 00375 00376 _properties._generic_format = _request._generic_format; 00377 _properties._keep_format = _request._keep_format; 00378 00379 if (_request._format != EggTexture::F_unspecified) { 00380 _properties._format = _request._format; 00381 _properties._force_format = _request._force_format; 00382 } 00383 00384 if (_request._minfilter != EggTexture::FT_unspecified) { 00385 _properties._minfilter = _request._minfilter; 00386 } 00387 if (_request._magfilter != EggTexture::FT_unspecified) { 00388 _properties._magfilter = _request._magfilter; 00389 } 00390 00391 _properties._anisotropic_degree = _request._anisotropic_degree; 00392 00393 if (_properties._color_type == (PNMFileType *)NULL) { 00394 _properties._color_type = _request._properties._color_type; 00395 _properties._alpha_type = _request._properties._alpha_type; 00396 } 00397 00398 // Finally, make sure our properties are fully defined. 00399 _properties.fully_define(); 00400 00401 // Now, if our properties have changed in all that from our previous 00402 // session, we need to re-place ourself in all palette groups. 00403 if (_properties != _pre_txa_properties) { 00404 force_replace(); 00405 00406 // The above will mark the egg files stale when the texture is 00407 // palettized (since the UV's will certainly need to be 00408 // recomputed), but sometimes we need to mark the egg files stale 00409 // even when the texture is not palettized (if a critical property 00410 // has changed). The following accomplishes this: 00411 if (!_properties.egg_properties_match(_pre_txa_properties)) { 00412 mark_eggs_stale(); 00413 } 00414 } 00415 00416 // The alpha mode isn't stored in the properties, because it doesn't 00417 // affect which textures may be associated into a common palette. 00418 if (_request._alpha_mode != EggRenderMode::AM_unspecified) { 00419 _alpha_mode = _request._alpha_mode; 00420 } 00421 00422 // On the other hand, if we don't have an alpha channel, we 00423 // shouldn't have an alpha mode. 00424 if (_properties.has_num_channels()) { 00425 int num_channels = _properties.get_num_channels(); 00426 if (num_channels == 1 || num_channels == 3) { 00427 _alpha_mode = EggRenderMode::AM_unspecified; 00428 } 00429 } 00430 00431 // If we've changed the alpha mode, we should also mark the eggs 00432 // stale. 00433 if (_pre_txa_alpha_mode != _alpha_mode) { 00434 mark_eggs_stale(); 00435 } 00436 } 00437 00438 //////////////////////////////////////////////////////////////////// 00439 // Function: TextureImage::got_txa_file 00440 // Access: Public 00441 // Description: Returns true if this TextureImage has been looked up 00442 // in the .txa file this session, false otherwise. 00443 //////////////////////////////////////////////////////////////////// 00444 bool TextureImage:: 00445 got_txa_file() const { 00446 return _got_txa_file; 00447 } 00448 00449 //////////////////////////////////////////////////////////////////// 00450 // Function: TextureImage::determine_placement_size 00451 // Access: Public 00452 // Description: Calls determine_size() on each TexturePlacement for 00453 // the texture, to ensure that each TexturePlacement is 00454 // still requesting the best possible size for the 00455 // texture. 00456 //////////////////////////////////////////////////////////////////// 00457 void TextureImage:: 00458 determine_placement_size() { 00459 Placement::iterator pi; 00460 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00461 TexturePlacement *placement = (*pi).second; 00462 placement->determine_size(); 00463 } 00464 } 00465 00466 //////////////////////////////////////////////////////////////////// 00467 // Function: TextureImage::get_omit 00468 // Access: Public 00469 // Description: Returns true if the user specifically requested to 00470 // omit this texture via the "omit" keyword in the .txa 00471 // file, or false otherwise. 00472 //////////////////////////////////////////////////////////////////// 00473 bool TextureImage:: 00474 get_omit() const { 00475 return _request._omit; 00476 } 00477 00478 //////////////////////////////////////////////////////////////////// 00479 // Function: TextureImage::get_coverage_threshold 00480 // Access: Public 00481 // Description: Returns the appropriate coverage threshold for this 00482 // texture. This is either the 00483 // Palettizer::_coverage_threshold parameter, given 00484 // globally via -r, or a particular value for this 00485 // texture as supplied by the "coverage" keyword in the 00486 // .txa file. 00487 //////////////////////////////////////////////////////////////////// 00488 double TextureImage:: 00489 get_coverage_threshold() const { 00490 return _request._coverage_threshold; 00491 } 00492 00493 //////////////////////////////////////////////////////////////////// 00494 // Function: TextureImage::get_margin 00495 // Access: Public 00496 // Description: Returns the appropriate margin for this texture. 00497 // This is either the Palettizer::_margin parameter, or 00498 // a particular value for this texture as supplied by 00499 // the "margin" keyword in the .txa file. 00500 //////////////////////////////////////////////////////////////////// 00501 int TextureImage:: 00502 get_margin() const { 00503 return _request._margin; 00504 } 00505 00506 //////////////////////////////////////////////////////////////////// 00507 // Function: TextureImage::is_surprise 00508 // Access: Public 00509 // Description: Returns true if this particular texture is a 00510 // 'surprise', i.e. it wasn't matched by a line in the 00511 // .txa file that didn't include the keyword 'cont'. 00512 //////////////////////////////////////////////////////////////////// 00513 bool TextureImage:: 00514 is_surprise() const { 00515 if (_placement.empty()) { 00516 // A texture that is not actually placed anywhere is not 00517 // considered a surprise. 00518 return false; 00519 } 00520 00521 return _is_surprise; 00522 } 00523 00524 //////////////////////////////////////////////////////////////////// 00525 // Function: TextureImage::is_used 00526 // Access: Public 00527 // Description: Returns true if this particular texture has been 00528 // placed somewhere, anywhere, or false if it is not 00529 // used. 00530 //////////////////////////////////////////////////////////////////// 00531 bool TextureImage:: 00532 is_used() const { 00533 return !_placement.empty(); 00534 } 00535 00536 //////////////////////////////////////////////////////////////////// 00537 // Function: TextureImage::get_alpha_mode 00538 // Access: Public 00539 // Description: Returns the alpha mode that should be used to render 00540 // objects with this texture, as specified by the user 00541 // or as determined from examining the texture's alpha 00542 // channel. 00543 //////////////////////////////////////////////////////////////////// 00544 EggRenderMode::AlphaMode TextureImage:: 00545 get_alpha_mode() const { 00546 return _alpha_mode; 00547 } 00548 00549 //////////////////////////////////////////////////////////////////// 00550 // Function: TextureImage::get_txa_wrap_u 00551 // Access: Public 00552 // Description: Returns the wrap mode specified in the u direction in 00553 // the txa file, or WM_unspecified. 00554 //////////////////////////////////////////////////////////////////// 00555 EggTexture::WrapMode TextureImage:: 00556 get_txa_wrap_u() const { 00557 return _txa_wrap_u; 00558 } 00559 00560 //////////////////////////////////////////////////////////////////// 00561 // Function: TextureImage::get_txa_wrap_v 00562 // Access: Public 00563 // Description: Returns the wrap mode specified in the v direction in 00564 // the txa file, or WM_unspecified. 00565 //////////////////////////////////////////////////////////////////// 00566 EggTexture::WrapMode TextureImage:: 00567 get_txa_wrap_v() const { 00568 return _txa_wrap_v; 00569 } 00570 00571 00572 //////////////////////////////////////////////////////////////////// 00573 // Function: TextureImage::get_source 00574 // Access: Public 00575 // Description: Returns the SourceTextureImage corresponding to the 00576 // given filename(s). If the given filename has never 00577 // been used as a SourceTexture for this particular 00578 // texture, creates a new SourceTextureImage and returns 00579 // that. 00580 //////////////////////////////////////////////////////////////////// 00581 SourceTextureImage *TextureImage:: 00582 get_source(const Filename &filename, const Filename &alpha_filename, 00583 int alpha_file_channel) { 00584 string key = get_source_key(filename, alpha_filename, alpha_file_channel); 00585 00586 Sources::iterator si; 00587 si = _sources.find(key); 00588 if (si != _sources.end()) { 00589 return (*si).second; 00590 } 00591 00592 SourceTextureImage *source = 00593 new SourceTextureImage(this, filename, alpha_filename, alpha_file_channel); 00594 _sources.insert(Sources::value_type(key, source)); 00595 00596 // Clear out the preferred source image to force us to rederive this 00597 // next time someone asks. 00598 _preferred_source = (SourceTextureImage *)NULL; 00599 _read_source_image = false; 00600 00601 return source; 00602 } 00603 00604 //////////////////////////////////////////////////////////////////// 00605 // Function: TextureImage::get_preferred_source 00606 // Access: Public 00607 // Description: Determines the preferred source image for examining 00608 // size and reading pixels, etc. This is the largest 00609 // and most recent of all the available source images. 00610 //////////////////////////////////////////////////////////////////// 00611 SourceTextureImage *TextureImage:: 00612 get_preferred_source() { 00613 if (_preferred_source != (SourceTextureImage *)NULL) { 00614 return _preferred_source; 00615 } 00616 00617 // Now examine all of the various source images available to us and 00618 // pick the most suitable. We base this on the following criteria: 00619 00620 // (1) A suitable source image must be referenced by at least one 00621 // egg file, unless no source images are referenced by any egg file. 00622 00623 // (2) A larger source image is preferable to a smaller one. 00624 00625 // (3) Given two source images of the same size, the more recent one 00626 // is preferable. 00627 00628 // Are any source images referenced by an egg file? 00629 00630 bool any_referenced = false; 00631 Sources::iterator si; 00632 for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) { 00633 SourceTextureImage *source = (*si).second; 00634 if (source->get_egg_count() > 0) { 00635 any_referenced = true; 00636 } 00637 } 00638 00639 SourceTextureImage *best = (SourceTextureImage *)NULL; 00640 int best_size = 0; 00641 00642 for (si = _sources.begin(); si != _sources.end(); ++si) { 00643 SourceTextureImage *source = (*si).second; 00644 00645 if (source->get_egg_count() > 0 || !any_referenced) { 00646 // Rule (1) passes. 00647 00648 if (source->exists() && source->get_size()) { 00649 int source_size = source->get_x_size() * source->get_y_size(); 00650 if (best == (SourceTextureImage *)NULL) { 00651 best = source; 00652 best_size = source_size; 00653 00654 } else if (source_size > best_size) { 00655 // Rule (2) passes. 00656 best = source; 00657 best_size = source_size; 00658 00659 } else if (source_size == best_size && 00660 source->get_filename().compare_timestamps(best->get_filename()) > 0) { 00661 // Rule (3) passes. 00662 best = source; 00663 best_size = source_size; 00664 } 00665 } 00666 } 00667 } 00668 00669 if (best == (SourceTextureImage *)NULL && !_sources.empty()) { 00670 // If we didn't pick any that pass, it must be that all of them 00671 // are unreadable. In this case, it really doesn't matter which 00672 // one we pick, but we should at least pick one that has an egg 00673 // reference, if any of them do. 00674 if (any_referenced) { 00675 for (si = _sources.begin(); 00676 si != _sources.end() && best == (SourceTextureImage *)NULL; 00677 ++si) { 00678 SourceTextureImage *source = (*si).second; 00679 if (source->get_egg_count() > 0) { 00680 best = source; 00681 } 00682 } 00683 } else { 00684 best = (*_sources.begin()).second; 00685 } 00686 } 00687 00688 _preferred_source = best; 00689 return _preferred_source; 00690 } 00691 00692 //////////////////////////////////////////////////////////////////// 00693 // Function: TextureImage::clear_source_basic_properties 00694 // Access: Public 00695 // Description: Calls clear_basic_properties() on each source texture 00696 // image used by this texture, to reset the properties 00697 // in preparation for re-applying them from the set of 00698 // all known egg files. 00699 //////////////////////////////////////////////////////////////////// 00700 void TextureImage:: 00701 clear_source_basic_properties() { 00702 Sources::iterator si; 00703 for (si = _sources.begin(); si != _sources.end(); ++si) { 00704 SourceTextureImage *source = (*si).second; 00705 source->clear_basic_properties(); 00706 } 00707 } 00708 00709 //////////////////////////////////////////////////////////////////// 00710 // Function: TextureImage::copy_unplaced 00711 // Access: Public 00712 // Description: Copies the texture to whichever destination 00713 // directories are appropriate for the groups in which 00714 // it has been unplaced. Also removes the old filenames 00715 // for previous sessions where it was unplaced, but is 00716 // no longer. 00717 // 00718 // If redo_all is true, this recopies the texture 00719 // whether it needed to or not. 00720 //////////////////////////////////////////////////////////////////// 00721 void TextureImage:: 00722 copy_unplaced(bool redo_all) { 00723 // First, we need to build up the set of DestTextureImages that 00724 // represents the files we need to generate. 00725 Dests generate; 00726 00727 // Go through all the TexturePlacements and note the ones for which 00728 // we're unplaced. We check get_omit_reason() and not is_placed(), 00729 // because we want to consider solitary images to be unplaced in 00730 // this case. 00731 Placement::iterator pi; 00732 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00733 TexturePlacement *placement = (*pi).second; 00734 if (placement->get_omit_reason() != OR_none && 00735 placement->get_omit_reason() != OR_unknown) { 00736 DestTextureImage *dest = new DestTextureImage(placement); 00737 Filename filename = dest->get_filename(); 00738 FilenameUnifier::make_canonical(filename); 00739 00740 pair<Dests::iterator, bool> insert_result = generate.insert 00741 (Dests::value_type(filename, dest)); 00742 if (!insert_result.second) { 00743 // At least two DestTextureImages map to the same filename, no 00744 // sweat. 00745 delete dest; 00746 dest = (*insert_result.first).second; 00747 } 00748 00749 placement->set_dest(dest); 00750 00751 } else { 00752 placement->set_dest((DestTextureImage *)NULL); 00753 } 00754 } 00755 00756 if (redo_all) { 00757 // If we're redoing everything, we remove everything first and 00758 // then recopy it again. 00759 Dests empty; 00760 remove_old_dests(empty, _dests); 00761 copy_new_dests(generate, empty); 00762 00763 } else { 00764 // Otherwise, we only remove and recopy the things that changed 00765 // between this time and last time. 00766 remove_old_dests(generate, _dests); 00767 copy_new_dests(generate, _dests); 00768 } 00769 00770 // Clean up the old set. 00771 Dests::iterator di; 00772 for (di = _dests.begin(); di != _dests.end(); ++di) { 00773 delete (*di).second; 00774 } 00775 00776 _dests.swap(generate); 00777 } 00778 00779 //////////////////////////////////////////////////////////////////// 00780 // Function: TextureImage::read_source_image 00781 // Access: Public 00782 // Description: Reads in the original image, if it has not already 00783 // been read, and returns it. 00784 //////////////////////////////////////////////////////////////////// 00785 const PNMImage &TextureImage:: 00786 read_source_image() { 00787 if (!_read_source_image) { 00788 SourceTextureImage *source = get_preferred_source(); 00789 if (source != (SourceTextureImage *)NULL) { 00790 source->read(_source_image); 00791 } 00792 _read_source_image = true; 00793 _allow_release_source_image = true; 00794 _ever_read_image = true; 00795 } 00796 00797 return _source_image; 00798 } 00799 00800 //////////////////////////////////////////////////////////////////// 00801 // Function: TextureImage::release_source_image 00802 // Access: Public 00803 // Description: Frees the memory that was allocated by a previous 00804 // call to read_source_image(). The next time 00805 // read_source_image() is called, it will have to read 00806 // the disk again. 00807 //////////////////////////////////////////////////////////////////// 00808 void TextureImage:: 00809 release_source_image() { 00810 if (_read_source_image && _allow_release_source_image) { 00811 _source_image.clear(); 00812 _read_source_image = false; 00813 } 00814 } 00815 00816 //////////////////////////////////////////////////////////////////// 00817 // Function: TextureImage::set_source_image 00818 // Access: Public 00819 // Description: Accepts the indicated source image as if it had been 00820 // read from disk. This image is copied into the 00821 // structure, and will be returned by future calls to 00822 // read_source_image(). 00823 //////////////////////////////////////////////////////////////////// 00824 void TextureImage:: 00825 set_source_image(const PNMImage &image) { 00826 _source_image = image; 00827 _allow_release_source_image = false; 00828 _read_source_image = true; 00829 _ever_read_image = true; 00830 } 00831 00832 //////////////////////////////////////////////////////////////////// 00833 // Function: TextureImage::read_header 00834 // Access: Public 00835 // Description: Causes the header part of the image to be reread, 00836 // usually to confirm that its image properties (size, 00837 // number of channels, etc.) haven't changed. 00838 //////////////////////////////////////////////////////////////////// 00839 void TextureImage:: 00840 read_header() { 00841 if (!_read_source_image) { 00842 SourceTextureImage *source = get_preferred_source(); 00843 if (source != (SourceTextureImage *)NULL) { 00844 source->read_header(); 00845 } 00846 } 00847 } 00848 00849 //////////////////////////////////////////////////////////////////// 00850 // Function: TextureImage::is_newer_than 00851 // Access: Public 00852 // Description: Returns true if the source image is newer than the 00853 // indicated file, false otherwise. If the image has 00854 // already been read, this always returns false. 00855 //////////////////////////////////////////////////////////////////// 00856 bool TextureImage:: 00857 is_newer_than(const Filename &reference_filename) { 00858 if (!_read_source_image) { 00859 SourceTextureImage *source = get_preferred_source(); 00860 if (source != (SourceTextureImage *)NULL) { 00861 const Filename &source_filename = source->get_filename(); 00862 return source_filename.compare_timestamps(reference_filename) >= 0; 00863 } 00864 } 00865 00866 return false; 00867 } 00868 00869 //////////////////////////////////////////////////////////////////// 00870 // Function: TextureImage::write_source_pathnames 00871 // Access: Public 00872 // Description: Writes the list of source pathnames that might 00873 // contribute to this texture to the indicated output 00874 // stream, one per line. 00875 //////////////////////////////////////////////////////////////////// 00876 void TextureImage:: 00877 write_source_pathnames(ostream &out, int indent_level) const { 00878 Sources::const_iterator si; 00879 for (si = _sources.begin(); si != _sources.end(); ++si) { 00880 SourceTextureImage *source = (*si).second; 00881 00882 if (source->get_egg_count() > 0) { 00883 indent(out, indent_level); 00884 source->output_filename(out); 00885 if (!source->is_size_known()) { 00886 out << " (unknown size)"; 00887 00888 } else { 00889 out << " " << source->get_x_size() << " " 00890 << source->get_y_size(); 00891 00892 if (source->get_properties().has_num_channels()) { 00893 out << " " << source->get_properties().get_num_channels(); 00894 } 00895 } 00896 out << "\n"; 00897 } 00898 } 00899 00900 if (_is_cutout) { 00901 indent(out, indent_level) 00902 << "Cutout image (ratio " << (float)_mid_pixel_ratio << ")\n"; 00903 } 00904 00905 // Now write out the group assignments. 00906 if (!_egg_files.empty()) { 00907 // Sort the egg files into order by name for output. 00908 pvector<EggFile *> egg_vector; 00909 egg_vector.reserve(_egg_files.size()); 00910 EggFiles::const_iterator ei; 00911 for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) { 00912 egg_vector.push_back(*ei); 00913 } 00914 sort(egg_vector.begin(), egg_vector.end(), 00915 IndirectCompareNames<EggFile>()); 00916 00917 indent(out, indent_level) 00918 << "Used by:\n"; 00919 pvector<EggFile *>::const_iterator evi; 00920 for (evi = egg_vector.begin(); evi != egg_vector.end(); ++evi) { 00921 EggFile *egg = (*evi); 00922 indent(out, indent_level + 2) 00923 << egg->get_name() << " ("; 00924 if (egg->get_explicit_groups().empty()) { 00925 out << *egg->get_default_group(); 00926 } else { 00927 out << egg->get_explicit_groups(); 00928 } 00929 out << ")\n"; 00930 } 00931 } 00932 if (!_explicitly_assigned_groups.empty()) { 00933 indent(out, indent_level) 00934 << "Explicitly assigned to " << _explicitly_assigned_groups << " in .txa\n"; 00935 } 00936 00937 if (_placement.empty()) { 00938 indent(out, indent_level) 00939 << "Not used.\n"; 00940 } else { 00941 indent(out, indent_level) 00942 << "Assigned to " << _actual_assigned_groups << "\n"; 00943 } 00944 } 00945 00946 //////////////////////////////////////////////////////////////////// 00947 // Function: TextureImage::write_scale_info 00948 // Access: Public 00949 // Description: Writes the information about the texture's size and 00950 // placement. 00951 //////////////////////////////////////////////////////////////////// 00952 void TextureImage:: 00953 write_scale_info(ostream &out, int indent_level) { 00954 SourceTextureImage *source = get_preferred_source(); 00955 indent(out, indent_level) << get_name(); 00956 00957 // Write the list of groups we're placed in. 00958 if (_placement.empty()) { 00959 out << " (not used)"; 00960 } else { 00961 Placement::const_iterator pi; 00962 pi = _placement.begin(); 00963 out << " (" << (*pi).second->get_group()->get_name(); 00964 ++pi; 00965 while (pi != _placement.end()) { 00966 out << " " << (*pi).second->get_group()->get_name(); 00967 ++pi; 00968 } 00969 out << ")"; 00970 } 00971 00972 out << " orig "; 00973 00974 if (source == (SourceTextureImage *)NULL || 00975 !source->is_size_known()) { 00976 out << "unknown"; 00977 } else { 00978 out << source->get_x_size() << " " << source->get_y_size() 00979 << " " << source->get_num_channels(); 00980 } 00981 00982 if (!_placement.empty() && is_size_known()) { 00983 out << " new " << get_x_size() << " " << get_y_size() 00984 << " " << get_num_channels(); 00985 00986 if (source != (SourceTextureImage *)NULL && 00987 source->is_size_known()) { 00988 double scale = 00989 100.0 * (((double)get_x_size() / (double)source->get_x_size()) + 00990 ((double)get_y_size() / (double)source->get_y_size())) / 2.0; 00991 out << " scale " << scale << "%"; 00992 } 00993 } 00994 out << "\n"; 00995 00996 // Also cross-reference the placed and unplaced information. 00997 Placement::iterator pi; 00998 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 00999 TexturePlacement *placement = (*pi).second; 01000 if (placement->get_omit_reason() == OR_none) { 01001 PaletteImage *image = placement->get_image(); 01002 nassertv(image != (PaletteImage *)NULL); 01003 indent(out, indent_level + 2) 01004 << "placed on " 01005 << FilenameUnifier::make_user_filename(image->get_filename()) 01006 << "\n"; 01007 01008 } else if (placement->get_omit_reason() == OR_unknown) { 01009 indent(out, indent_level + 2) 01010 << "not placed because unknown.\n"; 01011 01012 } else { 01013 DestTextureImage *image = placement->get_dest(); 01014 nassertv(image != (DestTextureImage *)NULL); 01015 indent(out, indent_level + 2) 01016 << "copied to " 01017 << FilenameUnifier::make_user_filename(image->get_filename()); 01018 if (image->is_size_known() && is_size_known() && 01019 (image->get_x_size() != get_x_size() || 01020 image->get_y_size() != get_y_size())) { 01021 out << " at size " << image->get_x_size() << " " 01022 << image->get_y_size(); 01023 if (source != (SourceTextureImage *)NULL && 01024 source->is_size_known()) { 01025 double scale = 01026 100.0 * (((double)image->get_x_size() / (double)source->get_x_size()) + 01027 ((double)image->get_y_size() / (double)source->get_y_size())) / 2.0; 01028 out << " scale " << scale << "%"; 01029 } 01030 } 01031 out << "\n"; 01032 } 01033 } 01034 } 01035 01036 //////////////////////////////////////////////////////////////////// 01037 // Function: TextureImage::compute_egg_count 01038 // Access: Private 01039 // Description: Counts the number of egg files in the indicated set 01040 // that will be satisfied if a texture is assigned to 01041 // the indicated group. 01042 //////////////////////////////////////////////////////////////////// 01043 int TextureImage:: 01044 compute_egg_count(PaletteGroup *group, 01045 const TextureImage::WorkingEggs &egg_files) { 01046 int count = 0; 01047 01048 WorkingEggs::const_iterator ei; 01049 for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) { 01050 if ((*ei)->get_complete_groups().count(group) != 0) { 01051 count++; 01052 } 01053 } 01054 01055 return count; 01056 } 01057 01058 //////////////////////////////////////////////////////////////////// 01059 // Function: TextureImage::assign_to_groups 01060 // Access: Private 01061 // Description: Assigns the texture to the indicated set of groups. 01062 // If the texture was previously assigned to any of 01063 // these groups, keeps the same TexturePlacement object 01064 // for the assignment; at the same time, deletes any 01065 // TexturePlacement objects that represent groups we are 01066 // no longer assigned to. 01067 //////////////////////////////////////////////////////////////////// 01068 void TextureImage:: 01069 assign_to_groups(const PaletteGroups &groups) { 01070 PaletteGroups::const_iterator gi; 01071 Placement::const_iterator pi; 01072 01073 Placement new_placement; 01074 01075 gi = groups.begin(); 01076 pi = _placement.begin(); 01077 01078 while (gi != groups.end() && pi != _placement.end()) { 01079 PaletteGroup *a = (*gi); 01080 PaletteGroup *b = (*pi).first; 01081 01082 if (a < b) { 01083 // Here's a group we're now assigned to that we weren't assigned 01084 // to previously. 01085 TexturePlacement *place = a->prepare(this); 01086 new_placement.insert 01087 (new_placement.end(), Placement::value_type(a, place)); 01088 ++gi; 01089 01090 } else if (b < a) { 01091 // Here's a group we're no longer assigned to. 01092 TexturePlacement *place = (*pi).second; 01093 delete place; 01094 ++pi; 01095 01096 } else { // b == a 01097 // Here's a group we're still assigned to. 01098 TexturePlacement *place = (*pi).second; 01099 new_placement.insert 01100 (new_placement.end(), Placement::value_type(a, place)); 01101 ++gi; 01102 ++pi; 01103 } 01104 } 01105 01106 while (gi != groups.end()) { 01107 // Here's a group we're now assigned to that we weren't assigned 01108 // to previously. 01109 PaletteGroup *a = (*gi); 01110 TexturePlacement *place = a->prepare(this); 01111 new_placement.insert 01112 (new_placement.end(), Placement::value_type(a, place)); 01113 ++gi; 01114 } 01115 01116 while (pi != _placement.end()) { 01117 // Here's a group we're no longer assigned to. 01118 TexturePlacement *place = (*pi).second; 01119 delete place; 01120 ++pi; 01121 } 01122 01123 _placement.swap(new_placement); 01124 _actual_assigned_groups = groups; 01125 } 01126 01127 //////////////////////////////////////////////////////////////////// 01128 // Function: TextureImage::consider_grayscale 01129 // Access: Private 01130 // Description: Examines the actual contents of the image to 01131 // determine if it should maybe be considered a 01132 // grayscale image (even though it has separate rgb 01133 // components). 01134 //////////////////////////////////////////////////////////////////// 01135 void TextureImage:: 01136 consider_grayscale() { 01137 // Since this isn't likely to change for a particular texture after 01138 // its creation, we save a bit of time by not performing this check 01139 // unless this is the first time we've ever seen this texture. This 01140 // will save us from having to load the texture images each time we 01141 // look at them. On the other hand, if we've already loaded up the 01142 // image, then go ahead. 01143 if (!_read_source_image && _ever_read_image) { 01144 if (_forced_grayscale) { 01145 _properties.force_grayscale(); 01146 } 01147 return; 01148 } 01149 01150 const PNMImage &source = read_source_image(); 01151 if (!source.is_valid()) { 01152 return; 01153 } 01154 01155 for (int y = 0; y < source.get_y_size(); y++) { 01156 for (int x = 0; x < source.get_x_size(); x++) { 01157 const xel &v = source.get_xel_val(x, y); 01158 if (PPM_GETR(v) != PPM_GETG(v) || PPM_GETR(v) != PPM_GETB(v)) { 01159 // Here's a colored pixel. We can't go grayscale. 01160 _forced_grayscale = false; 01161 return; 01162 } 01163 } 01164 } 01165 01166 // All pixels in the image were grayscale! 01167 _properties.force_grayscale(); 01168 _forced_grayscale = true; 01169 } 01170 01171 //////////////////////////////////////////////////////////////////// 01172 // Function: TextureImage::consider_alpha 01173 // Access: Private 01174 // Description: Examines the actual contents of the image to 01175 // determine what alpha properties it has. 01176 //////////////////////////////////////////////////////////////////// 01177 void TextureImage:: 01178 consider_alpha() { 01179 // As above, we don't bother doing this if we've already done this 01180 // in a previous session. 01181 01182 // _alpha_bits == -1 indicates we have read an older textures.boo 01183 // file that didn't define these bits. 01184 if (_read_source_image || !_ever_read_image || _alpha_bits == -1) { 01185 _alpha_bits = 0; 01186 int num_mid_pixels = 0; 01187 01188 const PNMImage &source = read_source_image(); 01189 if (source.is_valid() && source.has_alpha()) { 01190 xelval maxval = source.get_maxval(); 01191 for (int y = 0; y < source.get_y_size(); y++) { 01192 for (int x = 0; x < source.get_x_size(); x++) { 01193 xelval alpha_val = source.get_alpha_val(x, y); 01194 if (alpha_val == 0) { 01195 _alpha_bits |= AB_zero; 01196 } else if (alpha_val == maxval) { 01197 _alpha_bits |= AB_one; 01198 } else { 01199 _alpha_bits |= AB_mid; 01200 ++num_mid_pixels; 01201 } 01202 } 01203 } 01204 } 01205 01206 int num_pixels = source.get_x_size() * source.get_y_size(); 01207 _mid_pixel_ratio = 0.0; 01208 if (num_pixels != 0) { 01209 _mid_pixel_ratio = (double)num_mid_pixels / (double)num_pixels; 01210 } 01211 } 01212 01213 _is_cutout = false; 01214 01215 if (_alpha_bits != 0) { 01216 if (_alpha_bits == AB_one) { 01217 // All alpha pixels are white; drop the alpha channel. 01218 _properties.force_nonalpha(); 01219 01220 } else if (_alpha_bits == AB_zero) { 01221 // All alpha pixels are invisible; this is probably a mistake. 01222 // Drop the alpha channel and complain. 01223 _properties.force_nonalpha(); 01224 if (_read_source_image) { 01225 nout << *this << " has an all-zero alpha channel; dropping alpha.\n"; 01226 } 01227 01228 } else if (_alpha_mode == EggRenderMode::AM_unspecified) { 01229 // Consider fiddling with the alpha mode, if the user hasn't 01230 // specified a particular alpha mode in the txa file. 01231 if ((_alpha_bits & AB_mid) == 0) { 01232 // No middle range bits: a binary alpha image. 01233 _alpha_mode = EggRenderMode::AM_binary; 01234 01235 } else if ((_alpha_bits & AB_one) != 0 && _mid_pixel_ratio < pal->_cutout_ratio) { 01236 // At least some opaque bits, and relatively few middle range 01237 // bits: a cutout image. 01238 _alpha_mode = pal->_cutout_mode; 01239 _is_cutout = true; 01240 01241 } else { 01242 // No opaque bits; just use regular alpha blending. 01243 _alpha_mode = EggRenderMode::AM_blend; 01244 } 01245 } 01246 } 01247 } 01248 01249 //////////////////////////////////////////////////////////////////// 01250 // Function: TextureImage::remove_old_dests 01251 // Access: Private 01252 // Description: Removes all of the filenames named in b that are not 01253 // also named in a. 01254 //////////////////////////////////////////////////////////////////// 01255 void TextureImage:: 01256 remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) { 01257 Dests::const_iterator ai = a.begin(); 01258 Dests::const_iterator bi = b.begin(); 01259 01260 while (ai != a.end() && bi != b.end()) { 01261 const string &astr = (*ai).first; 01262 const string &bstr = (*bi).first; 01263 01264 if (astr < bstr) { 01265 // Here's a filename in a, not in b. 01266 ++ai; 01267 01268 } else if (bstr < astr) { 01269 // Here's a filename in b, not in a. 01270 (*bi).second->unlink(); 01271 ++bi; 01272 01273 } else { // bstr == astr 01274 // Here's a filename in both a and b. 01275 ++ai; 01276 ++bi; 01277 } 01278 } 01279 01280 while (bi != b.end()) { 01281 // Here's a filename in b, not in a. 01282 (*bi).second->unlink(); 01283 ++bi; 01284 } 01285 01286 while (ai != a.end()) { 01287 ++ai; 01288 } 01289 } 01290 01291 //////////////////////////////////////////////////////////////////// 01292 // Function: TextureImage::copy_new_dests 01293 // Access: Private 01294 // Description: Copies a resized texture into each filename named in 01295 // a that is not also listed in b, or whose 01296 // corresponding listing in b is out of date. 01297 //////////////////////////////////////////////////////////////////// 01298 void TextureImage:: 01299 copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) { 01300 Dests::const_iterator ai = a.begin(); 01301 Dests::const_iterator bi = b.begin(); 01302 01303 while (ai != a.end() && bi != b.end()) { 01304 const string &astr = (*ai).first; 01305 const string &bstr = (*bi).first; 01306 01307 if (astr < bstr) { 01308 // Here's a filename in a, not in b. 01309 (*ai).second->copy(this); 01310 ++ai; 01311 01312 } else if (bstr < astr) { 01313 // Here's a filename in b, not in a. 01314 ++bi; 01315 01316 } else { // bstr == astr 01317 // Here's a filename in both a and b. 01318 (*ai).second->copy_if_stale((*bi).second, this); 01319 ++ai; 01320 ++bi; 01321 } 01322 } 01323 01324 while (ai != a.end()) { 01325 // Here's a filename in a, not in b. 01326 (*ai).second->copy(this); 01327 ++ai; 01328 } 01329 } 01330 01331 //////////////////////////////////////////////////////////////////// 01332 // Function: TextureImage::get_source_key 01333 // Access: Private 01334 // Description: Returns the key that a SourceTextureImage should be 01335 // stored in, given its one or two filenames. 01336 //////////////////////////////////////////////////////////////////// 01337 string TextureImage:: 01338 get_source_key(const Filename &filename, const Filename &alpha_filename, 01339 int alpha_file_channel) { 01340 Filename f = FilenameUnifier::make_bam_filename(filename); 01341 Filename a = FilenameUnifier::make_bam_filename(alpha_filename); 01342 01343 return f.get_fullpath() + ":" + a.get_fullpath() + ":" + 01344 format_string(alpha_file_channel); 01345 } 01346 01347 //////////////////////////////////////////////////////////////////// 01348 // Function: TextureImage::register_with_read_factory 01349 // Access: Public, Static 01350 // Description: Registers the current object as something that can be 01351 // read from a Bam file. 01352 //////////////////////////////////////////////////////////////////// 01353 void TextureImage:: 01354 register_with_read_factory() { 01355 BamReader::get_factory()-> 01356 register_factory(get_class_type(), make_TextureImage); 01357 } 01358 01359 //////////////////////////////////////////////////////////////////// 01360 // Function: TextureImage::write_datagram 01361 // Access: Public, Virtual 01362 // Description: Fills the indicated datagram up with a binary 01363 // representation of the current object, in preparation 01364 // for writing to a Bam file. 01365 //////////////////////////////////////////////////////////////////// 01366 void TextureImage:: 01367 write_datagram(BamWriter *writer, Datagram &datagram) { 01368 ImageFile::write_datagram(writer, datagram); 01369 datagram.add_string(get_name()); 01370 01371 // We don't write out _request; this is re-read from the .txa file 01372 // each time. 01373 01374 // We don't write out _pre_txa_properties; this is transitional. 01375 01376 // We don't write out _preferred_source; this is redetermined each 01377 // session. 01378 01379 datagram.add_bool(_is_surprise); 01380 datagram.add_bool(_ever_read_image); 01381 datagram.add_bool(_forced_grayscale); 01382 datagram.add_uint8(_alpha_bits); 01383 datagram.add_int16((int)_alpha_mode); 01384 datagram.add_float64(_mid_pixel_ratio); 01385 datagram.add_bool(_is_cutout); 01386 datagram.add_uint8((int)_txa_wrap_u); 01387 datagram.add_uint8((int)_txa_wrap_v); 01388 01389 // We don't write out _explicitly_assigned_groups; this is re-read 01390 // from the .txa file each time. 01391 01392 _actual_assigned_groups.write_datagram(writer, datagram); 01393 01394 // We don't write out _egg_files; this is redetermined each session. 01395 01396 datagram.add_uint32(_placement.size()); 01397 Placement::const_iterator pi; 01398 for (pi = _placement.begin(); pi != _placement.end(); ++pi) { 01399 writer->write_pointer(datagram, (*pi).first); 01400 writer->write_pointer(datagram, (*pi).second); 01401 } 01402 01403 datagram.add_uint32(_sources.size()); 01404 Sources::const_iterator si; 01405 for (si = _sources.begin(); si != _sources.end(); ++si) { 01406 writer->write_pointer(datagram, (*si).second); 01407 } 01408 01409 datagram.add_uint32(_dests.size()); 01410 Dests::const_iterator di; 01411 for (di = _dests.begin(); di != _dests.end(); ++di) { 01412 writer->write_pointer(datagram, (*di).second); 01413 } 01414 } 01415 01416 //////////////////////////////////////////////////////////////////// 01417 // Function: TextureImage::complete_pointers 01418 // Access: Public, Virtual 01419 // Description: Called after the object is otherwise completely read 01420 // from a Bam file, this function's job is to store the 01421 // pointers that were retrieved from the Bam file for 01422 // each pointer object written. The return value is the 01423 // number of pointers processed from the list. 01424 //////////////////////////////////////////////////////////////////// 01425 int TextureImage:: 01426 complete_pointers(TypedWritable **p_list, BamReader *manager) { 01427 int pi = ImageFile::complete_pointers(p_list, manager); 01428 01429 pi += _actual_assigned_groups.complete_pointers(p_list + pi, manager); 01430 01431 int i; 01432 for (i = 0; i < _num_placement; i++) { 01433 PaletteGroup *group; 01434 TexturePlacement *placement; 01435 DCAST_INTO_R(group, p_list[pi++], pi); 01436 DCAST_INTO_R(placement, p_list[pi++], pi); 01437 _placement.insert(Placement::value_type(group, placement)); 01438 } 01439 01440 for (i = 0; i < _num_sources; i++) { 01441 SourceTextureImage *source; 01442 DCAST_INTO_R(source, p_list[pi++], pi); 01443 string key = get_source_key(source->get_filename(), 01444 source->get_alpha_filename(), 01445 source->get_alpha_file_channel()); 01446 01447 bool inserted = _sources.insert(Sources::value_type(key, source)).second; 01448 if (!inserted) { 01449 nout << "Warning: texture key " << key 01450 << " is nonunique; texture lost.\n"; 01451 } 01452 } 01453 01454 for (i = 0; i < _num_dests; i++) { 01455 DestTextureImage *dest; 01456 DCAST_INTO_R(dest, p_list[pi++], pi); 01457 bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second; 01458 if (!inserted) { 01459 nout << "Warning: dest filename " << dest->get_filename() 01460 << " is nonunique; texture lost.\n"; 01461 } 01462 } 01463 01464 return pi; 01465 } 01466 01467 //////////////////////////////////////////////////////////////////// 01468 // Function: TextureImage::make_TextureImage 01469 // Access: Protected 01470 // Description: This method is called by the BamReader when an object 01471 // of this type is encountered in a Bam file; it should 01472 // allocate and return a new object with all the data 01473 // read. 01474 //////////////////////////////////////////////////////////////////// 01475 TypedWritable *TextureImage:: 01476 make_TextureImage(const FactoryParams ¶ms) { 01477 TextureImage *me = new TextureImage; 01478 DatagramIterator scan; 01479 BamReader *manager; 01480 01481 parse_params(params, scan, manager); 01482 me->fillin(scan, manager); 01483 return me; 01484 } 01485 01486 //////////////////////////////////////////////////////////////////// 01487 // Function: TextureImage::fillin 01488 // Access: Protected 01489 // Description: Reads the binary data from the given datagram 01490 // iterator, which was written by a previous call to 01491 // write_datagram(). 01492 //////////////////////////////////////////////////////////////////// 01493 void TextureImage:: 01494 fillin(DatagramIterator &scan, BamReader *manager) { 01495 ImageFile::fillin(scan, manager); 01496 set_name(scan.get_string()); 01497 01498 _is_surprise = scan.get_bool(); 01499 _ever_read_image = scan.get_bool(); 01500 _forced_grayscale = scan.get_bool(); 01501 _alpha_bits = scan.get_uint8(); 01502 _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16(); 01503 if (pal->_read_pi_version >= 16) { 01504 _mid_pixel_ratio = scan.get_float64(); 01505 _is_cutout = scan.get_bool(); 01506 } else { 01507 // Force a re-read of the image if we are upgrading to pi version 16. 01508 _ever_read_image = false; 01509 _mid_pixel_ratio = 0.0; 01510 _is_cutout = false; 01511 } 01512 if (pal->_read_pi_version >= 17) { 01513 _txa_wrap_u = (EggTexture::WrapMode)scan.get_uint8(); 01514 _txa_wrap_v = (EggTexture::WrapMode)scan.get_uint8(); 01515 } 01516 01517 _actual_assigned_groups.fillin(scan, manager); 01518 01519 _num_placement = scan.get_uint32(); 01520 manager->read_pointers(scan, _num_placement * 2); 01521 01522 _num_sources = scan.get_uint32(); 01523 manager->read_pointers(scan, _num_sources); 01524 _num_dests = scan.get_uint32(); 01525 manager->read_pointers(scan, _num_dests); 01526 }