Panda3D
eggPalettize.cxx
1 // Filename: eggPalettize.cxx
2 // Created by: drose (28Nov00)
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 "eggPalettize.h"
16 #include "palettizer.h"
17 #include "eggFile.h"
18 #include "pal_string_utils.h"
19 #include "filenameUnifier.h"
20 
21 #include "dcast.h"
22 #include "eggData.h"
23 #include "bamFile.h"
24 #include "pnotify.h"
25 #include "notifyCategory.h"
26 #include "notifySeverity.h"
27 #include "pystub.h"
28 
29 #include <stdio.h>
30 
31 ////////////////////////////////////////////////////////////////////
32 // Function: EggPalettize::Constructor
33 // Access: Public
34 // Description:
35 ////////////////////////////////////////////////////////////////////
36 EggPalettize::
37 EggPalettize() : EggMultiFilter(true) {
38  set_program_brief("pack textures from various .egg models into palette images");
39  set_program_description
40  ("egg-palettize attempts to pack several texture maps from various models "
41  "together into one or more palette images, for improved rendering performance "
42  "and ease of texture management. It can also resize textures and convert "
43  "them to another image file format, whether or not they are actually "
44  "placed on a palette, and can manage some "
45  "simple texture properties, like mipmapping and rendering "
46  "format.\n\n"
47 
48  "egg-palettize reads a texture attributes file, usually named "
49  "textures.txa, which contains instructions from the user about "
50  "resizing particular textures. Type egg-palettize -H for an "
51  "introduction to the syntax of this file.\n\n"
52 
53  "The palettization information from previous runs is recorded in a file "
54  "named textures.boo (assuming the attributes file is named "
55  "textures.txa); a complete record of every egg file and every texture "
56  "that has been referenced is kept here. This allows the program "
57  "to intelligently manage the multiple egg files that may reference "
58  "the textures in question.");
59 
60 
61  clear_runlines();
62  add_runline("[opts] file.egg [file.egg ...]");
63 
64  // We always have EggMultiBase's -f on: force complete load. In
65  // fact, we use -f for our own purposes, below.
66  remove_option("f");
67  _force_complete = true;
68 
69  add_option
70  ("af", "filename", 0,
71  "Read the indicated file as the .txa file. The default is textures.txa.",
72  &EggPalettize::dispatch_filename, &_got_txa_filename, &_txa_filename);
73 
74  add_option
75  ("a", "filename", 0,
76  "Deprecated option. This is the same as -af.",
77  &EggPalettize::dispatch_filename, &_got_txa_filename, &_txa_filename);
78 
79  add_option
80  ("as", "script", 0,
81  "Accept the script specified on the command line as the contents of the "
82  ".txa file, instead of reading a file on disk. This implies -nodb and "
83  "-opt.",
84  &EggPalettize::dispatch_string, &_got_txa_script, &_txa_script);
85 
86  add_option
87  ("nodb", "", 0,
88  "Don't read or record the state information to a .boo file. By default, "
89  "the palettization information is recorded so it can be preserved "
90  "between multiple invocations of egg-palettize. If you specify this "
91  "parameter, all the egg files to be palettized together must be "
92  "named at the same time. This also implies -opt, since there's no point "
93  "in not making an optimal packing if you won't be preserving the "
94  "state for future adjustments.",
95  &EggPalettize::dispatch_none, &_nodb);
96 
97  add_option
98  ("tn", "pattern", 0,
99  "Specify the name to generate for each palette image. The string should "
100  "contain %g for the group name, %p for the page name, and %i for the "
101  "index within the page. The extension is inferred from the image "
102  "type. The default is '%g_palette_%p_%i'.",
103  &EggPalettize::dispatch_string, &_got_generated_image_pattern,
104  &_generated_image_pattern);
105 
106  add_option
107  ("pi", "", 0,
108  "Do not process anything, but instead report the detailed palettization "
109  "information written in the state file.",
110  &EggPalettize::dispatch_none, &_report_pi);
111 
112  add_option
113  ("s", "", 0,
114  "Do not process anything, but report statistics on palette "
115  "and texture utilization from the state file.",
116  &EggPalettize::dispatch_none, &_report_statistics);
117 
118  add_option
119  ("R", "", 0,
120  "Remove the named egg files from the previously-generated state data "
121  "file.",
122  &EggPalettize::dispatch_none, &_remove_eggs);
123 
124  // We redefine -d using add_option() instead of redescribe_option()
125  // so it gets listed along with these other options that relate.
126  add_option
127  ("d", "dirname", 0,
128  "The directory in which to write the palettized egg files. This is "
129  "only necessary if more than one egg file is processed at the same "
130  "time; if it is included, each egg file will be processed and written "
131  "into the indicated directory.",
132  &EggPalettize::dispatch_filename, &_got_output_dirname, &_output_dirname);
133  add_option
134  ("dm", "dirname", 0,
135  "The directory in which to place all maps: generated palettes, "
136  "as well as images which were not placed on palettes "
137  "(but may have been resized). If this contains the string %g, "
138  "this will be replaced with the 'dir' string associated with a "
139  "palette group; see egg-palettize -H.",
140  &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
141  add_option
142  ("ds", "dirname", 0,
143  "The directory to write palette shadow images to. These are working "
144  "copies of the palette images, useful when the palette image type is "
145  "a lossy-compression type like JPEG; you can avoid generational loss "
146  "of quality on the palette images with each pass through the palettes "
147  "by storing these extra shadow images in a lossless image type. This "
148  "directory is only used if the :shadowtype keyword appears in the .txa "
149  "file.",
150  &EggPalettize::dispatch_filename, &_got_shadow_dirname, &_shadow_dirname);
151  add_option
152  ("dr", "dirname", 0,
153  "The directory to make map filenames relative to when writing egg "
154  "files. If specified, this should be an initial substring of -dm.",
155  &EggPalettize::dispatch_filename, &_got_rel_dirname, &_rel_dirname);
156  add_option
157  ("g", "group", 0,
158  "The default palette group that egg files will be assigned to if they "
159  "are not explicitly assigned to any other group.",
160  &EggPalettize::dispatch_string, &_got_default_groupname, &_default_groupname);
161  add_option
162  ("gdir", "name", 0,
163  "The \"dir\" string to associate with the default palette group "
164  "specified with -g, if no other dir name is given in the .txa file.",
165  &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
166 
167  add_option
168  ("all", "", 0,
169  "Consider all the textures referenced in all egg files that have "
170  "ever been palettized, not just the egg files that appear on "
171  "the command line.",
172  &EggPalettize::dispatch_none, &_all_textures);
173  add_option
174  ("egg", "", 0,
175  "Regenerate all egg files that need modification, even those that "
176  "aren't named on the command line.",
177  &EggPalettize::dispatch_none, &_redo_eggs);
178  add_option
179  ("redo", "", 0,
180  "Force a regeneration of each image from its original source(s). "
181  "When used in conjunction with -egg, this also forces each egg file to "
182  "be regenerated.",
183  &EggPalettize::dispatch_none, &_redo_all);
184  add_option
185  ("opt", "", 0,
186  "Force an optimal packing. By default, textures are added to "
187  "existing palettes without disturbing them, which can lead to "
188  "suboptimal packing. Including this switch forces the palettes "
189  "to be rebuilt if necessary to optimize the packing, but this "
190  "may invalidate other egg files which share this palette.",
191  &EggPalettize::dispatch_none, &_optimal);
192  add_option
193  ("omitall", "", 0,
194  "Re-enables the flag to omit all textures. This flag is normally on "
195  "by default, causing nothing actually to be palettized, until the "
196  "first time egg-palettize is run with the -opt flag, which turns off "
197  "the omitall flag and thenceforth allows textures to be combined "
198  "into palettes. Specifying this flag restores the original behavior "
199  "of keeping every texture as a separate image (which is convenient for "
200  "development).",
201  &EggPalettize::dispatch_none, &_omitall);
202 
203  // This isn't even implemented yet. Presently, we never lock anyway.
204  // Dangerous, but hard to implement reliable file locking across
205  // NFS/Samba and between multiple OS's.
206  /*
207  add_option
208  ("nolock", "", 0,
209  "Don't attempt to grab a file lock on the .txa file. Use "
210  "with extreme caution, as multiple processes running on the same "
211  ".txa file may overwrite each other. Use this only if the lock "
212  "cannot be achieved for some reason.",
213  &EggPalettize::dispatch_none, &_dont_lock_txa);
214  */
215 
216  add_option
217  ("H", "", 0,
218  "Describe the syntax of the attributes file.",
219  &EggPalettize::dispatch_none, &_describe_input_file);
220 
221  _txa_filename = "textures.txa";
222 }
223 
224 
225 ////////////////////////////////////////////////////////////////////
226 // Function: EggPalettize::handle_args
227 // Access: Protected, Virtual
228 // Description: Does something with the additional arguments on the
229 // command line (after all the -options have been
230 // parsed). Returns true if the arguments are good,
231 // false otherwise.
232 ////////////////////////////////////////////////////////////////////
233 bool EggPalettize::
235  if (_describe_input_file) {
236  describe_input_file();
237  exit(1);
238  }
239 
240  if (_remove_eggs) {
241  // If we're removing these egg files from the database, we don't
242  // want to try to load them up. Instead, just save the filenames.
243  _remove_egg_list = args;
244  return true;
245  }
246 
247  // Otherwise, load the named egg files up normally.
248  return EggMultiFilter::handle_args(args);
249 }
250 
251 ////////////////////////////////////////////////////////////////////
252 // Function: EggPalettize::describe_input_file
253 // Access: Public
254 // Description:
255 ////////////////////////////////////////////////////////////////////
256 void EggPalettize::
257 describe_input_file() {
258  nout <<
259  "An attributes file consists mostly of lines describing desired sizes of "
260  "texture maps. The format resembles, but is not identical to, that of "
261  "the qtess input file. Examples:\n\n"
262 
263  " texturename.rgb : 64 64\n"
264  " texture-a.rgb texture-b.rgb : 32 16 margin 2\n"
265  " *.rgb : 50% cont\n"
266  " eyelids.rgb : 16 16 omit\n\n"
267 
268  "In general, each line consists of one or more filenames (and can "
269  "contain shell globbing characters like '*' or '?'), and a colon "
270  "followed by a size request. For each texture appearing in an egg "
271  "file, the input list is scanned from the beginning and the first "
272  "line that matches the filename defines the size of the texture, as "
273  "well as other properties associated with the texture.\n\n"
274 
275  "A size request is most often a pair of numbers, giving a specific x y "
276  "size of the texture. A third number may also be supplied, giving a "
277  "specific number of channels to convert to (for instance, to force an "
278  "image to a 64x64 grayscale image, set its size to 64 64 1). "
279  "Alternatively, a percentage scaling may be specified, e.g. 30%. The "
280  "requested size need not be a power of 2.\n\n"
281 
282  "Other valid keywords that may be specified on the same line with the "
283  "texture are:\n\n";
284 
285  show_text(" omit", 10,
286  "This indicates that the texture should not be placed on any "
287  "palette image. It may still be resized, and it will in any "
288  "case be copied into the install directory.\n\n");
289 
290  show_text(" margin i", 10,
291  "This specifies the number of pixels that should be written "
292  "around the border of the texture when it is placed in a "
293  "palette image; i is the integer number of pixels. The "
294  "use of a margin helps cut down on color bleed "
295  "from neighboring images. If the texture does "
296  "not end up placed in a palette image, the "
297  "margin is not used. If not specified, the default margin is "
298  "used, which is specified by the :margin command (see below).\n\n");
299 
300  show_text(" coverage f", 10,
301  "This parameter specifies the maximum coverage to allow for this "
302  "particular texture before rejecting it "
303  "from the palette. If not specified, the default is "
304  "specified by the :coverage command (see below).\n\n");
305 
306  nout << " nearest\n"
307  << " linear\n";
308  show_text(" mipmap", 10,
309  "One of these options may be used to force the texture to use "
310  "a particular minfilter/magfilter sampling mode. If this is not "
311  "specified, the sampling mode specified in the egg file is "
312  "used. Textures that use different sampling modes cannot "
313  "be placed together on the same palette images.\n\n");
314 
315  show_text(" rgba", 10,
316  "This specifies format 'rgba' should be in effect for this "
317  "particular texture. Any valid egg texture format, such as "
318  "rgba, rgba12, rgba8, rgb5, luminance, etc. may be specified. "
319  "If nothing is specified, the format specified in the egg file "
320  "is used. The format will automatically be downgraded to match "
321  "the number of channels in the texture image; e.g. rgba will "
322  "automatically be converted to rgb for a three-channel image. "
323  "As with the filter modes above, textures that use different "
324  "formats cannot be placed together on the same palette "
325  "images.\n\n");
326 
327  show_text(" force-rgba", 10,
328  "This specifies a particular format, as above, that should be "
329  "in effect for this texture, but it will not be downgraded to "
330  "match the number of channels. As above, any valid egg texture "
331  "format may be used, e.g. force-rgba12, force-rgb5, etc.\n\n");
332 
333  show_text(" keep-format", 10,
334  "This specifies that the image format requested by an egg file "
335  "should be exactly preserved, without attempting to optimize "
336  "it by, for instance, automatically downgrading.\n\n");
337 
338  show_text(" generic", 10,
339  "Specifies that any image format requested by an egg file "
340  "that requests a particular bitdepth should be replaced by "
341  "its generic equivalent, e.g. rgba8 should become rgba.\n\n");
342 
343  show_text(" (alpha mode)", 10,
344  "A particular alpha mode may be applied to a texture by naming "
345  "the alpha mode. This may be any valid egg alpha mode, e.g. "
346  "blend, binary, ms, or dual.\n\n");
347 
348  show_text(" repeat_u, repeat_v, clamp_u, clamp_v", 10,
349  "Explcitly specify whether the source texture should repeat or "
350  "clamp in each direction. Although palette images are always "
351  "clamped, this will affect the pixels that are painted into "
352  "the palette image.\n\n");
353 
354  show_text(" (image type)", 10,
355  "A texture may be converted to a particular image type, for "
356  "instance jpg or rgb, by naming the type. If present, this "
357  "overrides the :imagetype command, described below. As with "
358  ":imagetype, you may also specify two type names separated "
359  "by a comma, to indicate that a different file should be written "
360  "for the color and alpha components.\n\n");
361 
362  show_text(" (group name)", 10,
363  "A texture may also be assigned to a specific group by naming "
364  "the group. The groups are defined using the :group command "
365  "(see below). Normally, textures are not assigned directly "
366  "to groups; instead, it is more useful to assign the egg files "
367  "they are referenced in to groups; see below.\n\n");
368 
369  show_text(" cont", 10,
370  "Normally, a texture file (or egg file) scans the lines in the "
371  "attributes file from the top, and stops on the first line that "
372  "matches its name. If the keyword 'cont' is included on the "
373  "line, however, the texture will apply the properties given "
374  "on the line, and then continue scanning. This trick may be "
375  "used to specify general parameters for all files while still "
376  "allowing the texture to match a more specific line below.\n\n");
377 
378  nout <<
379  "The attributes file may also assign egg files to various "
380  "named palette groups. The syntax is similar to the above:\n\n"
381 
382  " car-blue.egg : main\n"
383  " road.egg house.egg : main\n"
384  " plane.egg : phase_2 main\n"
385  " *.egg : phase_2\n\n"
386 
387  "Any number of egg files may be named on one line, and the set of "
388  "named egg files may be simultaneously assigned to one or more groups. "
389  "Each group must have been previously defined using the :group command "
390  "(see below). Each texture that is referenced by a given "
391  "egg file will be palettized "
392  "into at least one of the groups assigned to the egg file.\n\n"
393 
394  "Finally, there are a number of special commands that may appear in the "
395  "attributes file; some of these have been alluded to in the above "
396  "comments. These commands typically specify global parameters or "
397  "palettization options. The command names begin with a colon to "
398  "distinguish them from other kinds of lines. Each command must "
399  "appear on a line by itself. The commands are:\n\n";
400 
401  show_text(" :palette xsize ysize", 10,
402  "This specifies the size of the palette images to be "
403  "created. The default is 512 by 512.\n\n");
404 
405  show_text(" :margin msize", 10,
406  "This specifies the amount of default margin to apply to all "
407  "textures that are placed within a palette image. The margin "
408  "is a number of additional pixels that are written around the "
409  "texture image to help prevent color bleeding between "
410  "neighboring images within the same palette. The default "
411  "is 2.\n\n");
412 
413  show_text(" :background r g b a", 10,
414  "Specifies the background color of the generated palette "
415  "images. Normally, this is black, and it doesn't matter much "
416  "since the background color is, by definition, the color "
417  "of the palette images where nothing is used.\n\n");
418 
419  show_text(" :coverage area", 10,
420  "The 'coverage' of a texture refers to the fraction of "
421  "the area in the texture image that is actually used, according "
422  "to the UV's that appear in the various egg files. If a texture's "
423  "coverage is less than 1, only some of the texture image is used "
424  "(and only this part will be written to the palette). If the "
425  "coverage is greater than 1, the texture repeats that number of "
426  "times. A repeating texture may still be palettized by writing "
427  "the required number of copies into the palette image, according "
428  "to the coverage area.\n\n"
429 
430  "This command specifies the maximum coverage to allow for any "
431  "texture before rejecting it from the palette. It may be any "
432  "floating-point number greater than zero. Set this to 1 "
433  "to avoid palettizing repeating textures altogether. This may "
434  "also be overridden for a particular texture using the 'coverage' "
435  "keyword on the texture line.\n\n");
436 
437  show_text(" :powertwo flag", 10,
438  "Specifies whether textures should be forced to a power of two "
439  "size when they are not placed within a palette. Use 1 for true, "
440  "to force textures to a power of two; or 0 to leave them exactly "
441  "the size they are specified. The default is true.\n\n");
442 
443  show_text(" :round fraction fuzz", 10,
444  "When the coverage area is computed, it may optionally be "
445  "rounded up to the next sizeable unit before placing the "
446  "texture within the palette. This helps reduce constant "
447  "repalettization caused by slight differences in coverage "
448  "between egg files. For instance, say file a.egg references a "
449  "texture with a coverage of 0.91, and then later file b.egg "
450  "is discovered to reference the same texture with a coverage of "
451  "0.92. If the texture was already palettized with the original "
452  "coverage of 0.91, it must now be moved in the palette.\n\n"
453 
454  "Rounding the coverage area up to some fixed unit reduces this "
455  "problem. For instance, if you specified a value 0.5 for "
456  "fraction in the above command, it would round both of these "
457  "values up to the next half-unit, or 1.0.\n\n"
458 
459  "The second number is a fuzz factor, and should be a small "
460  "number; if the coverage area is just slightly larger than "
461  "the last unit (within the fuzz factor), it is rounded down "
462  "instead of up. This is intended to prevent UV coordinates "
463  "that are just slightly out of the range [0, 1] (which happens "
464  "fairly often) from forcing the palettization area all the "
465  "way up to the next stop.\n\n"
466 
467  "The default if this is unspecified is 0.1 0.01. That is, "
468  "round up to the next tenth, unless within a hundredth of the "
469  "last tenth. To disable rounding, specify ':round no'. "
470  "Rounding is implicitly disabled when you run with the -opt "
471  "command line option.\n\n");
472 
473  show_text(" :remap (never | group | poly)", 10,
474  "Sometimes two different parts of an egg file may reference "
475  "different regions of a repeating texture. For instance, "
476  "group A may reference UV coordinate values ranging from (0,5) "
477  "to (1,6), for a coverage of 1.0, while group B references "
478  "values ranging from (0,2) to (1,4), for a coverage of 2.0. "
479  "The maximum coverage used is only 2.0, and thus the texture "
480  "only needs to appear in the palette twice, but the total range "
481  "of UV's is from (0,2) to (1,6), causing an apparent coverage "
482  "of 4.0.\n\n"
483 
484  "It's possible for egg-palettize to reduce this kind of mistake "
485  "by remapping both groups of UV's so that they overlap. This "
486  "parameter specifies how this operation should be done. If "
487  "the option is 'never', remapping will not be performed; if "
488  "'group', entire groups will be remapped as a unit, if 'poly', "
489  "individual polygons within a group may be remapped. This last "
490  "option provides the greatest minimization of UV coverage, "
491  "but possibly at the expense of triangle strips in the resulting "
492  "model (since some vertices can no longer be shared).\n\n"
493 
494  "Sometimes, it may be necessary to be more restrictive on "
495  "character geometry than on non-character geometry, because "
496  "the cost of adding additional vertices on characters is "
497  "greater. You can specify a different kind of remapping for "
498  "characters only, by using the keyword 'char' on the same line, "
499  "e.g. ':remap group char never'.\n\n"
500 
501  "The default remap mode for all geometry, character or otherwise, "
502  "if no remap mode is specified is 'poly'.\n\n");
503 
504  show_text(" :imagetype type[,alpha_type]", 10,
505  "This specifies the default type of image file that should be "
506  "generated for each palette image and for each unplaced texture "
507  "copied into the install directory. This may be overridden for "
508  "a particular texture by specifying the image type on the "
509  "texture line.\n\n"
510 
511  "If two image type names separate by a comma are given, it means "
512  "to generate a second file of the second type for the alpha "
513  "channel, for images that require an alpha channel. This allows "
514  "support for image file formats that do not support alpha "
515  "(for instance, JPEG).\n\n");
516 
517  show_text(" :shadowtype type[,alpha_type]", 10,
518  "When generating palette images, egg-palettize sometimes has to "
519  "read and write the same palette image repeatedly. If the "
520  "palette image is stored in a lossy file format (like JPEG, see "
521  ":imagetype), this can eventually lead to degradation of the "
522  "palette images. As a workaround, egg-palettize can store "
523  "its working copies of the palette images in lossless shadow "
524  "images. Specify this to enable this feature; give it the "
525  "name of a lossless image file format. The shadow images will "
526  "be written to the directory specified by -ds on the command "
527  "line.\n\n");
528 
529  show_text(" :group groupname [dir dirname] [on group1 group2 ...] [includes group1 group2 ...]", 10,
530  "This defines a palette group, a logical division of textures. "
531  "Each texture is assigned to one or more palette groups before "
532  "being placed in any palette image; the palette images are "
533  "tied to the groups.\n\n"
534 
535  "The optional parameter 'dir' specifies a directory name to "
536  "associate with this group. This name is substituted in for "
537  "the string '%g' when it appears in the map directory name "
538  "specified on the command line with -dm; this may be used to "
539  "install textures and palettes into different directories based "
540  "on the groups they are assigned to.\n\n"
541 
542  "Palette groups can also be hierarchically related. The "
543  "keyword 'on' specifies any number of groups that this "
544  "palette group depends on; if a texture has already been "
545  "assigned to one of this group's dependent groups, it will "
546  "not need to be assigned to this group. This also implicitly "
547  "specifies a dir if one has not already been specified.\n\n"
548 
549  "The keyword 'includes' names one or more groups that depend "
550  "on this group.\n\n");
551 
552  show_text(" :textureswap groupname texturename0 texturename1 [texturename2 ...]", 10,
553  "This option builds a set of matching, interchangeable palette images. "
554  "All palette images in the set share the same internal texture layout. "
555  "The intention is to be able to swap palette images out at runtime, "
556  "to replace entire sets of textures on a model in one operation. "
557  "The textures named by this option indicate the texture images "
558  "which are similar to each other, and which all should be assigned "
559  "to the same placement on the different palette images: "
560  "texturename0 will be assigned to palette image 0, "
561  "texturename1 to the same position on palette image 1, "
562  "texturename2 to the same position on palette image 2, and so on. "
563  "To define a complete palette image, you must repeat this option "
564  "several times to associate all of the similar texture images.\n\n");
565 
566  nout <<
567  "Comments may appear freely throughout the file, and are set off by a "
568  "hash mark (#).\n\n";
569 }
570 
571 
572 ////////////////////////////////////////////////////////////////////
573 // Function: EggPalettize::run
574 // Access: Public
575 // Description:
576 ////////////////////////////////////////////////////////////////////
577 void EggPalettize::
578 run() {
579  // Fiddle with the loader severity, so we don't confuse the user
580  // with spurious "reading" and "writing" messages about the state
581  // file. If the severity is currently NS_info (the default), set it
582  // to NS_warning instead.
583  Notify *notify = Notify::ptr();
584  NotifyCategory *loader_cat = notify->get_category(":loader");
585  if (loader_cat != (NotifyCategory *)NULL &&
586  loader_cat->get_severity() == NS_info) {
587  loader_cat->set_severity(NS_warning);
588  }
589 
590  Filename state_filename;
591  BamFile state_file;
592 
593  if (_got_txa_script) {
594  // If we got a command-line script instead of a .txa file, we
595  // won't be encoding a .boo file either.
596  _nodb = true;
597 
598  } else {
599  // Look for the .txa file.
600  if (!_txa_filename.exists() && !_got_txa_filename) {
601  // If we did not specify a filename, and the default filename of
602  // "textures.txa" doesn't exist, try looking in src/maps, as
603  // another likely possibility.
604  Filename maybe = _txa_filename;
605  maybe.set_dirname("src/maps");
606  if (maybe.exists()) {
607  _txa_filename = maybe;
608  }
609  }
610 
611  if (!_txa_filename.exists()) {
612  nout << FilenameUnifier::make_user_filename(_txa_filename)
613  << " does not exist; cannot run.\n";
614  exit(1);
615  }
616 
617  FilenameUnifier::set_txa_filename(_txa_filename);
618 
619  state_filename = _txa_filename;
620  state_filename.set_extension("boo");
621  }
622 
623  if (_nodb) {
624  // -nodb means don't attempt to read textures.boo; in fact, don't
625  // even bother reporting this absence to the user.
626  pal = new Palettizer;
627 
628  // And -nodb implies -opt.
629  _optimal = true;
630 
631  } else if (!state_filename.exists()) {
632  nout << FilenameUnifier::make_user_filename(state_filename)
633  << " does not exist; starting palettization from scratch.\n";
634  pal = new Palettizer;
635 
636  // By default, the -omitall flag is true from the beginning.
637  pal->_omit_everything = true;
638 
639  } else {
640  // Read the Palettizer object from the Bam file written
641  // previously. This will recover all of the state saved from the
642  // past session.
643  nout << "Reading " << FilenameUnifier::make_user_filename(state_filename)
644  << "\n";
645 
646  if (!state_file.open_read(state_filename)) {
647  nout << FilenameUnifier::make_user_filename(state_filename)
648  << " exists, but cannot be read. Perhaps you should "
649  << "remove it so a new one can be created.\n";
650  exit(1);
651  }
652 
653  TypedWritable *obj = state_file.read_object();
654  if (obj == (TypedWritable *)NULL || !state_file.resolve()) {
655  nout << FilenameUnifier::make_user_filename(state_filename)
656  << " exists, but appears to be corrupt. Perhaps you "
657  << "should remove it so a new one can be created.\n";
658  exit(1);
659  }
660 
661  if (!obj->is_of_type(Palettizer::get_class_type())) {
662  nout << FilenameUnifier::make_user_filename(state_filename)
663  << " exists, but does not appear to be "
664  << "an egg-palettize output file. Perhaps you "
665  << "should remove it so a new one can be created.\n";
666  exit(1);
667  }
668 
669  state_file.close();
670 
671  pal = DCAST(Palettizer, obj);
672 
673  if (pal->_read_pi_version > pal->_pi_version) {
674  nout << FilenameUnifier::make_user_filename(state_filename)
675  << " was written by a more recent version of egg-palettize "
676  << "than this one. You will need to update your egg-palettize.\n";
677  exit(1);
678  }
679 
680  if (pal->_read_pi_version < pal->_min_pi_version) {
681  nout << FilenameUnifier::make_user_filename(state_filename)
682  << " was written by an old version of egg-palettize.\n\n"
683  << "You will need to make undo-pal (or simply remove the file "
684  << FilenameUnifier::make_user_filename(state_filename)
685  << " and try again).\n\n";
686  exit(1);
687  }
688 
689  if (!pal->is_valid()) {
690  nout << FilenameUnifier::make_user_filename(state_filename)
691  << " could not be properly read. You will need to remove it.\n";
692  exit(1);
693  }
694  }
695 
696  pal->set_noabs(_noabs);
697 
698  if (_report_pi) {
699  pal->report_pi();
700  exit(0);
701  }
702 
703  if (_report_statistics) {
704  pal->report_statistics();
705  exit(0);
706  }
707 
708  bool okflag = true;
709 
710  if (_got_txa_script) {
711  istringstream txa_script(_txa_script);
712  pal->read_txa_file(txa_script, "command line");
713 
714  } else {
715  _txa_filename.set_text();
716  ifstream txa_file;
717  if (!_txa_filename.open_read(txa_file)) {
718  nout << "Unable to open " << _txa_filename << "\n";
719  exit(1);
720  }
721  pal->read_txa_file(txa_file, _txa_filename);
722  }
723 
724  if (_got_generated_image_pattern) {
725  pal->_generated_image_pattern = _generated_image_pattern;
726  }
727 
728  if (_got_default_groupname) {
729  pal->_default_groupname = _default_groupname;
730  } else {
731  pal->_default_groupname = _txa_filename.get_basename_wo_extension();
732  }
733 
734  if (_got_default_groupdir) {
735  pal->_default_groupdir = _default_groupdir;
736  }
737 
738  if (_got_map_dirname) {
739  pal->_map_dirname = _map_dirname;
740  }
741  if (_got_shadow_dirname) {
742  pal->_shadow_dirname = _shadow_dirname;
743  }
744  if (_got_rel_dirname) {
745  pal->_rel_dirname = _rel_dirname;
746  FilenameUnifier::set_rel_dirname(_rel_dirname);
747  }
748 
749  // We only omit solitary textures from palettes if we're running in
750  // optimal mode. Otherwise, we're likely to invalidate old egg
751  // files by changing a texture from solitary to nonsolitary state or
752  // vice-versa.
753  pal->_omit_solitary = _optimal;
754 
755  if (_omitall) {
756  pal->_omit_everything = true;
757  } else if (_optimal) {
758  pal->_omit_everything = false;
759  }
760 
761  pal->all_params_set();
762 
763  // Remove any files named for removal.
764  Args::const_iterator ai;
765  for (ai = _remove_egg_list.begin(); ai != _remove_egg_list.end(); ++ai) {
766  Filename filename = (*ai);
767  pal->remove_egg_file(filename.get_basename());
768  }
769 
770  // And process the egg files named for addition.
771  bool all_eggs_valid = true;
772 
773  string egg_comment = get_exec_command();
774  Eggs::const_iterator ei;
775  for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
776  EggData *egg_data = (*ei);
777  Filename source_filename = egg_data->get_egg_filename();
778  Filename dest_filename = get_output_filename(source_filename);
779  string name = source_filename.get_basename();
780 
781  EggFile *egg_file = pal->get_egg_file(name);
782  if (!egg_file->from_command_line(egg_data, source_filename, dest_filename,
783  egg_comment)) {
784  all_eggs_valid = false;
785 
786  } else {
787  pal->add_command_line_egg(egg_file);
788  }
789  }
790 
791  if (!all_eggs_valid) {
792  nout << "Errors reading egg file(s).\n";
793  exit(1);
794  }
795 
796  if (_optimal) {
797  // If we're asking for an optimal packing, throw away the old
798  // packing and start fresh.
799  pal->reset_images();
800  _all_textures = true;
801 
802  /* Asad: I disagree: unless :round is set to no from textures.txa, we
803  should always leave the _round_uvs to default.
804  // Also turn off the rounding-up of UV's for this purpose.
805  pal->_round_uvs = false;
806  */
807  }
808 
809  if (_all_textures) {
810  pal->process_all(_redo_all, state_filename);
811  } else {
812  pal->process_command_line_eggs(_redo_all, state_filename);
813  }
814 
815  if (_optimal) {
816  // If we're asking for optimal packing, this also implies we want
817  // to resize the big empty palette images down.
818  pal->optimal_resize();
819  }
820 
821  if (_redo_eggs) {
822  if (!pal->read_stale_eggs(_redo_all)) {
823  okflag = false;
824  }
825  }
826 
827  if (okflag) {
828  pal->generate_images(_redo_all);
829 
830  if (_redo_eggs) {
831  // generate_images() might have made a few more stale egg files
832  // (particularly if a texture palette changed filenames).
833  if (!pal->read_stale_eggs(false)) {
834  okflag = false;
835  }
836  }
837  }
838 
839  if (okflag) {
840  if (!pal->write_eggs()) {
841  okflag = false;
842  }
843  }
844 
845  if (!_nodb) {
846  // Make up a temporary filename to write the state file to, then
847  // move the state file into place. We do this in case the user
848  // interrupts us (or we core dump) before we're done; that way we
849  // won't leave the state file incompletely written.
850  string dirname = state_filename.get_dirname();
851  if (dirname.empty()) {
852  dirname = ".";
853  }
854  Filename temp_filename = Filename::temporary(dirname, "pi");
855 
856  if (!state_file.open_write(temp_filename) ||
857  !state_file.write_object(pal)) {
858  nout << "Unable to write palettization information to "
859  << FilenameUnifier::make_user_filename(temp_filename)
860  << "\n";
861  exit(1);
862  }
863 
864  state_file.close();
865  state_filename.unlink();
866  if (!temp_filename.rename_to(state_filename)) {
867  nout << "Unable to rename temporary file "
868  << FilenameUnifier::make_user_filename(temp_filename) << " to "
869  << FilenameUnifier::make_user_filename(state_filename) << "\n";
870  exit(1);
871  }
872  }
873 
874  if (!okflag) {
875  exit(1);
876  }
877 }
878 
879 int
880 main(int argc, char *argv[]) {
881  // A call to pystub() to force libpystub.so to be linked in.
882  pystub();
883 
884  EggPalettize prog;
885  prog.parse_command_line(argc, argv);
886  prog.run();
887  return 0;
888 }
889 
890 
bool is_valid() const
Returns true if the palette information file was read correctly, or false if there was some error and...
Definition: palettizer.cxx:175
The principle public interface to reading and writing Bam disk files.
Definition: bamFile.h:45
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void report_statistics() const
Output a report of the palettization effectiveness, texture memory utilization, and so on...
Definition: palettizer.cxx:309
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
void set_extension(const string &s)
Replaces the file extension.
Definition: filename.cxx:837
bool write_object(const TypedWritable *object)
Writes the indicated object to the Bam file.
Definition: bamFile.cxx:258
bool open_read(const Filename &bam_filename, bool report_errors=true)
Attempts to open the indicated filename for reading.
Definition: bamFile.cxx:56
bool resolve()
This must be called after one or more objects have been read via calls to read_object() in order to r...
Definition: bamFile.cxx:128
This is the program wrapper for egg-palettize, but it mainly serves to read in all the command-line p...
Definition: eggPalettize.h:28
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
This is the main engine behind egg-palettize.
Definition: palettizer.h:43
static void set_txa_filename(const Filename &txa_filename)
Notes the filename the .txa file was found in.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
void process_all(bool force_texture_read, const Filename &state_filename)
Reprocesses all textures known.
Definition: palettizer.cxx:539
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:507
EggFile * get_egg_file(const string &name)
Returns the EggFile with the given name.
Definition: palettizer.cxx:795
const Filename & get_egg_filename() const
Returns the directory in which the egg file is considered to reside.
Definition: eggData.I:135
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
void set_severity(NotifySeverity severity)
Sets the severity level of messages that will be reported from this Category.
void close()
Closes the input or output stream.
Definition: bamFile.cxx:277
bool remove_egg_file(const string &name)
Removes the named egg file from the database, if it exists.
Definition: palettizer.cxx:815
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:337
A particular category of error messages.
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
Definition: filename.cxx:439
void read_txa_file(istream &txa_file, const string &txa_filename)
Reads in the .txa file and keeps it ready for matching textures and egg files.
Definition: palettizer.cxx:367
NotifyCategory * get_category(const string &basename, NotifyCategory *parent_category)
Finds or creates a new Category given the basename of the category and its parent in the category hie...
Definition: notify.cxx:206
static Filename make_user_filename(Filename filename)
Returns a new filename that&#39;s made relative to the current directory, suitable for reporting to the u...
This is a base class for a program that reads in a number of egg files, operates on them...
void report_pi() const
Output a verbose description of all the palettization information to standard output, for the user&#39;s perusal.
Definition: palettizer.cxx:187
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void show_text(const string &text)
Formats the indicated text to stderr with the known _terminal_width.
Definition: programBase.I:23
void all_params_set()
Called after all command line parameters have been set up, this is a hook to do whatever initializati...
Definition: palettizer.cxx:423
void reset_images()
Throws away all of the current PaletteImages, so that new ones may be created (and the packing made m...
Definition: palettizer.cxx:652
bool write_eggs()
Adjusts the egg files to reference the newly generated textures, and writes them out.
Definition: palettizer.cxx:757
string get_exec_command() const
Returns the command that invoked this program, as a shell-friendly string, suitable for pasting into ...
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2554
void generate_images(bool redo_all)
Actually generates the appropriate palette and unplaced texture images into the map directories...
Definition: palettizer.cxx:669
void set_dirname(const string &s)
Replaces the directory part of the filename.
Definition: filename.cxx:726
void process_command_line_eggs(bool force_texture_read, const Filename &state_filename)
Processes all the textures named in the _command_line_eggs, placing them on the appropriate palettes ...
Definition: palettizer.cxx:444
bool from_command_line(EggData *data, const Filename &source_filename, const Filename &dest_filename, const string &egg_comment)
Accepts the information about the egg file as supplied from the command line.
Definition: eggFile.cxx:63
void set_noabs(bool noabs)
Changes the current setting of the noabs flag.
Definition: palettizer.cxx:163
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
virtual bool handle_args(Args &args)
Does something with the additional arguments on the command line (after all the -options have been pa...
bool open_write(const Filename &bam_filename, bool report_errors=true)
Attempts to open the indicated file for writing.
Definition: bamFile.cxx:217
void optimal_resize()
Attempts to resize each PalettteImage down to its smallest possible size.
Definition: palettizer.cxx:636
bool read_stale_eggs(bool redo_all)
Reads in any egg file that is known to be stale, even if it was not listed on the command line...
Definition: palettizer.cxx:696
void add_command_line_egg(EggFile *egg_file)
Adds the indicated EggFile to the list of eggs that are considered to have been read on the command l...
Definition: palettizer.cxx:836
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
Definition: filename.cxx:2577
TypedWritable * read_object()
Reads and returns the next object from the Bam file, or NULL if the end of the file has been reached...
Definition: bamFile.cxx:96
An object that handles general error reporting to the user.
Definition: pnotify.h:38
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
This represents a single egg file known to the palettizer.
Definition: eggFile.h:39
static void set_rel_dirname(const Filename &rel_dirname)
Sets the name of the directory that texture filenames will be written relative to, when generating egg files.