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