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  */
226 bool EggPalettize::
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 }
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
The principle public interface to reading and writing Bam disk files.
Definition: bamFile.h:41
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void report_statistics() const
Output a report of the palettization effectiveness, texture memory utilization, and so on.
Definition: palettizer.cxx:297
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
bool write_object(const TypedWritable *object)
Writes the indicated object to the Bam file.
Definition: bamFile.cxx:226
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
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
This is the program wrapper for egg-palettize, but it mainly serves to read in all the command-line p...
Definition: eggPalettize.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:39
static void set_txa_filename(const Filename &txa_filename)
Notes the filename the .txa file was found in.
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:179
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
void process_all(bool force_texture_read, const Filename &state_filename)
Reprocesses all textures known.
Definition: palettizer.cxx:514
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
get_egg_filename
Returns the directory in which the egg file is considered to reside.
Definition: eggData.h:74
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
bool remove_egg_file(const std::string &name)
Removes the named egg file from the database, if it exists.
Definition: palettizer.cxx:766
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void close()
Closes the input or output stream.
Definition: bamFile.cxx:243
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:289
A particular category of error messages.
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
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
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...
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,...
Definition: palettizer.cxx:176
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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
bool write_eggs()
Adjusts the egg files to reference the newly generated textures, and writes them out.
Definition: palettizer.cxx:714
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_exec_command() const
Returns the command that invoked this program, as a shell-friendly string, suitable for pasting into ...
set_severity
Sets the severity level of messages that will be reported from this Category.
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2319
void generate_images(bool redo_all)
Actually generates the appropriate palette and unplaced texture images into the map directories.
Definition: palettizer.cxx:633
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 set_noabs(bool noabs)
Changes the current setting of the noabs flag.
Definition: palettizer.cxx:158
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
void set_dirname(const std::string &s)
Replaces the directory part of the filename.
Definition: filename.cxx:704
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
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:190
void optimal_resize()
Attempts to resize each PalettteImage down to its smallest possible size.
Definition: palettizer.cxx:606
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 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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void show_text(const std::string &text)
Formats the indicated text to stderr with the known _terminal_width.
Definition: programBase.I:18
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
Definition: filename.cxx:2339
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
An object that handles general error reporting to the user.
Definition: pnotify.h:33
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
This represents a single egg file known to the palettizer.
Definition: eggFile.h:36
static void set_rel_dirname(const Filename &rel_dirname)
Sets the name of the directory that texture filenames will be written relative to,...
EggFile * get_egg_file(const std::string &name)
Returns the EggFile with the given name.
Definition: palettizer.cxx:749