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  */
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  */
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  */
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  */
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 }
PaletteGroup
This is the highest level of grouping for TextureImages.
Definition: paletteGroup.h:43
EggFile
This represents a single egg file known to the palettizer.
Definition: eggFile.h:36
PaletteGroup::add_texture_swap_info
void add_texture_swap_info(const std::string sourceTextureName, const vector_string &swapTextures)
Store textureswap information from textures.txa.
Definition: paletteGroup.cxx:699
txaFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TxaFile::write
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
PaletteGroup::get_dirname
const std::string & get_dirname() const
Returns the directory name associated with the palette group.
Definition: paletteGroup.cxx:71
TxaFile::match_texture
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
PaletteGroup::has_dirname
bool has_dirname() const
Returns true if the directory name has been explicitly set for this group.
Definition: paletteGroup.cxx:62
TxaLine::parse
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
PaletteGroup::group_with
void group_with(PaletteGroup *other)
Indicates a dependency of this group on some other group.
Definition: paletteGroup.cxx:94
Palettizer::string_remap
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
textureImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnotify.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PaletteGroup::set_margin_override
void set_margin_override(const int override)
Returns the set of groups this group depends on.
Definition: paletteGroup.cxx:118
TxaLine
This is a single matching line in the .txa file.
Definition: txaLine.h:36
PNMFileTypeRegistry::write
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.
Definition: pnmFileTypeRegistry.cxx:248
trim_left
string trim_left(const string &str)
Returns a new string representing the contents of the given string with the leading whitespace remove...
Definition: string_utils.cxx:223
palettizer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMFileTypeRegistry::get_global_ptr
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
Definition: pnmFileTypeRegistry.cxx:277
TxaFile::read
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
string_to_int
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
Definition: string_utils.cxx:324
pnmFileTypeRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PaletteGroup::set_dirname
void set_dirname(const std::string &dirname)
Sets the directory name associated with the palette group.
Definition: paletteGroup.cxx:53
string_to_double
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
Definition: string_utils.cxx:354
EggRenderMode::string_alpha_mode
static AlphaMode string_alpha_mode(const std::string &string)
Returns the AlphaMode value associated with the given string representation, or AM_unspecified if the...
Definition: eggRenderMode.cxx:173
extract_words
int extract_words(const string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
Definition: string_utils.cxx:105
pal_string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Palettizer::get_palette_group
PaletteGroup * get_palette_group(const std::string &name)
Returns the PaletteGroup with the given name.
Definition: palettizer.cxx:793
paletteGroup.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureImage
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:46
TxaFile::match_egg
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