Panda3D
 All Classes Functions Variables Enumerations
txaFile.cxx
1 // Filename: txaFile.cxx
2 // Created by: drose (30Nov00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "txaFile.h"
16 #include "pal_string_utils.h"
17 #include "palettizer.h"
18 #include "paletteGroup.h"
19 #include "textureImage.h"
20 
21 #include "pnotify.h"
22 #include "pnmFileTypeRegistry.h"
23 
24 ////////////////////////////////////////////////////////////////////
25 // Function: TxaFile::Constructor
26 // Access: Public
27 // Description:
28 ////////////////////////////////////////////////////////////////////
29 TxaFile::
30 TxaFile() {
31 }
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: TxaFile::read
35 // Access: Public
36 // Description: Reads the indicated stream, and returns true
37 // if successful, or false if there is an error.
38 ////////////////////////////////////////////////////////////////////
39 bool TxaFile::
40 read(istream &in, const string &filename) {
41  string line;
42  int line_number = 1;
43 
44  int ch = get_line_or_semicolon(in, line);
45  while (ch != EOF || !line.empty()) {
46  bool okflag = true;
47 
48  // Strip off the comment.
49  size_t hash = line.find('#');
50  if (hash != string::npos) {
51  line = line.substr(0, hash);
52  }
53  line = trim_left(line);
54  if (line.empty()) {
55  // Empty lines are ignored.
56 
57  } else if (line[0] == ':') {
58  // This is a keyword line.
59  vector_string words;
60  extract_words(line, words);
61  if (words[0] == ":group") {
62  okflag = parse_group_line(words);
63 
64  } else if (words[0] == ":palette") {
65  okflag = parse_palette_line(words);
66 
67  } else if (words[0] == ":margin") {
68  okflag = parse_margin_line(words);
69 
70  } else if (words[0] == ":background") {
71  okflag = parse_background_line(words);
72 
73  } else if (words[0] == ":coverage") {
74  okflag = parse_coverage_line(words);
75 
76  } else if (words[0] == ":powertwo") {
77  okflag = parse_powertwo_line(words);
78 
79  } else if (words[0] == ":imagetype") {
80  okflag = parse_imagetype_line(words);
81 
82  } else if (words[0] == ":shadowtype") {
83  okflag = parse_shadowtype_line(words);
84 
85  } else if (words[0] == ":round") {
86  okflag = parse_round_line(words);
87 
88  } else if (words[0] == ":remap") {
89  okflag = parse_remap_line(words);
90 
91  } else if (words[0] == ":cutout") {
92  okflag = parse_cutout_line(words);
93 
94  } else if (words[0] == ":textureswap") {
95  okflag = parse_textureswap_line(words);
96 
97  } else {
98  nout << "Invalid keyword " << words[0] << "\n";
99  okflag = false;
100  }
101 
102  } else {
103  _lines.push_back(TxaLine());
104  TxaLine &txa_line = _lines.back();
105 
106  okflag = txa_line.parse(line);
107  }
108 
109  if (!okflag) {
110  nout << "Error on line " << line_number << " of " << filename << "\n";
111  return false;
112  }
113  if (ch == '\n') {
114  line_number++;
115  }
116  ch = get_line_or_semicolon(in, line);
117  }
118 
119  if (!in.eof()) {
120  nout << "I/O error reading " << filename << "\n";
121  return false;
122  }
123 
124  return true;
125 }
126 
127 ////////////////////////////////////////////////////////////////////
128 // Function: TxaFile::match_egg
129 // Access: Public
130 // Description: Searches for a matching line in the .txa file for the
131 // given egg file and applies its specifications. If a
132 // match is found, returns true; otherwise, returns
133 // false. Also returns false if all the matching lines
134 // for the egg file include the keyword "cont".
135 ////////////////////////////////////////////////////////////////////
136 bool TxaFile::
137 match_egg(EggFile *egg_file) const {
138  Lines::const_iterator li;
139  for (li = _lines.begin(); li != _lines.end(); ++li) {
140  if ((*li).match_egg(egg_file)) {
141  return true;
142  }
143  }
144 
145  return false;
146 }
147 
148 ////////////////////////////////////////////////////////////////////
149 // Function: TxaFile::match_texture
150 // Access: Public
151 // Description: Searches for a matching line in the .txa file for the
152 // given texture and applies its specifications. If a
153 // match is found, returns true; otherwise, returns
154 // false. Also returns false if all the matching lines
155 // for the texture include the keyword "cont".
156 ////////////////////////////////////////////////////////////////////
157 bool TxaFile::
158 match_texture(TextureImage *texture) const {
159  Lines::const_iterator li;
160  for (li = _lines.begin(); li != _lines.end(); ++li) {
161  if ((*li).match_texture(texture)) {
162  return true;
163  }
164  }
165 
166  return false;
167 }
168 
169 ////////////////////////////////////////////////////////////////////
170 // Function: TxaFile::write
171 // Access: Public
172 // Description: Outputs a representation of the lines that were read
173 // in to the indicated output stream. This is primarily
174 // useful for debugging.
175 ////////////////////////////////////////////////////////////////////
176 void TxaFile::
177 write(ostream &out) const {
178  Lines::const_iterator li;
179  for (li = _lines.begin(); li != _lines.end(); ++li) {
180  out << (*li) << "\n";
181  }
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: TxaFile::get_line_or_semicolon
186 // Access: Private, Static
187 // Description: Reads the next line, or the next semicolon-delimited
188 // phrase, from the indicated input stream. Returns
189 // the character that marks the end of the line, or EOF
190 // if the end of file has been reached.
191 ////////////////////////////////////////////////////////////////////
192 int TxaFile::
193 get_line_or_semicolon(istream &in, string &line) {
194  line = string();
195  int ch = in.get();
196  char semicolon = ';';
197 
198  while (ch != EOF && ch != '\n' && ch != semicolon) {
199  if (ch == '#') {
200  // We don't consider a semicolon within a comment to be a line
201  // break.
202  semicolon = EOF;
203  }
204  line += ch;
205  ch = in.get();
206  }
207 
208  return ch;
209 }
210 
211 ////////////////////////////////////////////////////////////////////
212 // Function: TxaFile::parse_group_line
213 // Access: Private
214 // Description: Handles the line in a .txa file that begins with the
215 // keyword ":group" and indicates the relationships
216 // between one or more groups.
217 ////////////////////////////////////////////////////////////////////
218 bool TxaFile::
219 parse_group_line(const vector_string &words) {
220  vector_string::const_iterator wi;
221  wi = words.begin();
222  assert (wi != words.end());
223  ++wi;
224 
225  const string &group_name = (*wi);
226  PaletteGroup *group = pal->get_palette_group(group_name);
227  ++wi;
228 
229  enum State {
230  S_none,
231  S_on,
232  S_includes,
233  S_dir,
234  S_margin,
235  };
236  State state = S_none;
237 
238  bool first_on = true;
239 
240  while (wi != words.end()) {
241  const string &word = (*wi);
242  if (word == "with") {
243  // Deprecated keyword: "with" means the same thing as "on".
244  state = S_on;
245 
246  } else if (word == "on") {
247  state = S_on;
248 
249  } else if (word == "includes") {
250  state = S_includes;
251 
252  } else if (word == "dir") {
253  state = S_dir;
254 
255  } else if (word == "margin") {
256  state = S_margin;
257 
258  } else {
259  switch (state) {
260  case S_none:
261  nout << "Invalid keyword: " << word << "\n";
262  return false;
263 
264  case S_on:
265  {
266  PaletteGroup *on_group = pal->get_palette_group(word);
267  if (first_on) {
268  if (!group->has_dirname() && on_group->has_dirname()) {
269  group->set_dirname(on_group->get_dirname());
270  }
271  first_on = false;
272  }
273  group->group_with(on_group);
274  }
275  break;
276 
277  case S_includes:
278  pal->get_palette_group(word)->group_with(group);
279  break;
280 
281  case S_dir:
282  group->set_dirname(word);
283  state = S_none;
284  break;
285 
286  case S_margin:
287  int margin_override;
288  if (string_to_int(word, margin_override)) {
289  group->set_margin_override(margin_override);
290  }
291  state = S_none;
292  break;
293  }
294 
295  }
296 
297  ++wi;
298  }
299 
300  return true;
301 }
302 
303 ////////////////////////////////////////////////////////////////////
304 // Function: TxaFile::parse_palette_line
305 // Access: Private
306 // Description: Handles the line in a .txa file that begins with the
307 // keyword ":palette" and indicates the appropriate size
308 // for the palette images.
309 ////////////////////////////////////////////////////////////////////
310 bool TxaFile::
311 parse_palette_line(const vector_string &words) {
312  if (words.size() != 3) {
313  nout << "Exactly two parameters required for :palette, the x and y "
314  << "size of the palette images to generate.\n";
315  return false;
316  }
317 
318  if (!string_to_int(words[1], pal->_pal_x_size) ||
319  !string_to_int(words[2], pal->_pal_y_size)) {
320  nout << "Invalid palette size: " << words[1] << " " << words[2] << "\n";
321  return false;
322  }
323 
324  if (pal->_pal_x_size <= 0 || pal->_pal_y_size <= 0) {
325  nout << "Invalid palette size: " << pal->_pal_x_size
326  << " " << pal->_pal_y_size << "\n";
327  return false;
328  }
329 
330  return true;
331 }
332 
333 ////////////////////////////////////////////////////////////////////
334 // Function: TxaFile::parse_margin_line
335 // Access: Private
336 // Description: Handles the line in a .txa file that begins with the
337 // keyword ":margin" and indicates the default margin
338 // size.
339 ////////////////////////////////////////////////////////////////////
340 bool TxaFile::
341 parse_margin_line(const vector_string &words) {
342  if (words.size() != 2) {
343  nout << "Exactly one parameter required for :margin, the "
344  << "size of the default margin to apply.\n";
345  return false;
346  }
347 
348  if (!string_to_int(words[1], pal->_margin)) {
349  nout << "Invalid margin: " << words[1] << "\n";
350  return false;
351  }
352 
353  if (pal->_margin < 0) {
354  nout << "Invalid margin: " << pal->_margin << "\n";
355  return false;
356  }
357 
358  return true;
359 }
360 
361 ////////////////////////////////////////////////////////////////////
362 // Function: TxaFile::parse_background_line
363 // Access: Private
364 // Description: Handles the line in a .txa file that begins with the
365 // keyword ":background" and indicates the palette
366 // background color.
367 ////////////////////////////////////////////////////////////////////
368 bool TxaFile::
369 parse_background_line(const vector_string &words) {
370  if (words.size() != 5) {
371  nout << "Exactly four parameter required for :background: the "
372  << "four [r g b a] components of the background color.\n";
373  return false;
374  }
375 
376  if (!string_to_double(words[1], pal->_background[0]) ||
377  !string_to_double(words[2], pal->_background[1]) ||
378  !string_to_double(words[3], pal->_background[2]) ||
379  !string_to_double(words[4], pal->_background[3])) {
380  nout << "Invalid color: "
381  << words[1] << " " << words[2] << " "
382  << words[3] << " " << words[4] << " " << "\n";
383  return false;
384  }
385 
386  return true;
387 }
388 
389 ////////////////////////////////////////////////////////////////////
390 // Function: TxaFile::parse_coverage_line
391 // Access: Private
392 // Description: Handles the line in a .txa file that begins with the
393 // keyword ":coverage" and indicates the default
394 // coverage threshold.
395 ////////////////////////////////////////////////////////////////////
396 bool TxaFile::
397 parse_coverage_line(const vector_string &words) {
398  if (words.size() != 2) {
399  nout << "Exactly one parameter required for :coverage, the "
400  << "value for the default coverage threshold.\n";
401  return false;
402  }
403 
404 
405  if (!string_to_double(words[1], pal->_coverage_threshold)) {
406  nout << "Invalid coverage threshold: " << words[1] << "\n";
407  return false;
408  }
409 
410  if (pal->_coverage_threshold <= 0.0) {
411  nout << "Invalid coverage threshold: " << pal->_coverage_threshold << "\n";
412  return false;
413  }
414 
415  return true;
416 }
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: TxaFile::parse_powertwo_line
420 // Access: Private
421 // Description: Handles the line in a .txa file that begins with the
422 // keyword ":powertwo" and indicates whether textures
423 // should by default be forced to a power of two.
424 ////////////////////////////////////////////////////////////////////
425 bool TxaFile::
426 parse_powertwo_line(const vector_string &words) {
427  if (words.size() != 2) {
428  nout << "Exactly one parameter required for :powertwo, either a 0 "
429  << "or a 1.\n";
430  return false;
431  }
432 
433  int flag;
434  if (!string_to_int(words[1], flag)) {
435  nout << "Invalid powertwo flag: " << words[1] << "\n";
436  return false;
437  }
438 
439  if (flag != 0 && flag != 1) {
440  nout << "Invalid powertwo flag: " << flag << "\n";
441  return false;
442  }
443 
444  pal->_force_power_2 = (flag != 0);
445 
446  return true;
447 }
448 
449 ////////////////////////////////////////////////////////////////////
450 // Function: TxaFile::parse_imagetype_line
451 // Access: Private
452 // Description: Handles the line in a .txa file that begins with the
453 // keyword ":imagetype" and indicates the default image
454 // file type to convert palettes and textures to.
455 ////////////////////////////////////////////////////////////////////
456 bool TxaFile::
457 parse_imagetype_line(const vector_string &words) {
458  if (words.size() != 2) {
459  nout << "Exactly one parameter required for :imagetype.\n";
460  return false;
461  }
462  const string &imagetype = words[1];
463  if (!parse_image_type_request(imagetype, pal->_color_type, pal->_alpha_type)) {
464  nout << "\nKnown image types are:\n";
466  nout << "\n";
467  return false;
468  }
469 
470  return true;
471 }
472 
473 ////////////////////////////////////////////////////////////////////
474 // Function: TxaFile::parse_shadowtype_line
475 // Access: Private
476 // Description: Handles the line in a .txa file that begins with the
477 // keyword ":shadowtype" and indicates the image file
478 // type to convert working copies of the palette images
479 // to.
480 ////////////////////////////////////////////////////////////////////
481 bool TxaFile::
482 parse_shadowtype_line(const vector_string &words) {
483  if (words.size() != 2) {
484  nout << "Exactly one parameter required for :shadowtype.\n";
485  return false;
486  }
487  const string &shadowtype = words[1];
488  if (!parse_image_type_request(shadowtype, pal->_shadow_color_type,
489  pal->_shadow_alpha_type)) {
490  nout << "\nKnown image types are:\n";
492  nout << "\n";
493  return false;
494  }
495 
496  return true;
497 }
498 
499 ////////////////////////////////////////////////////////////////////
500 // Function: TxaFile::parse_round_line
501 // Access: Private
502 // Description: Handles the line in a .txa file that begins with the
503 // keyword ":round" and indicates how or whether to
504 // round up UV minmax boxes.
505 ////////////////////////////////////////////////////////////////////
506 bool TxaFile::
507 parse_round_line(const vector_string &words) {
508  if (words.size() == 2) {
509  if (words[1] == "no") {
510  pal->_round_uvs = false;
511  return true;
512  } else {
513  nout << "Invalid round keyword: " << words[1] << ".\n"
514  << "Expected 'no', or a round fraction and fuzz factor.\n";
515  return false;
516  }
517  }
518 
519  if (words.size() != 3) {
520  nout << "Exactly two parameters required for :round, the fraction "
521  << "to round to, and the fuzz factor.\n";
522  return false;
523  }
524 
525  if (!string_to_double(words[1], pal->_round_unit) ||
526  !string_to_double(words[2], pal->_round_fuzz)) {
527  nout << "Invalid rounding: " << words[1] << " " << words[2] << "\n";
528  return false;
529  }
530 
531  if (pal->_round_unit <= 0.0 || pal->_round_fuzz < 0.0) {
532  nout << "Invalid rounding: " << pal->_round_unit
533  << " " << pal->_round_fuzz << "\n";
534  return false;
535  }
536 
537  pal->_round_uvs = true;
538  return true;
539 }
540 
541 ////////////////////////////////////////////////////////////////////
542 // Function: TxaFile::parse_remap_line
543 // Access: Private
544 // Description: Handles the line in a .txa file that begins with the
545 // keyword ":remap" and indicates how or whether to
546 // remap UV coordinates in egg files to the unit box.
547 ////////////////////////////////////////////////////////////////////
548 bool TxaFile::
549 parse_remap_line(const vector_string &words) {
550  int i = 1;
551  while (i < (int)words.size()) {
552  const string &keyword = words[i];
553  if (keyword == "char") {
554  // Defining how to remap UV's for characters.
555  i++;
556  if (i == (int)words.size()) {
557  nout << "Keyword expected following 'char'\n";
558  return false;
559  }
560  pal->_remap_char_uv = Palettizer::string_remap(words[i]);
561  if (pal->_remap_char_uv == Palettizer::RU_invalid) {
562  nout << "Invalid remap keyword: " << words[i] << "\n";
563  return false;
564  }
565 
566  } else {
567  // Defining how to remap UV's in general.
568  pal->_remap_uv = Palettizer::string_remap(words[i]);
569  if (pal->_remap_uv == Palettizer::RU_invalid) {
570  nout << "Invalid remap keyword: " << words[i] << "\n";
571  return false;
572  }
573 
574  pal->_remap_char_uv = pal->_remap_uv;
575  }
576 
577  i++;
578  }
579 
580  return true;
581 }
582 
583 
584 ////////////////////////////////////////////////////////////////////
585 // Function: TxaFile::parse_cutout_line
586 // Access: Private
587 // Description: Handles the line in a .txa file that begins with the
588 // keyword ":cutout" and indicates how to handle
589 // alpha-cutout textures: those textures that appear to
590 // be mostly solid parts and invisible parts, with a
591 // thin border of antialiased alpha along the boundary.
592 ////////////////////////////////////////////////////////////////////
593 bool TxaFile::
594 parse_cutout_line(const vector_string &words) {
595  if (words.size() < 2 || words.size() > 3) {
596  nout << ":cutout alpha-mode [ratio]\n";
597  return false;
598  }
599 
600  EggRenderMode::AlphaMode am = EggRenderMode::string_alpha_mode(words[1]);
601  if (am == EggRenderMode::AM_unspecified) {
602  nout << "Invalid cutout keyword: " << words[1] << "\n";
603  return false;
604  }
605  pal->_cutout_mode = am;
606 
607  if (words.size() >= 3) {
608  if (!string_to_double(words[2], pal->_cutout_ratio)) {
609  nout << "Invalid cutout ratio: " << words[2] << "\n";
610  }
611  }
612 
613  return true;
614 }
615 
616 ////////////////////////////////////////////////////////////////////
617 // Function: TxaFile::parse_textureswap_line
618 // Access: Private
619 // Description: Handles the line in a .txa file that begins with the
620 // keyword ":textureswap" and indicates the relationships
621 // between textures to be swapped.
622 ////////////////////////////////////////////////////////////////////
623 bool TxaFile::
624 parse_textureswap_line(const vector_string &words) {
625  vector_string::const_iterator wi;
626  wi = words.begin();
627  assert (wi != words.end());
628  ++wi;
629 
630  const string &group_name = (*wi);
631  PaletteGroup *group = pal->get_palette_group(group_name);
632  ++wi;
633 
634  string sourceTextureName = (*wi);
635  ++wi;
636 
637  //vector_string swapTextures;
638  //copy(words.begin(), words.end(), swapTextures);
639  //group->add_texture_swap_info(sourceTextureName, swapTextures);
640  size_t dot = sourceTextureName.rfind('.');
641  if (dot != string::npos) {
642  sourceTextureName = sourceTextureName.substr(0, dot);
643  }
644  group->add_texture_swap_info(sourceTextureName, words);
645 
646  return true;
647 }
static AlphaMode string_alpha_mode(const string &string)
Returns the AlphaMode value associated with the given string representation, or AM_unspecified if the...
This is the highest level of grouping for TextureImages.
Definition: paletteGroup.h:47
This is a single matching line in the .txa file.
Definition: txaLine.h:39
void group_with(PaletteGroup *other)
Indicates a dependency of this group on some other group.
void add_texture_swap_info(const string sourceTextureName, const vector_string &swapTextures)
Store textureswap information from textures.txa.
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
static RemapUV string_remap(const string &str)
Returns the RemapUV code corresponding to the indicated string, or RU_invalid if the string is invali...
Definition: palettizer.cxx:945
bool match_egg(EggFile *egg_file) const
Searches for a matching line in the .txa file for the given egg file and applies its specifications...
Definition: txaFile.cxx:137
void set_dirname(const string &dirname)
Sets the directory name associated with the palette group.
const string & get_dirname() const
Returns the directory name associated with the palette group.
bool read(istream &in, const string &filename)
Reads the indicated stream, and returns true if successful, or false if there is an error...
Definition: txaFile.cxx:40
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:51
void write(ostream &out) const
Outputs a representation of the lines that were read in to the indicated output stream.
Definition: txaFile.cxx:177
bool parse(const string &line)
Accepts a string that defines a line of the .txa file and parses it into its constinuent parts...
Definition: txaLine.cxx:63
PaletteGroup * get_palette_group(const string &name)
Returns the PaletteGroup with the given name.
Definition: palettizer.cxx:848
bool has_dirname() const
Returns true if the directory name has been explicitly set for this group.
This represents a single egg file known to the palettizer.
Definition: eggFile.h:39
void write(ostream &out, int indent_level=0) const
Writes a list of supported image file types to the indicated output stream, one per line...
void set_margin_override(const int override)
Returns the set of groups this group depends on.
bool match_texture(TextureImage *texture) const
Searches for a matching line in the .txa file for the given texture and applies its specifications...
Definition: txaFile.cxx:158