Panda3D
|
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 }