texturePlacement.cxx

00001 // Filename: texturePlacement.cxx
00002 // Created by:  drose (30Nov00)
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 "texturePlacement.h"
00016 #include "textureReference.h"
00017 #include "textureImage.h"
00018 #include "paletteGroup.h"
00019 #include "paletteImage.h"
00020 #include "palettizer.h"
00021 #include "eggFile.h"
00022 #include "destTextureImage.h"
00023 
00024 #include "indent.h"
00025 #include "datagram.h"
00026 #include "datagramIterator.h"
00027 #include "bamReader.h"
00028 #include "bamWriter.h"
00029 #include "pnmImage.h"
00030 
00031 TypeHandle TexturePlacement::_type_handle;
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //     Function: TexturePlacement::Default Constructor
00035 //       Access: Private
00036 //  Description: The default constructor is only for the convenience
00037 //               of the Bam reader.
00038 ////////////////////////////////////////////////////////////////////
00039 TexturePlacement::
00040 TexturePlacement() {
00041   _texture = (TextureImage *)NULL;
00042   _group = (PaletteGroup *)NULL;
00043   _image = (PaletteImage *)NULL;
00044   _dest = (DestTextureImage *)NULL;
00045   _has_uvs = false;
00046   _size_known = false;
00047   _is_filled = true;
00048   _omit_reason = OR_none;
00049 }
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: TexturePlacement::Constructor
00053 //       Access: Public
00054 //  Description:
00055 ////////////////////////////////////////////////////////////////////
00056 TexturePlacement::
00057 TexturePlacement(TextureImage *texture, PaletteGroup *group) :
00058   _texture(texture),
00059   _group(group)
00060 {
00061   _omit_reason = OR_working;
00062 
00063   if (!texture->is_size_known()) {
00064     // If we were never able to figure out what size the texture
00065     // actually is, then we can't place the texture on a palette.
00066     _omit_reason = OR_unknown;
00067   }
00068 
00069   _image = (PaletteImage *)NULL;
00070   _dest = (DestTextureImage *)NULL;
00071   _has_uvs = false;
00072   _size_known = false;
00073   _is_filled = false;
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: TexturePlacement::Destructor
00078 //       Access: Public
00079 //  Description:
00080 ////////////////////////////////////////////////////////////////////
00081 TexturePlacement::
00082 ~TexturePlacement() {
00083   // Make sure we tell all our egg references they're not using us any
00084   // more.
00085   References::iterator ri;
00086   References copy_references = _references;
00087   for (ri = copy_references.begin(); ri != copy_references.end(); ++ri) {
00088     TextureReference *reference = (*ri);
00089     nassertv(reference->get_placement() == this);
00090     reference->clear_placement();
00091   }
00092 
00093   // And also our group, etc.
00094   _group->unplace(this);
00095 }
00096 
00097 ////////////////////////////////////////////////////////////////////
00098 //     Function: TexturePlacement::get_name
00099 //       Access: Public
00100 //  Description: Returns the name of the texture that this placement
00101 //               represents.
00102 ////////////////////////////////////////////////////////////////////
00103 const string &TexturePlacement::
00104 get_name() const {
00105   return _texture->get_name();
00106 }
00107 
00108 ////////////////////////////////////////////////////////////////////
00109 //     Function: TexturePlacement::get_texture
00110 //       Access: Public
00111 //  Description: Returns the texture that this placement represents.
00112 ////////////////////////////////////////////////////////////////////
00113 TextureImage *TexturePlacement::
00114 get_texture() const {
00115   return _texture;
00116 }
00117 
00118 ////////////////////////////////////////////////////////////////////
00119 //     Function: TexturePlacement::get_properties
00120 //       Access: Public
00121 //  Description: Returns the grouping properties of the image.
00122 ////////////////////////////////////////////////////////////////////
00123 const TextureProperties &TexturePlacement::
00124 get_properties() const {
00125   return _texture->get_properties();
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: TexturePlacement::get_group
00130 //       Access: Public
00131 //  Description: Returns the group that this placement represents.
00132 ////////////////////////////////////////////////////////////////////
00133 PaletteGroup *TexturePlacement::
00134 get_group() const {
00135   return _group;
00136 }
00137 
00138 ////////////////////////////////////////////////////////////////////
00139 //     Function: TexturePlacement::add_egg
00140 //       Access: Public
00141 //  Description: Records the fact that a particular egg file is using
00142 //               this particular TexturePlacement.
00143 ////////////////////////////////////////////////////////////////////
00144 void TexturePlacement::
00145 add_egg(TextureReference *reference) {
00146   reference->mark_egg_stale();
00147 
00148   // Turns out that turning these off is a bad idea, because it may
00149   // make us forget the size information halfway through processing.
00150   /*
00151   _has_uvs = false;
00152   _size_known = false;
00153   */
00154   _references.insert(reference);
00155 }
00156 
00157 ////////////////////////////////////////////////////////////////////
00158 //     Function: TexturePlacement::remove_egg
00159 //       Access: Public
00160 //  Description: Notes that a particular egg file is no longer using
00161 //               this particular TexturePlacement.
00162 ////////////////////////////////////////////////////////////////////
00163 void TexturePlacement::
00164 remove_egg(TextureReference *reference) {
00165   reference->mark_egg_stale();
00166   /*
00167     _has_uvs = false;
00168     _size_known = false;
00169   */
00170   _references.erase(reference);
00171 }
00172 
00173 ////////////////////////////////////////////////////////////////////
00174 //     Function: TexturePlacement::mark_eggs_stale
00175 //       Access: Public
00176 //  Description: Marks all the egg files that reference this placement
00177 //               stale.  Presumably this is called after moving the
00178 //               texture around in the palette or something.
00179 ////////////////////////////////////////////////////////////////////
00180 void TexturePlacement::
00181 mark_eggs_stale() {
00182   References::iterator ri;
00183   for (ri = _references.begin(); ri != _references.end(); ++ri) {
00184     TextureReference *reference = (*ri);
00185 
00186     reference->mark_egg_stale();
00187   }
00188 }
00189 
00190 ////////////////////////////////////////////////////////////////////
00191 //     Function: TexturePlacement::set_dest
00192 //       Access: Public
00193 //  Description: Sets the DestTextureImage that corresponds to this
00194 //               texture as it was copied to the install directory.
00195 ////////////////////////////////////////////////////////////////////
00196 void TexturePlacement::
00197 set_dest(DestTextureImage *dest) {
00198   _dest = dest;
00199 }
00200 
00201 ////////////////////////////////////////////////////////////////////
00202 //     Function: TexturePlacement::get_dest
00203 //       Access: Public
00204 //  Description: Returns the DestTextureImage that corresponds to this
00205 //               texture as it was copied to the install directory.
00206 ////////////////////////////////////////////////////////////////////
00207 DestTextureImage *TexturePlacement::
00208 get_dest() const {
00209   return _dest;
00210 }
00211 
00212 ////////////////////////////////////////////////////////////////////
00213 //     Function: TexturePlacement::determine_size
00214 //       Access: Public
00215 //  Description: Attempts to determine the appropriate size of the
00216 //               texture for the given placement.  This is based on
00217 //               the UV range of the egg files that reference the
00218 //               texture.  Returns true on success, or false if the
00219 //               texture size cannot be determined (e.g. the texture
00220 //               file is unknown).
00221 //
00222 //               After this returns true, get_x_size() and
00223 //               get_y_size() may safely be called.
00224 ////////////////////////////////////////////////////////////////////
00225 bool TexturePlacement::
00226 determine_size() {
00227   if (!_texture->is_size_known()) {
00228     // Too bad.
00229     force_replace();
00230     _omit_reason = OR_unknown;
00231     return false;
00232   }
00233 
00234   // This seems to be unnecessary (because of omit_solitary() and
00235   // not_solitary()), and in fact bitches the logic in omit_solitary()
00236   // and not_solitary() so that we call mark_egg_stale()
00237   // unnecessarily.
00238   /*
00239   if (_omit_reason == OR_solitary) {
00240     // If the texture was previously 'omitted' for being solitary, we
00241     // give it a second chance now.
00242     _omit_reason = OR_none;
00243   }
00244   */
00245 
00246   // Determine the actual minmax of the UV's in use, as well as
00247   // whether we should wrap or clamp.
00248   _has_uvs = false;
00249   _position._wrap_u = EggTexture::WM_clamp;
00250   _position._wrap_v = EggTexture::WM_clamp;
00251 
00252   LTexCoordd max_uv, min_uv;
00253 
00254   References::iterator ri;
00255   for (ri = _references.begin(); ri != _references.end(); ++ri) {
00256     TextureReference *reference = (*ri);
00257     if (reference->has_uvs()) {
00258       const LTexCoordd &n = reference->get_min_uv();
00259       const LTexCoordd &x = reference->get_max_uv();
00260 
00261       if (_has_uvs) {
00262         min_uv.set(min(min_uv[0], n[0]), min(min_uv[1], n[1]));
00263         max_uv.set(max(max_uv[0], x[0]), max(max_uv[1], x[1]));
00264       } else {
00265         min_uv = n;
00266         max_uv = x;
00267         _has_uvs = true;
00268       }
00269     }
00270 
00271     // If any reference repeats the texture, the texture repeats in
00272     // the palette.
00273     if (reference->get_wrap_u() == EggTexture::WM_repeat) {
00274       _position._wrap_u = EggTexture::WM_repeat;
00275     }
00276     if (reference->get_wrap_v() == EggTexture::WM_repeat) {
00277       _position._wrap_v = EggTexture::WM_repeat;
00278     }
00279   }
00280 
00281   // However, if the user specified an explicit wrap mode, allow it to
00282   // apply.
00283   if (_texture->get_txa_wrap_u() != EggTexture::WM_unspecified) {
00284     _position._wrap_u = _texture->get_txa_wrap_u();
00285   }
00286   if (_texture->get_txa_wrap_v() != EggTexture::WM_unspecified) {
00287     _position._wrap_v = _texture->get_txa_wrap_v();
00288   }
00289 
00290   if (!_has_uvs) {
00291     force_replace();
00292     _omit_reason = OR_unused;
00293     return false;
00294   }
00295 
00296   LTexCoordd rounded_min_uv = min_uv;
00297   LTexCoordd rounded_max_uv = max_uv;
00298 
00299   //cout << get_name() << endl;
00300 
00301   // If so requested, round the minmax out to the next _round_unit.
00302   // This cuts down on unnecessary resizing of textures within the
00303   // palettes as the egg references change in trivial amounts.
00304   //cout << "rounded_min_uv: " << rounded_min_uv << endl;
00305   //cout << "rounded_max_uv: " << rounded_max_uv << endl;
00306 
00307   if (pal->_round_uvs) {
00308     rounded_max_uv[0] =
00309       ceil((rounded_max_uv[0] - pal->_round_fuzz) / pal->_round_unit) *
00310       pal->_round_unit;
00311     rounded_max_uv[1] =
00312       ceil((rounded_max_uv[1] - pal->_round_fuzz) / pal->_round_unit) *
00313       pal->_round_unit;
00314 
00315     rounded_min_uv[0] =
00316       floor((rounded_min_uv[0] + pal->_round_fuzz) / pal->_round_unit) *
00317       pal->_round_unit;
00318     rounded_min_uv[1] =
00319       floor((rounded_min_uv[1] + pal->_round_fuzz) / pal->_round_unit) *
00320       pal->_round_unit;
00321 
00322     //cout << "after rounded_min_uv: " << rounded_min_uv << endl;
00323     //cout << "after rounded_max_uv: " << rounded_max_uv << endl;
00324   }
00325 
00326   // Now determine the size in pixels we require based on the UV's
00327   // that actually reference this texture.
00328   compute_size_from_uvs(rounded_min_uv, rounded_max_uv);
00329 
00330   // Now, can it be placed?
00331   if (_texture->get_omit()) {
00332     // Not if the user says it can't.
00333     force_replace();
00334     _omit_reason = OR_omitted;
00335 
00336   } else if (get_uv_area() > _texture->get_coverage_threshold()) {
00337     // If the texture repeats too many times, we can't place it.
00338     force_replace();
00339     _omit_reason = OR_coverage;
00340 
00341   } else if ((_position._x_size > pal->_pal_x_size ||
00342               _position._y_size > pal->_pal_y_size) ||
00343              (_position._x_size == pal->_pal_x_size &&
00344               _position._y_size == pal->_pal_y_size)) {
00345     // If the texture exceeds the size of an empty palette image in
00346     // either dimension, or if it exactly equals the size of an empty
00347     // palette image in both dimensions, we can't place it because
00348     // it's too big.
00349     force_replace();
00350     _omit_reason = OR_size;
00351 
00352   } else if (pal->_omit_everything && (_group->is_none_texture_swap())) {
00353     // If we're omitting everything, omit everything.
00354     force_replace();
00355     _omit_reason = OR_default_omit;
00356 
00357   } else if (_omit_reason == OR_omitted ||
00358              _omit_reason == OR_default_omit ||
00359              _omit_reason == OR_size ||
00360              _omit_reason == OR_coverage ||
00361              _omit_reason == OR_unknown) {
00362     // On the other hand, if the texture was previously omitted
00363     // explicitly, or because of its size or coverage, now it seems to
00364     // fit.
00365     force_replace();
00366     mark_eggs_stale();
00367     _omit_reason = OR_working;
00368 
00369   } else if (is_placed()) {
00370     // It *can* be placed.  If it was already placed previously, can
00371     // we leave it where it is?
00372 
00373     if (_position._x_size != _placed._x_size ||
00374         _position._y_size != _placed._y_size ||
00375         _position._min_uv[0] < _placed._min_uv[0] ||
00376         _position._min_uv[1] < _placed._min_uv[1] ||
00377         _position._max_uv[0] > _placed._max_uv[0] ||
00378         _position._max_uv[1] > _placed._max_uv[1]) {
00379       // If the texture was previously placed but is now the wrong
00380       // size, or if the area we need to cover is different, we need
00381       // to re-place it.
00382 
00383       // However, we make a special exception: if it would have fit
00384       // without rounding up the UV's, then screw rounding it up and
00385       // just leave it alone.
00386       if ((_position._x_size > _placed._x_size ||
00387            _position._y_size > _placed._y_size) &&
00388           pal->_round_uvs) {
00389         compute_size_from_uvs(min_uv, max_uv);
00390         if (_position._x_size <= _placed._x_size &&
00391             _position._y_size <= _placed._y_size &&
00392             _position._min_uv[0] >= _placed._min_uv[0] &&
00393             _position._min_uv[1] >= _placed._min_uv[1] &&
00394             _position._max_uv[0] <= _placed._max_uv[0] &&
00395             _position._max_uv[1] <= _placed._max_uv[1]) {
00396           // No problem!  It fits here, so leave well enough alone.
00397         } else {
00398           // That's not good enough either, so go back to rounding.
00399           compute_size_from_uvs(rounded_min_uv, rounded_max_uv);
00400           force_replace();
00401         }
00402       } else {
00403         force_replace();
00404       }
00405     }
00406 
00407     if (_position._wrap_u != _placed._wrap_u ||
00408         _position._wrap_v != _placed._wrap_v) {
00409       // The wrap mode properties have changed slightly.  We may or
00410       // may not need to re-place it, but we will need to update it.
00411       _is_filled = false;
00412       _placed._wrap_u = _position._wrap_u;
00413       _placed._wrap_v = _position._wrap_v;
00414     }
00415   }
00416 
00417   return true;
00418 }
00419 
00420 ////////////////////////////////////////////////////////////////////
00421 //     Function: TexturePlacement::is_size_known
00422 //       Access: Public
00423 //  Description: Returns true if the texture's size is known, false
00424 //               otherwise.  Usually this can only be false after
00425 //               determine_size() has been called there is something
00426 //               wrong with the texture (in which case the placement
00427 //               will automatically omit itself from the palette
00428 //               anyway).
00429 ////////////////////////////////////////////////////////////////////
00430 bool TexturePlacement::
00431 is_size_known() const {
00432   return _size_known;
00433 }
00434 
00435 ////////////////////////////////////////////////////////////////////
00436 //     Function: TexturePlacement::get_omit_reason
00437 //       Access: Public
00438 //  Description: Returns the reason the texture has been omitted from
00439 //               a palette image, or OR_none if it has not.
00440 ////////////////////////////////////////////////////////////////////
00441 OmitReason TexturePlacement::
00442 get_omit_reason() const {
00443   return _omit_reason;
00444 }
00445 
00446 ////////////////////////////////////////////////////////////////////
00447 //     Function: TexturePlacement::get_x_size
00448 //       Access: Public
00449 //  Description: Returns the size in the X dimension, in pixels, of
00450 //               the texture image as it must appear in the palette.
00451 //               This accounts for any growing or shrinking of the
00452 //               texture due to the UV coordinate range.
00453 ////////////////////////////////////////////////////////////////////
00454 int TexturePlacement::
00455 get_x_size() const {
00456   nassertr(_size_known, 0);
00457   return _position._x_size;
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////
00461 //     Function: TexturePlacement::get_y_size
00462 //       Access: Public
00463 //  Description: Returns the size in the Y dimension, in pixels, of
00464 //               the texture image as it must appear in the palette.
00465 //               This accounts for any growing or shrinking of the
00466 //               texture due to the UV coordinate range.
00467 ////////////////////////////////////////////////////////////////////
00468 int TexturePlacement::
00469 get_y_size() const {
00470   nassertr(_size_known, 0);
00471   return _position._y_size;
00472 }
00473 
00474 ////////////////////////////////////////////////////////////////////
00475 //     Function: TexturePlacement::get_uv_area
00476 //       Access: Public
00477 //  Description: Returns the total area of the rectangle occupied by
00478 //               the UV minmax box, in UV coordinates.  1.0 is the
00479 //               entire texture; values greater than 1 imply the
00480 //               texture repeats.
00481 ////////////////////////////////////////////////////////////////////
00482 double TexturePlacement::
00483 get_uv_area() const {
00484   if (!_has_uvs) {
00485     return 0.0;
00486   }
00487 
00488   LTexCoordd range = _position._max_uv - _position._min_uv;
00489   return range[0] * range[1];
00490 }
00491 
00492 ////////////////////////////////////////////////////////////////////
00493 //     Function: TexturePlacement::is_placed
00494 //       Access: Public
00495 //  Description: Returns true if the texture has been placed on a
00496 //               palette image, false otherwise.  This will generally
00497 //               be true if get_omit_reason() returns OR_none or
00498 //               OR_solitary and false otherwise.
00499 ////////////////////////////////////////////////////////////////////
00500 bool TexturePlacement::
00501 is_placed() const {
00502   return _image != (PaletteImage *)NULL;
00503 }
00504 
00505 ////////////////////////////////////////////////////////////////////
00506 //     Function: TexturePlacement::get_image
00507 //       Access: Public
00508 //  Description: Returns the particular PaletteImage on which the
00509 //               texture has been placed.
00510 ////////////////////////////////////////////////////////////////////
00511 PaletteImage *TexturePlacement::
00512 get_image() const {
00513   nassertr(is_placed(), (PaletteImage *)NULL);
00514   return _image;
00515 }
00516 
00517 ////////////////////////////////////////////////////////////////////
00518 //     Function: TexturePlacement::get_page
00519 //       Access: Public
00520 //  Description: Returns the particular PalettePage on which the
00521 //               texture has been placed.
00522 ////////////////////////////////////////////////////////////////////
00523 PalettePage *TexturePlacement::
00524 get_page() const {
00525   nassertr(is_placed(), (PalettePage *)NULL);
00526   return _image->get_page();
00527 }
00528 
00529 ////////////////////////////////////////////////////////////////////
00530 //     Function: TexturePlacement::get_placed_x
00531 //       Access: Public
00532 //  Description: Returns the X pixel at which the texture has been
00533 //               placed within its PaletteImage.  It is an error to
00534 //               call this unless is_placed() returns true.
00535 ////////////////////////////////////////////////////////////////////
00536 int TexturePlacement::
00537 get_placed_x() const {
00538   nassertr(is_placed(), 0);
00539   return _placed._x;
00540 }
00541 
00542 ////////////////////////////////////////////////////////////////////
00543 //     Function: TexturePlacement::get_placed_y
00544 //       Access: Public
00545 //  Description: Returns the Y pixel at which the texture has been
00546 //               placed within its PaletteImage.  It is an error to
00547 //               call this unless is_placed() returns true.
00548 ////////////////////////////////////////////////////////////////////
00549 int TexturePlacement::
00550 get_placed_y() const {
00551   nassertr(is_placed(), 0);
00552   return _placed._y;
00553 }
00554 
00555 ////////////////////////////////////////////////////////////////////
00556 //     Function: TexturePlacement::get_placed_x_size
00557 //       Access: Public
00558 //  Description: Returns the size in the X dimension, in pixels, of
00559 //               the texture image as it has been placed within the
00560 //               palette.
00561 ////////////////////////////////////////////////////////////////////
00562 int TexturePlacement::
00563 get_placed_x_size() const {
00564   nassertr(is_placed(), 0);
00565   return _placed._x_size;
00566 }
00567 
00568 ////////////////////////////////////////////////////////////////////
00569 //     Function: TexturePlacement::get_placed_y_size
00570 //       Access: Public
00571 //  Description: Returns the size in the Y dimension, in pixels, of
00572 //               the texture image as it has been placed within the
00573 //               palette.
00574 ////////////////////////////////////////////////////////////////////
00575 int TexturePlacement::
00576 get_placed_y_size() const {
00577   nassertr(is_placed(), 0);
00578   return _placed._y_size;
00579 }
00580 
00581 ////////////////////////////////////////////////////////////////////
00582 //     Function: TexturePlacement::get_placed_uv_area
00583 //       Access: Public
00584 //  Description: Returns the total area of the rectangle occupied by
00585 //               the UV minmax box, as it has been placed.  See also
00586 //               get_uv_area().
00587 ////////////////////////////////////////////////////////////////////
00588 double TexturePlacement::
00589 get_placed_uv_area() const {
00590   nassertr(is_placed(), 0);
00591   LTexCoordd range = _placed._max_uv - _placed._min_uv;
00592   return range[0] * range[1];
00593 }
00594 
00595 ////////////////////////////////////////////////////////////////////
00596 //     Function: TexturePlacement::place_at
00597 //       Access: Public
00598 //  Description: Assigns the texture to a particular position within
00599 //               the indicated PaletteImage.  It is an error to call
00600 //               this if the texture has already been placed
00601 //               elsewhere.
00602 ////////////////////////////////////////////////////////////////////
00603 void TexturePlacement::
00604 place_at(PaletteImage *image, int x, int y) {
00605   nassertv(!is_placed());
00606   nassertv(_size_known);
00607 
00608   _image = image;
00609   _is_filled = false;
00610   _position._x = x;
00611   _position._y = y;
00612   _placed = _position;
00613   _omit_reason = OR_none;
00614 }
00615 
00616 ////////////////////////////////////////////////////////////////////
00617 //     Function: TexturePlacement::force_replace
00618 //       Access: Public
00619 //  Description: Removes the texture from its particular PaletteImage,
00620 //               but does not remove it from the PaletteGroup.  It
00621 //               will be re-placed when the PaletteGroup::place_all()
00622 //               is called.
00623 ////////////////////////////////////////////////////////////////////
00624 void TexturePlacement::
00625 force_replace() {
00626   if (_image != (PaletteImage *)NULL) {
00627     _image->unplace(this);
00628     _image = (PaletteImage *)NULL;
00629   }
00630   if (_omit_reason == OR_none) {
00631     mark_eggs_stale();
00632   }
00633   _omit_reason = OR_working;
00634 }
00635 
00636 ////////////////////////////////////////////////////////////////////
00637 //     Function: TexturePlacement::omit_solitary
00638 //       Access: Public
00639 //  Description: Sets the omit reason (returned by get_omit()) to
00640 //               OR_solitary, indicating that the palettized version
00641 //               of the texture should not be used because it is the
00642 //               only texture on a PaletteImage.  However, the texture
00643 //               is still considered placed, and is_placed() will
00644 //               return true.
00645 ////////////////////////////////////////////////////////////////////
00646 void TexturePlacement::
00647 omit_solitary() {
00648   nassertv(is_placed());
00649   if (_omit_reason != OR_solitary) {
00650     mark_eggs_stale();
00651     _omit_reason = OR_solitary;
00652   }
00653 }
00654 
00655 ////////////////////////////////////////////////////////////////////
00656 //     Function: TexturePlacement::not_solitary
00657 //       Access: Public
00658 //  Description: Indicates that the texture, formerly indicated as
00659 //               solitary, is now no longer.
00660 ////////////////////////////////////////////////////////////////////
00661 void TexturePlacement::
00662 not_solitary() {
00663   nassertv(is_placed());
00664   if (_omit_reason != OR_none) {
00665     mark_eggs_stale();
00666     _omit_reason = OR_none;
00667   }
00668 }
00669 
00670 ////////////////////////////////////////////////////////////////////
00671 //     Function: TexturePlacement::intersects
00672 //       Access: Public
00673 //  Description: Returns true if the particular position this texture
00674 //               has been assigned to overlaps the rectangle whose
00675 //               top left corner is at x, y and whose size is given by
00676 //               x_size, y_size, or false otherwise.
00677 ////////////////////////////////////////////////////////////////////
00678 bool TexturePlacement::
00679 intersects(int x, int y, int x_size, int y_size) {
00680   nassertr(is_placed(), false);
00681 
00682   int hright = x + x_size;
00683   int hbot = y + y_size;
00684 
00685   int mright = _placed._x + _placed._x_size;
00686   int mbot = _placed._y + _placed._y_size;
00687 
00688   return !(x >= mright || hright <= _placed._x ||
00689            y >= mbot || hbot <= _placed._y);
00690 }
00691 
00692 ////////////////////////////////////////////////////////////////////
00693 //     Function: TexturePlacement::compute_tex_matrix
00694 //       Access: Public
00695 //  Description: Stores in the indicated matrix the appropriate
00696 //               texture matrix transform for the new placement of the
00697 //               texture.
00698 ////////////////////////////////////////////////////////////////////
00699 void TexturePlacement::
00700 compute_tex_matrix(LMatrix3d &transform) {
00701   nassertv(is_placed());
00702 
00703   LMatrix3d source_uvs = LMatrix3d::ident_mat();
00704 
00705   LTexCoordd range = _placed._max_uv - _placed._min_uv;
00706   if (range[0] != 0.0 && range[1] != 0.0) {
00707     source_uvs =
00708       LMatrix3d::translate_mat(-_placed._min_uv) *
00709       LMatrix3d::scale_mat(1.0 / range[0], 1.0 / range[1]);
00710   }
00711 
00712   int top = _placed._y + _placed._margin;
00713   int left = _placed._x + _placed._margin;
00714   int x_size = _placed._x_size - _placed._margin * 2;
00715   int y_size = _placed._y_size - _placed._margin * 2;
00716 
00717   int bottom = top + y_size;
00718   int pal_x_size = _image->get_x_size();
00719   int pal_y_size = _image->get_y_size();
00720 
00721   LVecBase2d t((double)left / (double)pal_x_size,
00722                (double)(pal_y_size - bottom) / (double)pal_y_size);
00723   LVecBase2d s((double)x_size / (double)pal_x_size,
00724                (double)y_size / (double)pal_y_size);
00725 
00726   LMatrix3d dest_uvs
00727     (s[0],  0.0,  0.0,
00728      0.0, s[1],  0.0,
00729      t[0], t[1],  1.0);
00730 
00731   transform = source_uvs * dest_uvs;
00732 }
00733 
00734 ////////////////////////////////////////////////////////////////////
00735 //     Function: TexturePlacement::write_placed
00736 //       Access: Public
00737 //  Description: Writes the placement position information on a line
00738 //               by itself.
00739 ////////////////////////////////////////////////////////////////////
00740 void TexturePlacement::
00741 write_placed(ostream &out, int indent_level) {
00742   indent(out, indent_level)
00743     << get_texture()->get_name();
00744 
00745   if (is_placed()) {
00746     out << " at "
00747         << get_placed_x() << " " << get_placed_y() << " to "
00748         << get_placed_x() + get_placed_x_size() << " "
00749         << get_placed_y() + get_placed_y_size() << " (coverage "
00750         << get_placed_uv_area() << ")";
00751 
00752     if (_placed._wrap_u != EggTexture::WM_unspecified ||
00753         _placed._wrap_v != EggTexture::WM_unspecified) {
00754       if (_placed._wrap_u != _placed._wrap_v) {
00755         out << " (" << _placed._wrap_u << ", " << _placed._wrap_v << ")";
00756       } else {
00757         out << " " << _placed._wrap_u;
00758       }
00759     }
00760     out << "\n";
00761   } else {
00762     out << " not yet placed.\n";
00763   }
00764 };
00765 
00766 ////////////////////////////////////////////////////////////////////
00767 //     Function: TexturePlacement::is_filled
00768 //       Access: Public
00769 //  Description: Returns true if the texture has been filled
00770 //               (i.e. fill_image() has been called) since it was
00771 //               placed.
00772 ////////////////////////////////////////////////////////////////////
00773 bool TexturePlacement::
00774 is_filled() const {
00775   return _is_filled;
00776 }
00777 
00778 ////////////////////////////////////////////////////////////////////
00779 //     Function: TexturePlacement::mark_unfilled
00780 //       Access: Public
00781 //  Description: Marks the texture as unfilled, so that it will need
00782 //               to be copied into the palette image again.
00783 ////////////////////////////////////////////////////////////////////
00784 void TexturePlacement::
00785 mark_unfilled() {
00786   _is_filled = false;
00787 }
00788 
00789 ////////////////////////////////////////////////////////////////////
00790 //     Function: TexturePlacement::fill_image
00791 //       Access: Public
00792 //  Description: Fills in the rectangle of the palette image
00793 //               represented by the texture placement with the image
00794 //               pixels.
00795 ////////////////////////////////////////////////////////////////////
00796 void TexturePlacement::
00797 fill_image(PNMImage &image) {
00798   nassertv(is_placed());
00799 
00800   _is_filled = true;
00801 
00802   // We determine the pixels to place the source image at by
00803   // transforming the unit texture box: the upper-left and lower-right
00804   // corners.  These corners, in the final texture coordinate space,
00805   // represent where on the palette image the original texture should
00806   // be located.
00807 
00808   LMatrix3d transform;
00809   compute_tex_matrix(transform);
00810   LTexCoordd ul = LTexCoordd(0.0, 1.0) * transform;
00811   LTexCoordd lr = LTexCoordd(1.0, 0.0) * transform;
00812 
00813   // Now we convert those texture coordinates back to pixel units.
00814   int pal_x_size = _image->get_x_size();
00815   int pal_y_size = _image->get_y_size();
00816 
00817   int top = (int)floor((1.0 - ul[1]) * pal_y_size + 0.5);
00818   int left = (int)floor(ul[0] * pal_x_size + 0.5);
00819   int bottom = (int)floor((1.0 - lr[1]) * pal_y_size + 0.5);
00820   int right = (int)floor(lr[0] * pal_x_size + 0.5);
00821 
00822   // And now we can determine the size to scale the image to based on
00823   // that.  This may not be the same as texture->size() because of
00824   // margins.
00825   int x_size = right - left;
00826   int y_size = bottom - top;
00827   nassertv(x_size >= 0 && y_size >= 0);
00828 
00829   // Now we get a PNMImage that represents the source texture at that
00830   // size.
00831   const PNMImage &source_full = _texture->read_source_image();
00832   if (!source_full.is_valid()) {
00833     flag_error_image(image);
00834     return;
00835   }
00836 
00837   PNMImage source(x_size, y_size, source_full.get_num_channels(),
00838                   source_full.get_maxval());
00839   source.quick_filter_from(source_full);
00840 
00841   bool alpha = image.has_alpha();
00842   bool source_alpha = source.has_alpha();
00843 
00844   // Now copy the pixels.  We do this by walking through the
00845   // rectangular region on the palette image that we have reserved for
00846   // this texture; for each pixel in this region, we determine its
00847   // appropriate color based on its relation to the actual texture
00848   // image location (determined above), and on whether the texture
00849   // wraps or clamps.
00850   for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) {
00851     int sy = y - top;
00852 
00853     if (_placed._wrap_v == EggTexture::WM_clamp) {
00854       // Clamp at [0, y_size).
00855       sy = max(min(sy, y_size - 1), 0);
00856 
00857     } else {
00858       // Wrap: sign-independent modulo.
00859       sy = (sy < 0) ? y_size - 1 - ((-sy - 1) % y_size) : sy % y_size;
00860     }
00861 
00862     for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) {
00863       int sx = x - left;
00864 
00865       if (_placed._wrap_u == EggTexture::WM_clamp) {
00866         // Clamp at [0, x_size).
00867         sx = max(min(sx, x_size - 1), 0);
00868 
00869       } else {
00870         // Wrap: sign-independent modulo.
00871         sx = (sx < 0) ? x_size - 1 - ((-sx - 1) % x_size) : sx % x_size;
00872       }
00873 
00874       image.set_xel(x, y, source.get_xel(sx, sy));
00875       if (alpha) {
00876         if (source_alpha) {
00877           image.set_alpha(x, y, source.get_alpha(sx, sy));
00878         } else {
00879           image.set_alpha(x, y, 1.0);
00880         }
00881       }
00882     }
00883   }
00884 
00885   _texture->release_source_image();
00886 }
00887 
00888 
00889 ////////////////////////////////////////////////////////////////////
00890 //     Function: TexturePlacement::fill_swapped_image
00891 //       Access: Public
00892 //  Description: Fills in the rectangle of the swapped palette image
00893 //               represented by the texture placement with the image
00894 //               pixels.
00895 ////////////////////////////////////////////////////////////////////
00896 void TexturePlacement::
00897 fill_swapped_image(PNMImage &image, int index) {
00898   nassertv(is_placed());
00899 
00900   _is_filled = true;
00901 
00902   // We determine the pixels to place the source image at by
00903   // transforming the unit texture box: the upper-left and lower-right
00904   // corners.  These corners, in the final texture coordinate space,
00905   // represent where on the palette image the original texture should
00906   // be located.
00907 
00908   LMatrix3d transform;
00909   compute_tex_matrix(transform);
00910   LTexCoordd ul = LTexCoordd(0.0, 1.0) * transform;
00911   LTexCoordd lr = LTexCoordd(1.0, 0.0) * transform;
00912 
00913   // Now we convert those texture coordinates back to pixel units.
00914   int pal_x_size = _image->get_x_size();
00915   int pal_y_size = _image->get_y_size();
00916 
00917   int top = (int)floor((1.0 - ul[1]) * pal_y_size + 0.5);
00918   int left = (int)floor(ul[0] * pal_x_size + 0.5);
00919   int bottom = (int)floor((1.0 - lr[1]) * pal_y_size + 0.5);
00920   int right = (int)floor(lr[0] * pal_x_size + 0.5);
00921 
00922   // And now we can determine the size to scale the image to based on
00923   // that.  This may not be the same as texture->size() because of
00924   // margins.
00925   int x_size = right - left;
00926   int y_size = bottom - top;
00927   nassertv(x_size >= 0 && y_size >= 0);
00928 
00929   // Now we get a PNMImage that represents the swapped texture at that
00930   // size.
00931   TextureSwaps::iterator tsi;
00932   tsi = _textureSwaps.begin() + index;
00933   TextureImage *swapTexture = (*tsi);
00934   const PNMImage &source_full = swapTexture->read_source_image();
00935   if (!source_full.is_valid()) {
00936     flag_error_image(image);
00937     return;
00938   }
00939 
00940   PNMImage source(x_size, y_size, source_full.get_num_channels(),
00941                   source_full.get_maxval());
00942   source.quick_filter_from(source_full);
00943 
00944   bool alpha = image.has_alpha();
00945   bool source_alpha = source.has_alpha();
00946 
00947   // Now copy the pixels.  We do this by walking through the
00948   // rectangular region on the palette image that we have reserved for
00949   // this texture; for each pixel in this region, we determine its
00950   // appropriate color based on its relation to the actual texture
00951   // image location (determined above), and on whether the texture
00952   // wraps or clamps.
00953   for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) {
00954     int sy = y - top;
00955 
00956     if (_placed._wrap_v == EggTexture::WM_clamp) {
00957       // Clamp at [0, y_size).
00958       sy = max(min(sy, y_size - 1), 0);
00959 
00960     } else {
00961       // Wrap: sign-independent modulo.
00962       sy = (sy < 0) ? y_size - 1 - ((-sy - 1) % y_size) : sy % y_size;
00963     }
00964 
00965     for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) {
00966       int sx = x - left;
00967 
00968       if (_placed._wrap_u == EggTexture::WM_clamp) {
00969         // Clamp at [0, x_size).
00970         sx = max(min(sx, x_size - 1), 0);
00971 
00972       } else {
00973         // Wrap: sign-independent modulo.
00974         sx = (sx < 0) ? x_size - 1 - ((-sx - 1) % x_size) : sx % x_size;
00975       }
00976 
00977       image.set_xel(x, y, source.get_xel(sx, sy));
00978       if (alpha) {
00979         if (source_alpha) {
00980           image.set_alpha(x, y, source.get_alpha(sx, sy));
00981         } else {
00982           image.set_alpha(x, y, 1.0);
00983         }
00984       }
00985     }
00986   }
00987 
00988   swapTexture->release_source_image();
00989 }
00990 
00991 ////////////////////////////////////////////////////////////////////
00992 //     Function: TexturePlacement::flag_error_image
00993 //       Access: Public
00994 //  Description: Sets the rectangle of the palette image
00995 //               represented by the texture placement to red, to
00996 //               represent a missing texture.
00997 ////////////////////////////////////////////////////////////////////
00998 void TexturePlacement::
00999 flag_error_image(PNMImage &image) {
01000   nassertv(is_placed());
01001   for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) {
01002     for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) {
01003       image.set_xel_val(x, y, 1, 0, 0);
01004     }
01005   }
01006   if (image.has_alpha()) {
01007     for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) {
01008       for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) {
01009         image.set_alpha_val(x, y, 1);
01010       }
01011     }
01012   }
01013 }
01014 
01015 ////////////////////////////////////////////////////////////////////
01016 //     Function: TexturePlacement::compute_size_from_uvs
01017 //       Access: Private
01018 //  Description: A support function for determine_size(), this
01019 //               computes the appropriate size of the texture in
01020 //               pixels based on the UV coverage (as well as on the
01021 //               size of the source texture).
01022 ////////////////////////////////////////////////////////////////////
01023 void TexturePlacement::
01024 compute_size_from_uvs(const LTexCoordd &min_uv, const LTexCoordd &max_uv) {
01025   _position._min_uv = min_uv;
01026   _position._max_uv = max_uv;
01027 
01028   LTexCoordd range = _position._max_uv - _position._min_uv;
01029   //cout << "range: " << range << endl;
01030 
01031   //cout << "_x_size texture: " << _texture->get_x_size() << endl;
01032   //cout << "_y_size texture: " << _texture->get_y_size() << endl;
01033 
01034   _position._x_size = (int)floor(_texture->get_x_size() * range[0] + 0.5);
01035   _position._y_size = (int)floor(_texture->get_y_size() * range[1] + 0.5);
01036 
01037   //cout << "_x_size: " << _position._x_size << endl;
01038   //cout << "_y_size: " << _position._y_size << endl;
01039 
01040   // We arbitrarily require at least four pixels in each dimension.
01041   // Fewer than this may be asking for trouble.
01042   _position._x_size = max(_position._x_size, 4);
01043   _position._y_size = max(_position._y_size, 4);
01044 
01045   if(get_group()->has_margin_override()) {
01046     _position._margin = get_group()->get_margin_override();
01047   } else {
01048     _position._margin = _texture->get_margin();
01049   }
01050   //cout << "margin: " << _position._margin << endl;
01051   
01052   // Normally, we have interior margins, but if the image size is too
01053   // small--i.e. the margin size is too great a percentage of the
01054   // image size--we'll make them exterior margins so as not to overly
01055   // degrade the quality of the image.
01056   if ((double)_position._margin / (double)_position._x_size > 0.10) {
01057     _position._x_size += _position._margin * 2;
01058   }
01059   if ((double)_position._margin / (double)_position._y_size > 0.10) {
01060     _position._y_size += _position._margin * 2;
01061   }
01062 
01063   _size_known = true;
01064 }
01065 
01066 
01067 
01068 ////////////////////////////////////////////////////////////////////
01069 //     Function: TexturePlacement::register_with_read_factory
01070 //       Access: Public, Static
01071 //  Description: Registers the current object as something that can be
01072 //               read from a Bam file.
01073 ////////////////////////////////////////////////////////////////////
01074 void TexturePlacement::
01075 register_with_read_factory() {
01076   BamReader::get_factory()->
01077     register_factory(get_class_type(), make_TexturePlacement);
01078 }
01079 
01080 ////////////////////////////////////////////////////////////////////
01081 //     Function: TexturePlacement::write_datagram
01082 //       Access: Public, Virtual
01083 //  Description: Fills the indicated datagram up with a binary
01084 //               representation of the current object, in preparation
01085 //               for writing to a Bam file.
01086 ////////////////////////////////////////////////////////////////////
01087 void TexturePlacement::
01088 write_datagram(BamWriter *writer, Datagram &datagram) {
01089   TypedWritable::write_datagram(writer, datagram);
01090   writer->write_pointer(datagram, _texture);
01091   writer->write_pointer(datagram, _group);
01092   writer->write_pointer(datagram, _image);
01093   writer->write_pointer(datagram, _dest);
01094 
01095   datagram.add_bool(_has_uvs);
01096   datagram.add_bool(_size_known);
01097   _position.write_datagram(writer, datagram);
01098 
01099   datagram.add_bool(_is_filled);
01100   _placed.write_datagram(writer, datagram);
01101   datagram.add_int32((int)_omit_reason);
01102 
01103   datagram.add_int32(_references.size());
01104   References::const_iterator ri;
01105   for (ri = _references.begin(); ri != _references.end(); ++ri) {
01106     writer->write_pointer(datagram, (*ri));
01107   }
01108 
01109   datagram.add_int32(_textureSwaps.size());
01110   TextureSwaps::const_iterator tsi;
01111   for (tsi = _textureSwaps.begin(); tsi != _textureSwaps.end(); ++tsi) {
01112     writer->write_pointer(datagram, (*tsi));
01113   }
01114 
01115 }
01116 
01117 ////////////////////////////////////////////////////////////////////
01118 //     Function: TexturePlacement::complete_pointers
01119 //       Access: Public, Virtual
01120 //  Description: Called after the object is otherwise completely read
01121 //               from a Bam file, this function's job is to store the
01122 //               pointers that were retrieved from the Bam file for
01123 //               each pointer object written.  The return value is the
01124 //               number of pointers processed from the list.
01125 ////////////////////////////////////////////////////////////////////
01126 int TexturePlacement::
01127 complete_pointers(TypedWritable **p_list, BamReader *manager) {
01128   int index = TypedWritable::complete_pointers(p_list, manager);
01129 
01130   if (p_list[index] != (TypedWritable *)NULL) {
01131     DCAST_INTO_R(_texture, p_list[index], index);
01132   }
01133   index++;
01134 
01135   if (p_list[index] != (TypedWritable *)NULL) {
01136     DCAST_INTO_R(_group, p_list[index], index);
01137   }
01138   index++;
01139 
01140   if (p_list[index] != (TypedWritable *)NULL) {
01141     DCAST_INTO_R(_image, p_list[index], index);
01142   }
01143   index++;
01144 
01145   if (p_list[index] != (TypedWritable *)NULL) {
01146     DCAST_INTO_R(_dest, p_list[index], index);
01147   }
01148   index++;
01149 
01150   int i;
01151   for (i = 0; i < _num_references; i++) {
01152     TextureReference *reference;
01153     DCAST_INTO_R(reference, p_list[index], index);
01154     _references.insert(reference);
01155     index++;
01156   }
01157 
01158   for (i = 0; i < _num_textureSwaps; i++) {
01159     TextureImage *swapTexture;
01160     DCAST_INTO_R(swapTexture, p_list[index], index);
01161     _textureSwaps.push_back(swapTexture);
01162     index++;
01163   }
01164 
01165   return index;
01166 }
01167 
01168 ////////////////////////////////////////////////////////////////////
01169 //     Function: TexturePlacement::make_TexturePlacement
01170 //       Access: Protected
01171 //  Description: This method is called by the BamReader when an object
01172 //               of this type is encountered in a Bam file; it should
01173 //               allocate and return a new object with all the data
01174 //               read.
01175 ////////////////////////////////////////////////////////////////////
01176 TypedWritable* TexturePlacement::
01177 make_TexturePlacement(const FactoryParams &params) {
01178   TexturePlacement *me = new TexturePlacement;
01179   DatagramIterator scan;
01180   BamReader *manager;
01181 
01182   parse_params(params, scan, manager);
01183   me->fillin(scan, manager);
01184   return me;
01185 }
01186 
01187 ////////////////////////////////////////////////////////////////////
01188 //     Function: TexturePlacement::fillin
01189 //       Access: Protected
01190 //  Description: Reads the binary data from the given datagram
01191 //               iterator, which was written by a previous call to
01192 //               write_datagram().
01193 ////////////////////////////////////////////////////////////////////
01194 void TexturePlacement::
01195 fillin(DatagramIterator &scan, BamReader *manager) {
01196   TypedWritable::fillin(scan, manager);
01197 
01198   manager->read_pointer(scan);  // _texture
01199   manager->read_pointer(scan);  // _group
01200   manager->read_pointer(scan);  // _image
01201   manager->read_pointer(scan);  // _dest
01202 
01203   _has_uvs = scan.get_bool();
01204   _size_known = scan.get_bool();
01205   _position.fillin(scan, manager);
01206 
01207   _is_filled = scan.get_bool();
01208   _placed.fillin(scan, manager);
01209   _omit_reason = (OmitReason)scan.get_int32();
01210 
01211   _num_references = scan.get_int32();
01212   manager->read_pointers(scan, _num_references);
01213 
01214   if (Palettizer::_read_pi_version >= 20) {
01215     _num_textureSwaps = scan.get_int32();
01216   } else {
01217     _num_textureSwaps = 0;
01218   }
01219   manager->read_pointers(scan, _num_textureSwaps);
01220 }
01221 
01222 
01223 ////////////////////////////////////////////////////////////////////
01224 //     Function: SortPlacementBySize::Function Operator
01225 //       Access: Public
01226 //  Description: Compares two TexturePlacement objects and returns
01227 //               true if the first one is bigger than the second one,
01228 //               false otherwise.
01229 ////////////////////////////////////////////////////////////////////
01230 bool SortPlacementBySize::
01231 operator ()(TexturePlacement *a, TexturePlacement *b) const {
01232   if (a->get_y_size() < b->get_y_size()) {
01233     return false;
01234 
01235   } else if (b->get_y_size() < a->get_y_size()) {
01236     return true;
01237 
01238   } else if (a->get_x_size() < b->get_x_size()) {
01239     return false;
01240 
01241   } else if (b->get_x_size() < a->get_x_size()) {
01242     return true;
01243   } else if (a->get_name() < b->get_name()) {
01244     //use this fall through case to let alphabetically smaller textures show up first
01245     return true;
01246   }
01247   
01248   return false;
01249 }