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