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