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

pandatool/src/egg-palettize/txaLine.cxx

Go to the documentation of this file.
00001 // Filename: txaLine.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 "txaLine.h"
00020 #include "pal_string_utils.h"
00021 #include "eggFile.h"
00022 #include "palettizer.h"
00023 #include "textureImage.h"
00024 #include "sourceTextureImage.h"
00025 #include "paletteGroup.h"
00026 
00027 #include <notify.h>
00028 #include <pnmFileType.h>
00029 
00030 ////////////////////////////////////////////////////////////////////
00031 //     Function: TxaLine::Constructor
00032 //       Access: Public
00033 //  Description:
00034 ////////////////////////////////////////////////////////////////////
00035 TxaLine::
00036 TxaLine() {
00037   _size_type = ST_none;
00038   _scale = 0.0;
00039   _x_size = 0;
00040   _y_size = 0;
00041   _aniso_degree = 0;
00042   _num_channels = 0;
00043   _format = EggTexture::F_unspecified;
00044   _force_format = false;
00045   _generic_format = false;
00046   _alpha_mode = EggRenderMode::AM_unspecified;
00047   _got_margin = false;
00048   _margin = 0;
00049   _got_coverage_threshold = false;
00050   _coverage_threshold = 0.0;
00051   _color_type = (PNMFileType *)NULL;
00052   _alpha_type = (PNMFileType *)NULL;
00053 }
00054 
00055 ////////////////////////////////////////////////////////////////////
00056 //     Function: TxaLine::parse
00057 //       Access: Public
00058 //  Description: Accepts a string that defines a line of the .txa file
00059 //               and parses it into its constinuent parts.  Returns
00060 //               true if successful, false on error.
00061 ////////////////////////////////////////////////////////////////////
00062 bool TxaLine::
00063 parse(const string &line) {
00064   size_t colon = line.find(':');
00065   if (colon == string::npos) {
00066     nout << "Colon required.\n";
00067     return false;
00068   }
00069 
00070   // Chop up the first part of the string (preceding the colon) into
00071   // its individual words.  These are patterns to match.
00072   vector_string words;
00073   extract_words(line.substr(0, colon), words);
00074 
00075   vector_string::iterator wi;
00076   for (wi = words.begin(); wi != words.end(); ++wi) {
00077     string word = (*wi);
00078 
00079     // If the pattern ends in the string ".egg", and only if it ends
00080     // in this string, it is deemed an egg pattern and will only be
00081     // tested against egg files.  If it ends in anything else, it is
00082     // deemed a texture pattern and will only be tested against
00083     // textures.
00084     if (word.length() > 4 && word.substr(word.length() - 4) == ".egg") {
00085       _egg_patterns.push_back(GlobPattern(word));
00086 
00087     } else {
00088       // However, the filename extension, if any, is stripped off
00089       // because the texture key names nowadays don't include them.
00090       size_t dot = word.rfind('.');
00091       if (dot != string::npos) {
00092         word = word.substr(0, dot);
00093       }
00094       _texture_patterns.push_back(GlobPattern(word));
00095     }
00096   }
00097 
00098   if (_egg_patterns.empty() && _texture_patterns.empty()) {
00099     nout << "No texture or egg filenames given.\n";
00100     return false;
00101   }
00102 
00103   // Now chop up the rest of the string (following the colon) into its
00104   // individual words.  These are keywords and size indications.
00105   words.clear();
00106   extract_words(line.substr(colon + 1), words);
00107 
00108   wi = words.begin();
00109   while (wi != words.end()) {
00110     const string &word = *wi;
00111     nassertr(!word.empty(), false);
00112 
00113     if (isdigit(word[0])) {
00114       // This is either a new size or a scale percentage.
00115       if (_size_type != ST_none) {
00116         nout << "Invalid repeated size request: " << word << "\n";
00117         return false;
00118       }
00119       if (word[word.length() - 1] == '%') {
00120         // It's a scale percentage!
00121         _size_type = ST_scale;
00122 
00123         string tail;
00124         _scale = string_to_double(word, tail);
00125         if (!(tail == "%")) {
00126           // This is an invalid number.
00127           return false;
00128         }
00129         ++wi;
00130 
00131       } else {
00132         // Collect a number of consecutive numeric fields.
00133         pvector<int> numbers;
00134         while (wi != words.end() && isdigit((*wi)[0])) {
00135           const string &word = *wi;
00136           int num;
00137           if (!string_to_int(word, num)) {
00138             nout << "Invalid size: " << word << "\n";
00139             return false;
00140           }
00141           numbers.push_back(num);
00142           ++wi;
00143         }
00144         if (numbers.size() < 2) {
00145           nout << "At least two size numbers must be given, or a percent sign used to indicate scaling.\n";
00146           return false;
00147 
00148         } else if (numbers.size() == 2) {
00149           _size_type = ST_explicit_2;
00150           _x_size = numbers[0];
00151           _y_size = numbers[1];
00152 
00153         } else if (numbers.size() == 3) {
00154           _size_type = ST_explicit_3;
00155           _x_size = numbers[0];
00156           _y_size = numbers[1];
00157           _num_channels = numbers[2];
00158 
00159         } else {
00160           nout << "Too many size numbers given.\n";
00161           return false;
00162         }
00163       }
00164 
00165     } else {
00166       // The word does not begin with a digit; therefore it's either a
00167       // keyword or an image file type request.
00168       if (word == "omit") {
00169         _keywords.push_back(KW_omit);
00170 
00171       } else if (word == "nearest") {
00172         _keywords.push_back(KW_nearest);
00173 
00174       } else if (word == "linear") {
00175         _keywords.push_back(KW_linear);
00176 
00177       } else if (word == "mipmap") {
00178         _keywords.push_back(KW_mipmap);
00179 
00180       } else if (word == "cont") {
00181         _keywords.push_back(KW_cont);
00182 
00183       } else if (word == "margin") {
00184         ++wi;
00185         if (wi == words.end()) {
00186           nout << "Argument required for 'margin'.\n";
00187           return false;
00188         }
00189 
00190         const string &arg = (*wi);
00191         if (!string_to_int(arg, _margin)) {
00192           nout << "Not an integer: " << arg << "\n";
00193           return false;
00194         }
00195         if (_margin < 0) {
00196           nout << "Invalid margin: " << _margin << "\n";
00197           return false;
00198         }
00199         _got_margin = true;
00200 
00201       } else if (word == "aniso") {
00202         ++wi;
00203         if (wi == words.end()) {
00204           nout << "Integer argument required for 'aniso'.\n";
00205           return false;
00206         }
00207 
00208         const string &arg = (*wi);
00209         if (!string_to_int(arg, _aniso_degree)) {
00210           nout << "Not an integer: " << arg << "\n";
00211           return false;
00212         }
00213         if ((_aniso_degree < 2) || (_aniso_degree > 16)) {
00214           // make it an error to specific degree 0 or 1, which means no anisotropy so it's probably an input mistake
00215           nout << "Invalid anistropic degree (range is 2-16): " << _aniso_degree << "\n";
00216           return false;
00217         }
00218 
00219         _keywords.push_back(KW_anisotropic);
00220 
00221       } else if (word == "coverage") {
00222         ++wi;
00223         if (wi == words.end()) {
00224           nout << "Argument required for 'coverage'.\n";
00225           return false;
00226         }
00227 
00228         const string &arg = (*wi);
00229         if (!string_to_double(arg, _coverage_threshold)) {
00230           nout << "Not a number: " << arg << "\n";
00231           return false;
00232         }
00233         if (_coverage_threshold <= 0.0) {
00234           nout << "Invalid coverage threshold: " << _coverage_threshold << "\n";
00235           return false;
00236         }
00237         _got_coverage_threshold = true;
00238 
00239       } else if (word.substr(0, 6) == "force-") {
00240         // Force a particular format, despite the number of channels
00241         // in the image.
00242         string format_name = word.substr(6);
00243         EggTexture::Format format = EggTexture::string_format(format_name);
00244         if (format != EggTexture::F_unspecified) {
00245           _format = format;
00246           _force_format = true;
00247         } else {
00248           nout << "Unknown image format: " << format_name << "\n";
00249           return false;
00250         }
00251 
00252       } else if (word == "generic") {
00253         // Genericize the image format by replacing bitcount-specific
00254         // formats with their generic equivalents, e.g. rgba8 becomes
00255         // rgba.
00256         _generic_format = true;
00257 
00258       } else {
00259         // Maybe it's a group name.
00260         PaletteGroup *group = pal->test_palette_group(word);
00261         if (group != (PaletteGroup *)NULL) {
00262           _palette_groups.insert(group);
00263 
00264         } else {
00265           // Maybe it's a format name.  This suggests an image format,
00266           // but may be overridden to reflect the number of channels in
00267           // the image.
00268           EggTexture::Format format = EggTexture::string_format(word);
00269           if (format != EggTexture::F_unspecified) {
00270             if (!_force_format) {
00271               _format = format;
00272             }
00273           } else {
00274             // Maybe it's an alpha mode.
00275             EggRenderMode::AlphaMode am = EggRenderMode::string_alpha_mode(word);
00276             if (am != EggRenderMode::AM_unspecified) {
00277               _alpha_mode = am;
00278 
00279             } else {
00280               // Maybe it's an image file request.
00281               if (!parse_image_type_request(word, _color_type, _alpha_type)) {
00282                 return false;
00283               }
00284             }
00285           }
00286         }
00287       }
00288       ++wi;
00289     }
00290   }
00291 
00292   return true;
00293 }
00294 
00295 ////////////////////////////////////////////////////////////////////
00296 //     Function: TxaLine::match_egg
00297 //       Access: Public
00298 //  Description: Compares the patterns on the line to the indicated
00299 //               EggFile.  If they match, updates the egg with the
00300 //               appropriate information.  Returns true if a match is
00301 //               detected and the search for another line should stop,
00302 //               or false if a match is not detected (or if the
00303 //               keyword "cont" is present, which means the search
00304 //               should continue regardless).
00305 ////////////////////////////////////////////////////////////////////
00306 bool TxaLine::
00307 match_egg(EggFile *egg_file) const {
00308   string name = egg_file->get_name();
00309 
00310   bool matched_any = false;
00311   Patterns::const_iterator pi;
00312   for (pi = _egg_patterns.begin();
00313        pi != _egg_patterns.end() && !matched_any;
00314        ++pi) {
00315     matched_any = (*pi).matches(name);
00316   }
00317 
00318   if (!matched_any) {
00319     // No match this line; continue.
00320     return false;
00321   }
00322 
00323   bool got_cont = false;
00324   Keywords::const_iterator ki;
00325   for (ki = _keywords.begin(); ki != _keywords.end(); ++ki) {
00326     switch (*ki) {
00327     case KW_omit:
00328       break;
00329 
00330     case KW_nearest:
00331     case KW_linear:
00332     case KW_mipmap:
00333     case KW_anisotropic:
00334       // These mean nothing to an egg file.
00335       break;
00336 
00337     case KW_cont:
00338       got_cont = true;
00339       break;
00340     }
00341   }
00342 
00343   egg_file->match_txa_groups(_palette_groups);
00344 
00345   if (got_cont) {
00346     // If we have the "cont" keyword, we should keep scanning for
00347     // another line, even though we matched this one.
00348     return false;
00349   }
00350 
00351   // Otherwise, in the normal case, a match ends the search for
00352   // matches.
00353   egg_file->clear_surprise();
00354 
00355   return true;
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: TxaLine::match_texture
00360 //       Access: Public
00361 //  Description: Compares the patterns on the line to the indicated
00362 //               TextureImage.  If they match, updates the texture
00363 //               with the appropriate information.  Returns true if a
00364 //               match is detected and the search for another line
00365 //               should stop, or false if a match is not detected (or
00366 //               if the keyword "cont" is present, which means the
00367 //               search should continue regardless).
00368 ////////////////////////////////////////////////////////////////////
00369 bool TxaLine::
00370 match_texture(TextureImage *texture) const {
00371   string name = texture->get_name();
00372 
00373   bool matched_any = false;
00374   Patterns::const_iterator pi;
00375   for (pi = _texture_patterns.begin();
00376        pi != _texture_patterns.end() && !matched_any;
00377        ++pi) {
00378     matched_any = (*pi).matches(name);
00379   }
00380 
00381   if (!matched_any) {
00382     // No match this line; continue.
00383     return false;
00384   }
00385 
00386   SourceTextureImage *source = texture->get_preferred_source();
00387   TextureRequest &request = texture->_request;
00388 
00389   if (!request._got_size) {
00390     switch (_size_type) {
00391     case ST_none:
00392       break;
00393 
00394     case ST_scale:
00395       if (source != (SourceTextureImage *)NULL && source->get_size()) {
00396         request._got_size = true;
00397         request._x_size = (int)(source->get_x_size() * _scale / 100.0);
00398         request._y_size = (int)(source->get_y_size() * _scale / 100.0);
00399       }
00400       break;
00401 
00402     case ST_explicit_3:
00403       request._got_num_channels = true;
00404       request._num_channels = _num_channels;
00405       // fall through
00406 
00407     case ST_explicit_2:
00408       request._got_size = true;
00409       request._x_size = _x_size;
00410       request._y_size = _y_size;
00411       break;
00412     }
00413   }
00414 
00415   if (_got_margin) {
00416     request._margin = _margin;
00417   }
00418 
00419   if (_got_coverage_threshold) {
00420     request._coverage_threshold = _coverage_threshold;
00421   }
00422 
00423   if (_color_type != (PNMFileType *)NULL) {
00424     request._properties._color_type = _color_type;
00425     request._properties._alpha_type = _alpha_type;
00426   }
00427 
00428   if (_format != EggTexture::F_unspecified) {
00429     request._format = _format;
00430     request._force_format = _force_format;
00431     request._generic_format = false;
00432   }
00433 
00434   if (_generic_format) {
00435     request._generic_format = true;
00436   }
00437 
00438   if (_alpha_mode != EggRenderMode::AM_unspecified) {
00439     request._alpha_mode = _alpha_mode;
00440   }
00441 
00442   bool got_cont = false;
00443   Keywords::const_iterator ki;
00444   for (ki = _keywords.begin(); ki != _keywords.end(); ++ki) {
00445     switch (*ki) {
00446     case KW_omit:
00447       request._omit = true;
00448       break;
00449 
00450     case KW_nearest:
00451       request._minfilter = EggTexture::FT_nearest;
00452       request._magfilter = EggTexture::FT_nearest;
00453       break;
00454 
00455     case KW_linear:
00456       request._minfilter = EggTexture::FT_linear;
00457       request._magfilter = EggTexture::FT_linear;
00458       break;
00459 
00460     case KW_mipmap:
00461       request._minfilter = EggTexture::FT_linear_mipmap_linear;
00462       request._magfilter = EggTexture::FT_linear_mipmap_linear;
00463       break;
00464 
00465     case KW_anisotropic:
00466       request._anisotropic_degree = _aniso_degree;
00467       break;
00468 
00469     case KW_cont:
00470       got_cont = true;
00471       break;
00472     }
00473   }
00474 
00475   texture->_explicitly_assigned_groups.make_union
00476     (texture->_explicitly_assigned_groups, _palette_groups);
00477 
00478   if (got_cont) {
00479     // If we have the "cont" keyword, we should keep scanning for
00480     // another line, even though we matched this one.
00481     return false;
00482   }
00483 
00484   // Otherwise, in the normal case, a match ends the search for
00485   // matches.
00486   texture->_is_surprise = false;
00487 
00488   return true;
00489 }
00490 
00491 ////////////////////////////////////////////////////////////////////
00492 //     Function: TxaLine::output
00493 //       Access: Public
00494 //  Description:
00495 ////////////////////////////////////////////////////////////////////
00496 void TxaLine::
00497 output(ostream &out) const {
00498   Patterns::const_iterator pi;
00499   for (pi = _texture_patterns.begin(); pi != _texture_patterns.end(); ++pi) {
00500     out << (*pi) << " ";
00501   }
00502   for (pi = _egg_patterns.begin(); pi != _egg_patterns.end(); ++pi) {
00503     out << (*pi) << " ";
00504   }
00505   out << ":";
00506 
00507   switch (_size_type) {
00508   case ST_none:
00509     break;
00510 
00511   case ST_scale:
00512     out << " " << _scale << "%";
00513     break;
00514 
00515   case ST_explicit_2:
00516     out << " " << _x_size << " " << _y_size;
00517     break;
00518 
00519   case ST_explicit_3:
00520     out << " " << _x_size << " " << _y_size << " " << _num_channels;
00521     break;
00522   }
00523 
00524   if (_got_margin) {
00525     out << " margin " << _margin;
00526   }
00527 
00528   if (_got_coverage_threshold) {
00529     out << " coverage " << _coverage_threshold;
00530   }
00531 
00532   Keywords::const_iterator ki;
00533   for (ki = _keywords.begin(); ki != _keywords.end(); ++ki) {
00534     switch (*ki) {
00535     case KW_omit:
00536       out << " omit";
00537       break;
00538 
00539     case KW_nearest:
00540       out << " nearest";
00541       break;
00542 
00543     case KW_linear:
00544       out << " linear";
00545       break;
00546 
00547     case KW_mipmap:
00548       out << " mipmap";
00549       break;
00550 
00551     case KW_cont:
00552       out << " cont";
00553       break;
00554 
00555     case KW_anisotropic:
00556       out << " aniso " << _aniso_degree;
00557       break;
00558     }
00559   }
00560 
00561   PaletteGroups::const_iterator gi;
00562   for (gi = _palette_groups.begin(); gi != _palette_groups.end(); ++gi) {
00563     out << " " << (*gi)->get_name();
00564   }
00565 
00566   if (_format != EggTexture::F_unspecified) {
00567     out << " " << _format;
00568     if (_force_format) {
00569       out << " (forced)";
00570     }
00571   }
00572 
00573   if (_color_type != (PNMFileType *)NULL) {
00574     out << " " << _color_type->get_suggested_extension();
00575     if (_alpha_type != (PNMFileType *)NULL) {
00576       out << "," << _alpha_type->get_suggested_extension();
00577     }
00578   }
00579 }

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