Panda3D
|
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 ¶ms) { 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 }