Panda3D

txaFile.cxx

00001 // Filename: txaFile.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 "txaFile.h"
00016 #include "pal_string_utils.h"
00017 #include "palettizer.h"
00018 #include "paletteGroup.h"
00019 #include "textureImage.h"
00020 
00021 #include "pnotify.h"
00022 #include "pnmFileTypeRegistry.h"
00023 
00024 ////////////////////////////////////////////////////////////////////
00025 //     Function: TxaFile::Constructor
00026 //       Access: Public
00027 //  Description:
00028 ////////////////////////////////////////////////////////////////////
00029 TxaFile::
00030 TxaFile() {
00031 }
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //     Function: TxaFile::read
00035 //       Access: Public
00036 //  Description: Reads the indicated stream, and returns true
00037 //               if successful, or false if there is an error.
00038 ////////////////////////////////////////////////////////////////////
00039 bool TxaFile::
00040 read(istream &in, const string &filename) {
00041   string line;
00042   int line_number = 1;
00043 
00044   int ch = get_line_or_semicolon(in, line);
00045   while (ch != EOF || !line.empty()) {
00046     bool okflag = true;
00047 
00048     // Strip off the comment.
00049     size_t hash = line.find('#');
00050     if (hash != string::npos) {
00051       line = line.substr(0, hash);
00052     }
00053     line = trim_left(line);
00054     if (line.empty()) {
00055       // Empty lines are ignored.
00056 
00057     } else if (line[0] == ':') {
00058       // This is a keyword line.
00059       vector_string words;
00060       extract_words(line, words);
00061       if (words[0] == ":group") {
00062         okflag = parse_group_line(words);
00063 
00064       } else if (words[0] == ":palette") {
00065         okflag = parse_palette_line(words);
00066 
00067       } else if (words[0] == ":margin") {
00068         okflag = parse_margin_line(words);
00069 
00070       } else if (words[0] == ":background") {
00071         okflag = parse_background_line(words);
00072 
00073       } else if (words[0] == ":coverage") {
00074         okflag = parse_coverage_line(words);
00075 
00076       } else if (words[0] == ":powertwo") {
00077         okflag = parse_powertwo_line(words);
00078 
00079       } else if (words[0] == ":imagetype") {
00080         okflag = parse_imagetype_line(words);
00081 
00082       } else if (words[0] == ":shadowtype") {
00083         okflag = parse_shadowtype_line(words);
00084 
00085       } else if (words[0] == ":round") {
00086         okflag = parse_round_line(words);
00087 
00088       } else if (words[0] == ":remap") {
00089         okflag = parse_remap_line(words);
00090 
00091       } else if (words[0] == ":cutout") {
00092         okflag = parse_cutout_line(words);
00093 
00094       } else if (words[0] == ":textureswap") {
00095         okflag = parse_textureswap_line(words);
00096 
00097       } else {
00098         nout << "Invalid keyword " << words[0] << "\n";
00099         okflag = false;
00100       }
00101 
00102     } else {
00103       _lines.push_back(TxaLine());
00104       TxaLine &txa_line = _lines.back();
00105 
00106       okflag = txa_line.parse(line);
00107     }
00108 
00109     if (!okflag) {
00110       nout << "Error on line " << line_number << " of " << filename << "\n";
00111       return false;
00112     }
00113     if (ch == '\n') {
00114       line_number++;
00115     }
00116     ch = get_line_or_semicolon(in, line);
00117   }
00118 
00119   if (!in.eof()) {
00120     nout << "I/O error reading " << filename << "\n";
00121     return false;
00122   }
00123 
00124   return true;
00125 }
00126 
00127 ////////////////////////////////////////////////////////////////////
00128 //     Function: TxaFile::match_egg
00129 //       Access: Public
00130 //  Description: Searches for a matching line in the .txa file for the
00131 //               given egg file and applies its specifications.  If a
00132 //               match is found, returns true; otherwise, returns
00133 //               false.  Also returns false if all the matching lines
00134 //               for the egg file include the keyword "cont".
00135 ////////////////////////////////////////////////////////////////////
00136 bool TxaFile::
00137 match_egg(EggFile *egg_file) const {
00138   Lines::const_iterator li;
00139   for (li = _lines.begin(); li != _lines.end(); ++li) {
00140     if ((*li).match_egg(egg_file)) {
00141       return true;
00142     }
00143   }
00144 
00145   return false;
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: TxaFile::match_texture
00150 //       Access: Public
00151 //  Description: Searches for a matching line in the .txa file for the
00152 //               given texture and applies its specifications.  If a
00153 //               match is found, returns true; otherwise, returns
00154 //               false.  Also returns false if all the matching lines
00155 //               for the texture include the keyword "cont".
00156 ////////////////////////////////////////////////////////////////////
00157 bool TxaFile::
00158 match_texture(TextureImage *texture) const {
00159   Lines::const_iterator li;
00160   for (li = _lines.begin(); li != _lines.end(); ++li) {
00161     if ((*li).match_texture(texture)) {
00162       return true;
00163     }
00164   }
00165 
00166   return false;
00167 }
00168 
00169 ////////////////////////////////////////////////////////////////////
00170 //     Function: TxaFile::write
00171 //       Access: Public
00172 //  Description: Outputs a representation of the lines that were read
00173 //               in to the indicated output stream.  This is primarily
00174 //               useful for debugging.
00175 ////////////////////////////////////////////////////////////////////
00176 void TxaFile::
00177 write(ostream &out) const {
00178   Lines::const_iterator li;
00179   for (li = _lines.begin(); li != _lines.end(); ++li) {
00180     out << (*li) << "\n";
00181   }
00182 }
00183 
00184 ////////////////////////////////////////////////////////////////////
00185 //     Function: TxaFile::get_line_or_semicolon
00186 //       Access: Private, Static
00187 //  Description: Reads the next line, or the next semicolon-delimited
00188 //               phrase, from the indicated input stream.  Returns
00189 //               the character that marks the end of the line, or EOF
00190 //               if the end of file has been reached.
00191 ////////////////////////////////////////////////////////////////////
00192 int TxaFile::
00193 get_line_or_semicolon(istream &in, string &line) {
00194   line = string();
00195   int ch = in.get();
00196   char semicolon = ';';
00197 
00198   while (ch != EOF && ch != '\n' && ch != semicolon) {
00199     if (ch == '#') {
00200       // We don't consider a semicolon within a comment to be a line
00201       // break.
00202       semicolon = EOF;
00203     }
00204     line += ch;
00205     ch = in.get();
00206   }
00207 
00208   return ch;
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: TxaFile::parse_group_line
00213 //       Access: Private
00214 //  Description: Handles the line in a .txa file that begins with the
00215 //               keyword ":group" and indicates the relationships
00216 //               between one or more groups.
00217 ////////////////////////////////////////////////////////////////////
00218 bool TxaFile::
00219 parse_group_line(const vector_string &words) {
00220   vector_string::const_iterator wi;
00221   wi = words.begin();
00222   assert (wi != words.end());
00223   ++wi;
00224 
00225   const string &group_name = (*wi);
00226   PaletteGroup *group = pal->get_palette_group(group_name);
00227   ++wi;
00228 
00229   enum State {
00230     S_none,
00231     S_on,
00232     S_includes,
00233     S_dir,
00234     S_margin,
00235   };
00236   State state = S_none;
00237 
00238   bool first_on = true;
00239 
00240   while (wi != words.end()) {
00241     const string &word = (*wi);
00242     if (word == "with") {
00243       // Deprecated keyword: "with" means the same thing as "on".
00244       state = S_on;
00245 
00246     } else if (word == "on") {
00247       state = S_on;
00248 
00249     } else if (word == "includes") {
00250       state = S_includes;
00251 
00252     } else if (word == "dir") {
00253       state = S_dir;
00254 
00255     } else if (word == "margin") {
00256       state = S_margin;
00257 
00258     } else {
00259       switch (state) {
00260       case S_none:
00261         nout << "Invalid keyword: " << word << "\n";
00262         return false;
00263 
00264       case S_on:
00265         {
00266           PaletteGroup *on_group = pal->get_palette_group(word);
00267           if (first_on) {
00268             if (!group->has_dirname() && on_group->has_dirname()) {
00269               group->set_dirname(on_group->get_dirname());
00270             }
00271             first_on = false;
00272           }
00273           group->group_with(on_group);
00274         }
00275         break;
00276 
00277       case S_includes:
00278         pal->get_palette_group(word)->group_with(group);
00279         break;
00280 
00281       case S_dir:
00282         group->set_dirname(word);
00283         state = S_none;
00284         break;
00285 
00286       case S_margin:
00287         int margin_override;
00288         if (string_to_int(word, margin_override)) {
00289           group->set_margin_override(margin_override);
00290         }
00291         state = S_none;
00292         break;
00293       }
00294 
00295     }
00296 
00297     ++wi;
00298   }
00299 
00300   return true;
00301 }
00302 
00303 ////////////////////////////////////////////////////////////////////
00304 //     Function: TxaFile::parse_palette_line
00305 //       Access: Private
00306 //  Description: Handles the line in a .txa file that begins with the
00307 //               keyword ":palette" and indicates the appropriate size
00308 //               for the palette images.
00309 ////////////////////////////////////////////////////////////////////
00310 bool TxaFile::
00311 parse_palette_line(const vector_string &words) {
00312   if (words.size() != 3) {
00313     nout << "Exactly two parameters required for :palette, the x and y "
00314          << "size of the palette images to generate.\n";
00315     return false;
00316   }
00317 
00318   if (!string_to_int(words[1], pal->_pal_x_size) ||
00319       !string_to_int(words[2], pal->_pal_y_size)) {
00320     nout << "Invalid palette size: " << words[1] << " " << words[2] << "\n";
00321     return false;
00322   }
00323 
00324   if (pal->_pal_x_size <= 0 || pal->_pal_y_size <= 0) {
00325     nout << "Invalid palette size: " << pal->_pal_x_size
00326          << " " << pal->_pal_y_size << "\n";
00327     return false;
00328   }
00329 
00330   return true;
00331 }
00332 
00333 ////////////////////////////////////////////////////////////////////
00334 //     Function: TxaFile::parse_margin_line
00335 //       Access: Private
00336 //  Description: Handles the line in a .txa file that begins with the
00337 //               keyword ":margin" and indicates the default margin
00338 //               size.
00339 ////////////////////////////////////////////////////////////////////
00340 bool TxaFile::
00341 parse_margin_line(const vector_string &words) {
00342   if (words.size() != 2) {
00343     nout << "Exactly one parameter required for :margin, the "
00344          << "size of the default margin to apply.\n";
00345     return false;
00346   }
00347 
00348   if (!string_to_int(words[1], pal->_margin)) {
00349     nout << "Invalid margin: " << words[1] << "\n";
00350     return false;
00351   }
00352 
00353   if (pal->_margin < 0) {
00354     nout << "Invalid margin: " << pal->_margin << "\n";
00355     return false;
00356   }
00357 
00358   return true;
00359 }
00360 
00361 ////////////////////////////////////////////////////////////////////
00362 //     Function: TxaFile::parse_background_line
00363 //       Access: Private
00364 //  Description: Handles the line in a .txa file that begins with the
00365 //               keyword ":background" and indicates the palette
00366 //               background color.
00367 ////////////////////////////////////////////////////////////////////
00368 bool TxaFile::
00369 parse_background_line(const vector_string &words) {
00370   if (words.size() != 5) {
00371     nout << "Exactly four parameter required for :background: the "
00372          << "four [r g b a] components of the background color.\n";
00373     return false;
00374   }
00375 
00376   if (!string_to_double(words[1], pal->_background[0]) ||
00377       !string_to_double(words[2], pal->_background[1]) ||
00378       !string_to_double(words[3], pal->_background[2]) ||
00379       !string_to_double(words[4], pal->_background[3])) {
00380     nout << "Invalid color: "
00381          << words[1] << " " << words[2] << " "
00382          << words[3] << " " << words[4] << " " << "\n";
00383     return false;
00384   }
00385 
00386   return true;
00387 }
00388 
00389 ////////////////////////////////////////////////////////////////////
00390 //     Function: TxaFile::parse_coverage_line
00391 //       Access: Private
00392 //  Description: Handles the line in a .txa file that begins with the
00393 //               keyword ":coverage" and indicates the default
00394 //               coverage threshold.
00395 ////////////////////////////////////////////////////////////////////
00396 bool TxaFile::
00397 parse_coverage_line(const vector_string &words) {
00398   if (words.size() != 2) {
00399     nout << "Exactly one parameter required for :coverage, the "
00400          << "value for the default coverage threshold.\n";
00401     return false;
00402   }
00403 
00404 
00405   if (!string_to_double(words[1], pal->_coverage_threshold)) {
00406     nout << "Invalid coverage threshold: " << words[1] << "\n";
00407     return false;
00408   }
00409 
00410   if (pal->_coverage_threshold <= 0.0) {
00411     nout << "Invalid coverage threshold: " << pal->_coverage_threshold << "\n";
00412     return false;
00413   }
00414 
00415   return true;
00416 }
00417 
00418 ////////////////////////////////////////////////////////////////////
00419 //     Function: TxaFile::parse_powertwo_line
00420 //       Access: Private
00421 //  Description: Handles the line in a .txa file that begins with the
00422 //               keyword ":powertwo" and indicates whether textures
00423 //               should by default be forced to a power of two.
00424 ////////////////////////////////////////////////////////////////////
00425 bool TxaFile::
00426 parse_powertwo_line(const vector_string &words) {
00427   if (words.size() != 2) {
00428     nout << "Exactly one parameter required for :powertwo, either a 0 "
00429          << "or a 1.\n";
00430     return false;
00431   }
00432 
00433   int flag;
00434   if (!string_to_int(words[1], flag)) {
00435     nout << "Invalid powertwo flag: " << words[1] << "\n";
00436     return false;
00437   }
00438 
00439   if (flag != 0 && flag != 1) {
00440     nout << "Invalid powertwo flag: " << flag << "\n";
00441     return false;
00442   }
00443 
00444   pal->_force_power_2 = (flag != 0);
00445 
00446   return true;
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: TxaFile::parse_imagetype_line
00451 //       Access: Private
00452 //  Description: Handles the line in a .txa file that begins with the
00453 //               keyword ":imagetype" and indicates the default image
00454 //               file type to convert palettes and textures to.
00455 ////////////////////////////////////////////////////////////////////
00456 bool TxaFile::
00457 parse_imagetype_line(const vector_string &words) {
00458   if (words.size() != 2) {
00459     nout << "Exactly one parameter required for :imagetype.\n";
00460     return false;
00461   }
00462   const string &imagetype = words[1];
00463   if (!parse_image_type_request(imagetype, pal->_color_type, pal->_alpha_type)) {
00464     nout << "\nKnown image types are:\n";
00465     PNMFileTypeRegistry::get_global_ptr()->write(nout, 2);
00466     nout << "\n";
00467     return false;
00468   }
00469 
00470   return true;
00471 }
00472 
00473 ////////////////////////////////////////////////////////////////////
00474 //     Function: TxaFile::parse_shadowtype_line
00475 //       Access: Private
00476 //  Description: Handles the line in a .txa file that begins with the
00477 //               keyword ":shadowtype" and indicates the image file
00478 //               type to convert working copies of the palette images
00479 //               to.
00480 ////////////////////////////////////////////////////////////////////
00481 bool TxaFile::
00482 parse_shadowtype_line(const vector_string &words) {
00483   if (words.size() != 2) {
00484     nout << "Exactly one parameter required for :shadowtype.\n";
00485     return false;
00486   }
00487   const string &shadowtype = words[1];
00488   if (!parse_image_type_request(shadowtype, pal->_shadow_color_type,
00489                                 pal->_shadow_alpha_type)) {
00490     nout << "\nKnown image types are:\n";
00491     PNMFileTypeRegistry::get_global_ptr()->write(nout, 2);
00492     nout << "\n";
00493     return false;
00494   }
00495 
00496   return true;
00497 }
00498 
00499 ////////////////////////////////////////////////////////////////////
00500 //     Function: TxaFile::parse_round_line
00501 //       Access: Private
00502 //  Description: Handles the line in a .txa file that begins with the
00503 //               keyword ":round" and indicates how or whether to
00504 //               round up UV minmax boxes.
00505 ////////////////////////////////////////////////////////////////////
00506 bool TxaFile::
00507 parse_round_line(const vector_string &words) {
00508   if (words.size() == 2) {
00509     if (words[1] == "no") {
00510       pal->_round_uvs = false;
00511       return true;
00512     } else {
00513       nout << "Invalid round keyword: " << words[1] << ".\n"
00514            << "Expected 'no', or a round fraction and fuzz factor.\n";
00515       return false;
00516     }
00517   }
00518 
00519   if (words.size() != 3) {
00520     nout << "Exactly two parameters required for :round, the fraction "
00521          << "to round to, and the fuzz factor.\n";
00522     return false;
00523   }
00524 
00525   if (!string_to_double(words[1], pal->_round_unit) ||
00526       !string_to_double(words[2], pal->_round_fuzz)) {
00527     nout << "Invalid rounding: " << words[1] << " " << words[2] << "\n";
00528     return false;
00529   }
00530 
00531   if (pal->_round_unit <= 0.0 || pal->_round_fuzz < 0.0) {
00532     nout << "Invalid rounding: " << pal->_round_unit
00533          << " " << pal->_round_fuzz << "\n";
00534     return false;
00535   }
00536 
00537   pal->_round_uvs = true;
00538   return true;
00539 }
00540 
00541 ////////////////////////////////////////////////////////////////////
00542 //     Function: TxaFile::parse_remap_line
00543 //       Access: Private
00544 //  Description: Handles the line in a .txa file that begins with the
00545 //               keyword ":remap" and indicates how or whether to
00546 //               remap UV coordinates in egg files to the unit box.
00547 ////////////////////////////////////////////////////////////////////
00548 bool TxaFile::
00549 parse_remap_line(const vector_string &words) {
00550   int i = 1;
00551   while (i < (int)words.size()) {
00552     const string &keyword = words[i];
00553     if (keyword == "char") {
00554       // Defining how to remap UV's for characters.
00555       i++;
00556       if (i == (int)words.size()) {
00557         nout << "Keyword expected following 'char'\n";
00558         return false;
00559       }
00560       pal->_remap_char_uv = Palettizer::string_remap(words[i]);
00561       if (pal->_remap_char_uv == Palettizer::RU_invalid) {
00562         nout << "Invalid remap keyword: " << words[i] << "\n";
00563         return false;
00564       }
00565 
00566     } else {
00567       // Defining how to remap UV's in general.
00568       pal->_remap_uv = Palettizer::string_remap(words[i]);
00569       if (pal->_remap_uv == Palettizer::RU_invalid) {
00570         nout << "Invalid remap keyword: " << words[i] << "\n";
00571         return false;
00572       }
00573 
00574       pal->_remap_char_uv = pal->_remap_uv;
00575     }
00576 
00577     i++;
00578   }
00579 
00580   return true;
00581 }
00582 
00583 
00584 ////////////////////////////////////////////////////////////////////
00585 //     Function: TxaFile::parse_cutout_line
00586 //       Access: Private
00587 //  Description: Handles the line in a .txa file that begins with the
00588 //               keyword ":cutout" and indicates how to handle
00589 //               alpha-cutout textures: those textures that appear to
00590 //               be mostly solid parts and invisible parts, with a
00591 //               thin border of antialiased alpha along the boundary.
00592 ////////////////////////////////////////////////////////////////////
00593 bool TxaFile::
00594 parse_cutout_line(const vector_string &words) {
00595   if (words.size() < 2 || words.size() > 3) {
00596     nout << ":cutout alpha-mode [ratio]\n";
00597     return false;
00598   }
00599 
00600   EggRenderMode::AlphaMode am = EggRenderMode::string_alpha_mode(words[1]);
00601   if (am == EggRenderMode::AM_unspecified) {
00602     nout << "Invalid cutout keyword: " << words[1] << "\n";
00603     return false;
00604   }
00605   pal->_cutout_mode = am;
00606 
00607   if (words.size() >= 3) {
00608     if (!string_to_double(words[2], pal->_cutout_ratio)) {
00609       nout << "Invalid cutout ratio: " << words[2] << "\n";
00610     }
00611   }
00612 
00613   return true;
00614 }
00615 
00616 ////////////////////////////////////////////////////////////////////
00617 //     Function: TxaFile::parse_textureswap_line
00618 //       Access: Private
00619 //  Description: Handles the line in a .txa file that begins with the
00620 //               keyword ":textureswap" and indicates the relationships
00621 //               between textures to be swapped.
00622 ////////////////////////////////////////////////////////////////////
00623 bool TxaFile::
00624 parse_textureswap_line(const vector_string &words) {
00625   vector_string::const_iterator wi;
00626   wi = words.begin();
00627   assert (wi != words.end());
00628   ++wi;
00629 
00630   const string &group_name = (*wi);
00631   PaletteGroup *group = pal->get_palette_group(group_name);
00632   ++wi;
00633 
00634   string sourceTextureName = (*wi);
00635   ++wi;
00636 
00637   //vector_string swapTextures;
00638   //copy(words.begin(), words.end(), swapTextures);
00639   //group->add_texture_swap_info(sourceTextureName, swapTextures);
00640   size_t dot = sourceTextureName.rfind('.');
00641   if (dot != string::npos) {
00642     sourceTextureName = sourceTextureName.substr(0, dot);
00643   }
00644   group->add_texture_swap_info(sourceTextureName, words);
00645   
00646   return true;
00647 }
 All Classes Functions Variables Enumerations