Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/egg-palettize/texturePlacement.cxx

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

Generated on Fri May 2 03:18:13 2003 for Panda-Tool by doxygen1.3