Panda3D

eggPalettize.cxx

00001 // Filename: eggPalettize.cxx
00002 // Created by:  drose (28Nov00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "eggPalettize.h"
00016 #include "palettizer.h"
00017 #include "eggFile.h"
00018 #include "pal_string_utils.h"
00019 #include "filenameUnifier.h"
00020 
00021 #include "dcast.h"
00022 #include "eggData.h"
00023 #include "bamFile.h"
00024 #include "pnotify.h"
00025 #include "notifyCategory.h"
00026 #include "notifySeverity.h"
00027 #include "pystub.h"
00028 
00029 #include <stdio.h>
00030 
00031 ////////////////////////////////////////////////////////////////////
00032 //     Function: EggPalettize::Constructor
00033 //       Access: Public
00034 //  Description:
00035 ////////////////////////////////////////////////////////////////////
00036 EggPalettize::
00037 EggPalettize() : EggMultiFilter(true) {
00038   set_program_description
00039     ("egg-palettize attempts to pack several texture maps from various models "
00040      "together into one or more palette images, for improved rendering performance "
00041      "and ease of texture management.  It can also resize textures and convert "
00042      "them to another image file format, whether or not they are actually "
00043      "placed on a palette, and can manage some "
00044      "simple texture properties, like mipmapping and rendering "
00045      "format.\n\n"
00046 
00047      "egg-palettize reads a texture attributes file, usually named "
00048      "textures.txa, which contains instructions from the user about "
00049      "resizing particular textures.  Type egg-palettize -H for an "
00050      "introduction to the syntax of this file.\n\n"
00051 
00052      "The palettization information from previous runs is recorded in a file "
00053      "named textures.boo (assuming the attributes file is named "
00054      "textures.txa); a complete record of every egg file and every texture "
00055      "that has been referenced is kept here.  This allows the program "
00056      "to intelligently manage the multiple egg files that may reference "
00057      "the textures in question.");
00058 
00059 
00060   clear_runlines();
00061   add_runline("[opts] file.egg [file.egg ...]");
00062 
00063   // We always have EggMultiBase's -f on: force complete load.  In
00064   // fact, we use -f for our own purposes, below.
00065   remove_option("f");
00066   _force_complete = true;
00067 
00068   add_option
00069     ("af", "filename", 0,
00070      "Read the indicated file as the .txa file.  The default is textures.txa.",
00071      &EggPalettize::dispatch_filename, &_got_txa_filename, &_txa_filename);
00072 
00073   add_option
00074     ("a", "filename", 0,
00075      "Deprecated option.  This is the same as -af.",
00076      &EggPalettize::dispatch_filename, &_got_txa_filename, &_txa_filename);
00077 
00078   add_option
00079     ("as", "script", 0,
00080      "Accept the script specified on the command line as the contents of the "
00081      ".txa file, instead of reading a file on disk.  This implies -nodb and "
00082      "-opt.",
00083      &EggPalettize::dispatch_string, &_got_txa_script, &_txa_script);
00084 
00085   add_option
00086     ("nodb", "", 0,
00087      "Don't read or record the state information to a .boo file.  By default, "
00088      "the palettization information is recorded so it can be preserved "
00089      "between multiple invocations of egg-palettize.  If you specify this "
00090      "parameter, all the egg files to be palettized together must be "
00091      "named at the same time.  This also implies -opt, since there's no point "
00092      "in not making an optimal packing if you won't be preserving the "
00093      "state for future adjustments.",
00094      &EggPalettize::dispatch_none, &_nodb);
00095 
00096   add_option
00097     ("tn", "pattern", 0,
00098      "Specify the name to generate for each palette image.  The string should "
00099      "contain %g for the group name, %p for the page name, and %i for the "
00100      "index within the page.  The extension is inferred from the image "
00101      "type.  The default is '%g_palette_%p_%i'.",
00102      &EggPalettize::dispatch_string, &_got_generated_image_pattern,
00103      &_generated_image_pattern);
00104 
00105   add_option
00106     ("pi", "", 0,
00107      "Do not process anything, but instead report the detailed palettization "
00108      "information written in the state file.",
00109      &EggPalettize::dispatch_none, &_report_pi);
00110 
00111   add_option
00112     ("s", "", 0,
00113      "Do not process anything, but report statistics on palette "
00114      "and texture utilization from the state file.",
00115      &EggPalettize::dispatch_none, &_report_statistics);
00116 
00117   add_option
00118     ("R", "", 0,
00119      "Remove the named egg files from the previously-generated state data "
00120      "file.",
00121      &EggPalettize::dispatch_none, &_remove_eggs);
00122 
00123   // We redefine -d using add_option() instead of redescribe_option()
00124   // so it gets listed along with these other options that relate.
00125   add_option
00126     ("d", "dirname", 0,
00127      "The directory in which to write the palettized egg files.  This is "
00128      "only necessary if more than one egg file is processed at the same "
00129      "time; if it is included, each egg file will be processed and written "
00130      "into the indicated directory.",
00131      &EggPalettize::dispatch_filename, &_got_output_dirname, &_output_dirname);
00132   add_option
00133     ("dm", "dirname", 0,
00134      "The directory in which to place all maps: generated palettes, "
00135      "as well as images which were not placed on palettes "
00136      "(but may have been resized).  If this contains the string %g, "
00137      "this will be replaced with the 'dir' string associated with a "
00138      "palette group; see egg-palettize -H.",
00139      &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
00140   add_option
00141     ("ds", "dirname", 0,
00142      "The directory to write palette shadow images to.  These are working "
00143      "copies of the palette images, useful when the palette image type is "
00144      "a lossy-compression type like JPEG; you can avoid generational loss "
00145      "of quality on the palette images with each pass through the palettes "
00146      "by storing these extra shadow images in a lossless image type.  This "
00147      "directory is only used if the :shadowtype keyword appears in the .txa "
00148      "file.",
00149      &EggPalettize::dispatch_filename, &_got_shadow_dirname, &_shadow_dirname);
00150   add_option
00151     ("dr", "dirname", 0,
00152      "The directory to make map filenames relative to when writing egg "
00153      "files.  If specified, this should be an initial substring of -dm.",
00154      &EggPalettize::dispatch_filename, &_got_rel_dirname, &_rel_dirname);
00155   add_option
00156     ("g", "group", 0,
00157      "The default palette group that egg files will be assigned to if they "
00158      "are not explicitly assigned to any other group.",
00159      &EggPalettize::dispatch_string, &_got_default_groupname, &_default_groupname);
00160   add_option
00161     ("gdir", "name", 0,
00162      "The \"dir\" string to associate with the default palette group "
00163      "specified with -g, if no other dir name is given in the .txa file.",
00164      &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
00165 
00166   add_option
00167     ("all", "", 0,
00168      "Consider all the textures referenced in all egg files that have "
00169      "ever been palettized, not just the egg files that appear on "
00170      "the command line.",
00171      &EggPalettize::dispatch_none, &_all_textures);
00172   add_option
00173     ("egg", "", 0,
00174      "Regenerate all egg files that need modification, even those that "
00175      "aren't named on the command line.",
00176      &EggPalettize::dispatch_none, &_redo_eggs);
00177   add_option
00178     ("redo", "", 0,
00179      "Force a regeneration of each image from its original source(s).  "
00180      "When used in conjunction with -egg, this also forces each egg file to "
00181      "be regenerated.",
00182      &EggPalettize::dispatch_none, &_redo_all);
00183   add_option
00184     ("opt", "", 0,
00185      "Force an optimal packing.  By default, textures are added to "
00186      "existing palettes without disturbing them, which can lead to "
00187      "suboptimal packing.  Including this switch forces the palettes "
00188      "to be rebuilt if necessary to optimize the packing, but this "
00189      "may invalidate other egg files which share this palette.",
00190      &EggPalettize::dispatch_none, &_optimal);
00191   add_option
00192     ("omitall", "", 0,
00193      "Re-enables the flag to omit all textures.  This flag is normally on "
00194      "by default, causing nothing actually to be palettized, until the "
00195      "first time egg-palettize is run with the -opt flag, which turns off "
00196      "the omitall flag and thenceforth allows textures to be combined "
00197      "into palettes.  Specifying this flag restores the original behavior "
00198      "of keeping every texture as a separate image (which is convenient for "
00199      "development).",
00200      &EggPalettize::dispatch_none, &_omitall);
00201 
00202   // This isn't even implemented yet.  Presently, we never lock anyway.
00203   // Dangerous, but hard to implement reliable file locking across
00204   // NFS/Samba and between multiple OS's.
00205   /*
00206   add_option
00207     ("nolock", "", 0,
00208      "Don't attempt to grab a file lock on the .txa file.  Use "
00209      "with extreme caution, as multiple processes running on the same "
00210      ".txa file may overwrite each other.  Use this only if the lock "
00211      "cannot be achieved for some reason.",
00212      &EggPalettize::dispatch_none, &_dont_lock_txa);
00213   */
00214 
00215   add_option
00216     ("H", "", 0,
00217      "Describe the syntax of the attributes file.",
00218      &EggPalettize::dispatch_none, &_describe_input_file);
00219 
00220   _txa_filename = "textures.txa";
00221 }
00222 
00223 
00224 ////////////////////////////////////////////////////////////////////
00225 //     Function: EggPalettize::handle_args
00226 //       Access: Protected, Virtual
00227 //  Description: Does something with the additional arguments on the
00228 //               command line (after all the -options have been
00229 //               parsed).  Returns true if the arguments are good,
00230 //               false otherwise.
00231 ////////////////////////////////////////////////////////////////////
00232 bool EggPalettize::
00233 handle_args(ProgramBase::Args &args) {
00234   if (_describe_input_file) {
00235     describe_input_file();
00236     exit(1);
00237   }
00238 
00239   if (_remove_eggs) {
00240     // If we're removing these egg files from the database, we don't
00241     // want to try to load them up.  Instead, just save the filenames.
00242     _remove_egg_list = args;
00243     return true;
00244   }
00245 
00246   // Otherwise, load the named egg files up normally.
00247   return EggMultiFilter::handle_args(args);
00248 }
00249 
00250 ////////////////////////////////////////////////////////////////////
00251 //     Function: EggPalettize::describe_input_file
00252 //       Access: Public
00253 //  Description:
00254 ////////////////////////////////////////////////////////////////////
00255 void EggPalettize::
00256 describe_input_file() {
00257   nout <<
00258     "An attributes file consists mostly of lines describing desired sizes of "
00259     "texture maps.  The format resembles, but is not identical to, that of "
00260     "the qtess input file.  Examples:\n\n"
00261 
00262     "  texturename.rgb : 64 64\n"
00263     "  texture-a.rgb texture-b.rgb : 32 16 margin 2\n"
00264     "  *.rgb : 50% cont\n"
00265     "  eyelids.rgb : 16 16 omit\n\n"
00266 
00267     "In general, each line consists of one or more filenames (and can "
00268     "contain shell globbing characters like '*' or '?'), and a colon "
00269     "followed by a size request.  For each texture appearing in an egg "
00270     "file, the input list is scanned from the beginning and the first "
00271     "line that matches the filename defines the size of the texture, as "
00272     "well as other properties associated with the texture.\n\n"
00273 
00274     "A size request is most often a pair of numbers, giving a specific x y "
00275     "size of the texture.  A third number may also be supplied, giving a "
00276     "specific number of channels to convert to (for instance, to force an "
00277     "image to a 64x64 grayscale image, set its size to 64 64 1).  "
00278     "Alternatively, a percentage scaling may be specified, e.g. 30%.  The "
00279     "requested size need not be a power of 2.\n\n"
00280 
00281     "Other valid keywords that may be specified on the same line with the "
00282     "texture are:\n\n";
00283 
00284   show_text("  omit", 10,
00285             "This indicates that the texture should not be placed on any "
00286             "palette image.  It may still be resized, and it will in any "
00287             "case be copied into the install directory.\n\n");
00288 
00289   show_text("  margin i", 10,
00290             "This specifies the number of pixels that should be written "
00291             "around the border of the texture when it is placed in a "
00292             "palette image; i is the integer number of pixels.  The "
00293             "use of a margin helps cut down on color bleed "
00294             "from neighboring images.  If the texture does "
00295             "not end up placed in a palette image, the "
00296             "margin is not used.  If not specified, the default margin is "
00297             "used, which is specified by the :margin command (see below).\n\n");
00298 
00299   show_text("  coverage f", 10,
00300             "This parameter specifies the maximum coverage to allow for this "
00301             "particular texture before rejecting it "
00302             "from the palette.  If not specified, the default is "
00303             "specified by the :coverage command (see below).\n\n");
00304 
00305   nout << "  nearest\n"
00306        << "  linear\n";
00307   show_text("  mipmap", 10,
00308             "One of these options may be used to force the texture to use "
00309             "a particular minfilter/magfilter sampling mode.  If this is not "
00310             "specified, the sampling mode specified in the egg file is "
00311             "used.  Textures that use different sampling modes cannot "
00312             "be placed together on the same palette images.\n\n");
00313 
00314   show_text("  rgba", 10,
00315             "This specifies format 'rgba' should be in effect for this "
00316             "particular texture.  Any valid egg texture format, such as "
00317             "rgba, rgba12, rgba8, rgb5, luminance, etc. may be specified.  "
00318             "If nothing is specified, the format specified in the egg file "
00319             "is used.  The format will automatically be downgraded to match "
00320             "the number of channels in the texture image; e.g. rgba will "
00321             "automatically be converted to rgb for a three-channel image.  "
00322             "As with the filter modes above, textures that use different "
00323             "formats cannot be placed together on the same palette "
00324             "images.\n\n");
00325 
00326   show_text("  force-rgba", 10,
00327             "This specifies a particular format, as above, that should be "
00328             "in effect for this texture, but it will not be downgraded to "
00329             "match the number of channels.  As above, any valid egg texture "
00330             "format may be used, e.g. force-rgba12, force-rgb5, etc.\n\n");
00331 
00332   show_text("  keep-format", 10,
00333             "This specifies that the image format requested by an egg file "
00334             "should be exactly preserved, without attempting to optimize "
00335             "it by, for instance, automatically downgrading.\n\n");
00336 
00337   show_text("  generic", 10,
00338             "Specifies that any image format requested by an egg file "
00339             "that requests a particular bitdepth should be replaced by "
00340             "its generic equivalent, e.g. rgba8 should become rgba.\n\n");
00341 
00342   show_text("  (alpha mode)", 10,
00343             "A particular alpha mode may be applied to a texture by naming "
00344             "the alpha mode.  This may be any valid egg alpha mode, e.g. "
00345             "blend, binary, ms, or dual.\n\n");
00346 
00347   show_text("  repeat_u, repeat_v, clamp_u, clamp_v", 10,
00348             "Explcitly specify whether the source texture should repeat or "
00349             "clamp in each direction.  Although palette images are always "
00350             "clamped, this will affect the pixels that are painted into "
00351             "the palette image.\n\n");
00352 
00353   show_text("  (image type)", 10,
00354             "A texture may be converted to a particular image type, for "
00355             "instance jpg or rgb, by naming the type.  If present, this "
00356             "overrides the :imagetype command, described below.  As with "
00357             ":imagetype, you may also specify two type names separated "
00358             "by a comma, to indicate that a different file should be written "
00359             "for the color and alpha components.\n\n");
00360 
00361   show_text("  (group name)", 10,
00362             "A texture may also be assigned to a specific group by naming "
00363             "the group.  The groups are defined using the :group command "
00364             "(see below).  Normally, textures are not assigned directly "
00365             "to groups; instead, it is more useful to assign the egg files "
00366             "they are referenced in to groups; see below.\n\n");
00367 
00368   show_text("  cont", 10,
00369             "Normally, a texture file (or egg file) scans the lines in the "
00370             "attributes file from the top, and stops on the first line that "
00371             "matches its name.  If the keyword 'cont' is included on the "
00372             "line, however, the texture will apply the properties given "
00373             "on the line, and then continue scanning.  This trick may be "
00374             "used to specify general parameters for all files while still "
00375             "allowing the texture to match a more specific line below.\n\n");
00376 
00377   nout <<
00378     "The attributes file may also assign egg files to various "
00379     "named palette groups.  The syntax is similar to the above:\n\n"
00380 
00381     "  car-blue.egg : main\n"
00382     "  road.egg house.egg : main\n"
00383     "  plane.egg : phase_2 main\n"
00384     "  *.egg : phase_2\n\n"
00385 
00386     "Any number of egg files may be named on one line, and the set of "
00387     "named egg files may be simultaneously assigned to one or more groups.  "
00388     "Each group must have been previously defined using the :group command "
00389     "(see below).  Each texture that is referenced by a given "
00390     "egg file will be palettized "
00391     "into at least one of the groups assigned to the egg file.\n\n"
00392 
00393     "Finally, there are a number of special commands that may appear in the "
00394     "attributes file; some of these have been alluded to in the above "
00395     "comments.  These commands typically specify global parameters or "
00396     "palettization options.  The command names begin with a colon to "
00397     "distinguish them from other kinds of lines.  Each command must "
00398     "appear on a line by itself.  The commands are:\n\n";
00399 
00400   show_text("  :palette xsize ysize", 10,
00401             "This specifies the size of the palette images to be "
00402             "created.  The default is 512 by 512.\n\n");
00403 
00404   show_text("  :margin msize", 10,
00405             "This specifies the amount of default margin to apply to all "
00406             "textures that are placed within a palette image.  The margin "
00407             "is a number of additional pixels that are written around the "
00408             "texture image to help prevent color bleeding between "
00409             "neighboring images within the same palette.  The default "
00410             "is 2.\n\n");
00411 
00412   show_text("  :background r g b a", 10,
00413             "Specifies the background color of the generated palette "
00414             "images.  Normally, this is black, and it doesn't matter much "
00415             "since the background color is, by definition, the color "
00416             "of the palette images where nothing is used.\n\n");
00417 
00418   show_text("  :coverage area", 10,
00419             "The 'coverage' of a texture refers to the fraction of "
00420             "the area in the texture image that is actually used, according "
00421             "to the UV's that appear in the various egg files.  If a texture's "
00422             "coverage is less than 1, only some of the texture image is used "
00423             "(and only this part will be written to the palette).  If the "
00424             "coverage is greater than 1, the texture repeats that number of "
00425             "times.  A repeating texture may still be palettized by writing "
00426             "the required number of copies into the palette image, according "
00427             "to the coverage area.\n\n"
00428 
00429             "This command specifies the maximum coverage to allow for any "
00430             "texture before rejecting it from the palette.  It may be any "
00431             "floating-point number greater than zero.  Set this to 1 "
00432             "to avoid palettizing repeating textures altogether.  This may "
00433             "also be overridden for a particular texture using the 'coverage' "
00434             "keyword on the texture line.\n\n");
00435 
00436   show_text("  :powertwo flag", 10,
00437             "Specifies whether textures should be forced to a power of two "
00438             "size when they are not placed within a palette.  Use 1 for true, "
00439             "to force textures to a power of two; or 0 to leave them exactly "
00440             "the size they are specified.  The default is true.\n\n");
00441 
00442   show_text("  :round fraction fuzz", 10,
00443             "When the coverage area is computed, it may optionally be "
00444             "rounded up to the next sizeable unit before placing the "
00445             "texture within the palette.  This helps reduce constant "
00446             "repalettization caused by slight differences in coverage "
00447             "between egg files.  For instance, say file a.egg references a "
00448             "texture with a coverage of 0.91, and then later file b.egg "
00449             "is discovered to reference the same texture with a coverage of "
00450             "0.92.  If the texture was already palettized with the original "
00451             "coverage of 0.91, it must now be moved in the palette.\n\n"
00452 
00453             "Rounding the coverage area up to some fixed unit reduces this "
00454             "problem.  For instance, if you specified a value 0.5 for "
00455             "fraction in the above command, it would round both of these "
00456             "values up to the next half-unit, or 1.0.\n\n"
00457 
00458             "The second number is a fuzz factor, and should be a small "
00459             "number; if the coverage area is just slightly larger than "
00460             "the last unit (within the fuzz factor), it is rounded down "
00461             "instead of up.  This is intended to prevent UV coordinates "
00462             "that are just slightly out of the range [0, 1] (which happens "
00463             "fairly often) from forcing the palettization area all the "
00464             "way up to the next stop.\n\n"
00465 
00466             "The default if this is unspecified is 0.1 0.01.  That is, "
00467             "round up to the next tenth, unless within a hundredth of the "
00468             "last tenth.  To disable rounding, specify ':round no'.  "
00469             "Rounding is implicitly disabled when you run with the -opt "
00470             "command line option.\n\n");
00471 
00472   show_text("  :remap (never | group | poly)", 10,
00473             "Sometimes two different parts of an egg file may reference "
00474             "different regions of a repeating texture.  For instance, "
00475             "group A may reference UV coordinate values ranging from (0,5) "
00476             "to (1,6), for a coverage of 1.0, while group B references "
00477             "values ranging from (0,2) to (1,4), for a coverage of 2.0.  "
00478             "The maximum coverage used is only 2.0, and thus the texture "
00479             "only needs to appear in the palette twice, but the total range "
00480             "of UV's is from (0,2) to (1,6), causing an apparent coverage "
00481             "of 4.0.\n\n"
00482 
00483             "It's possible for egg-palettize to reduce this kind of mistake "
00484             "by remapping both groups of UV's so that they overlap.  This "
00485             "parameter specifies how this operation should be done.  If "
00486             "the option is 'never', remapping will not be performed; if "
00487             "'group', entire groups will be remapped as a unit, if 'poly', "
00488             "individual polygons within a group may be remapped.  This last "
00489             "option provides the greatest minimization of UV coverage, "
00490             "but possibly at the expense of triangle strips in the resulting "
00491             "model (since some vertices can no longer be shared).\n\n"
00492 
00493             "Sometimes, it may be necessary to be more restrictive on "
00494             "character geometry than on non-character geometry, because "
00495             "the cost of adding additional vertices on characters is "
00496             "greater.  You can specify a different kind of remapping for "
00497             "characters only, by using the keyword 'char' on the same line, "
00498             "e.g. ':remap group char never'.\n\n"
00499 
00500             "The default remap mode for all geometry, character or otherwise, "
00501             "if no remap mode is specified is 'poly'.\n\n");
00502 
00503   show_text("  :imagetype type[,alpha_type]", 10,
00504             "This specifies the default type of image file that should be "
00505             "generated for each palette image and for each unplaced texture "
00506             "copied into the install directory.  This may be overridden for "
00507             "a particular texture by specifying the image type on the "
00508             "texture line.\n\n"
00509 
00510             "If two image type names separate by a comma are given, it means "
00511             "to generate a second file of the second type for the alpha "
00512             "channel, for images that require an alpha channel.  This allows "
00513             "support for image file formats that do not support alpha "
00514             "(for instance, JPEG).\n\n");
00515 
00516   show_text("  :shadowtype type[,alpha_type]", 10,
00517             "When generating palette images, egg-palettize sometimes has to "
00518             "read and write the same palette image repeatedly.  If the "
00519             "palette image is stored in a lossy file format (like JPEG, see "
00520             ":imagetype), this can eventually lead to degradation of the "
00521             "palette images.  As a workaround, egg-palettize can store "
00522             "its working copies of the palette images in lossless shadow "
00523             "images.  Specify this to enable this feature; give it the "
00524             "name of a lossless image file format.  The shadow images will "
00525             "be written to the directory specified by -ds on the command "
00526             "line.\n\n");
00527 
00528   show_text("  :group groupname [dir dirname] [on group1 group2 ...] [includes group1 group2 ...]", 10,
00529             "This defines a palette group, a logical division of textures.  "
00530             "Each texture is assigned to one or more palette groups before "
00531             "being placed in any palette image; the palette images are "
00532             "tied to the groups.\n\n"
00533 
00534             "The optional parameter 'dir' specifies a directory name to "
00535             "associate with this group.  This name is substituted in for "
00536             "the string '%g' when it appears in the map directory name "
00537             "specified on the command line with -dm; this may be used to "
00538             "install textures and palettes into different directories based "
00539             "on the groups they are assigned to.\n\n"
00540 
00541             "Palette groups can also be hierarchically related.  The "
00542             "keyword 'on' specifies any number of groups that this "
00543             "palette group depends on; if a texture has already been "
00544             "assigned to one of this group's dependent groups, it will "
00545             "not need to be assigned to this group.  This also implicitly "
00546             "specifies a dir if one has not already been specified.\n\n"
00547 
00548             "The keyword 'includes' names one or more groups that depend "
00549             "on this group.\n\n");
00550 
00551   show_text("  :textureswap groupname texturename0 texturename1 [texturename2 ...]", 10,
00552             "This option builds a set of matching, interchangeable palette images. "
00553             "All palette images in the set share the same internal texture layout. "
00554             "The intention is to be able to swap palette images out at runtime, "
00555             "to replace entire sets of textures on a model in one operation. "
00556             "The textures named by this option indicate the texture images "
00557             "which are similar to each other, and which all should be assigned "
00558             "to the same placement on the different palette images: "
00559             "texturename0 will be assigned to palette image 0, "
00560             "texturename1 to the same position on palette image 1, "
00561             "texturename2 to the same position on palette image 2, and so on. "
00562             "To define a complete palette image, you must repeat this option "
00563             "several times to associate all of the similar texture images.\n\n");
00564 
00565   nout <<
00566     "Comments may appear freely throughout the file, and are set off by a "
00567     "hash mark (#).\n\n";
00568 }
00569 
00570 
00571 ////////////////////////////////////////////////////////////////////
00572 //     Function: EggPalettize::run
00573 //       Access: Public
00574 //  Description:
00575 ////////////////////////////////////////////////////////////////////
00576 void EggPalettize::
00577 run() {
00578   // Fiddle with the loader severity, so we don't confuse the user
00579   // with spurious "reading" and "writing" messages about the state
00580   // file.  If the severity is currently NS_info (the default), set it
00581   // to NS_warning instead.
00582   Notify *notify = Notify::ptr();
00583   NotifyCategory *loader_cat = notify->get_category(":loader");
00584   if (loader_cat != (NotifyCategory *)NULL &&
00585       loader_cat->get_severity() == NS_info) {
00586     loader_cat->set_severity(NS_warning);
00587   }
00588 
00589   Filename state_filename;
00590   BamFile state_file;
00591 
00592   if (_got_txa_script) {
00593     // If we got a command-line script instead of a .txa file, we
00594     // won't be encoding a .boo file either.
00595     _nodb = true;
00596 
00597   } else {
00598     // Look for the .txa file.
00599     if (!_txa_filename.exists() && !_got_txa_filename) {
00600       // If we did not specify a filename, and the default filename of
00601       // "textures.txa" doesn't exist, try looking in src/maps, as
00602       // another likely possibility.
00603       Filename maybe = _txa_filename;
00604       maybe.set_dirname("src/maps");
00605       if (maybe.exists()) {
00606         _txa_filename = maybe;
00607       }
00608     }
00609     
00610     if (!_txa_filename.exists()) {
00611       nout << FilenameUnifier::make_user_filename(_txa_filename)
00612            << " does not exist; cannot run.\n";
00613       exit(1);
00614     }
00615     
00616     FilenameUnifier::set_txa_filename(_txa_filename);
00617 
00618     state_filename = _txa_filename;
00619     state_filename.set_extension("boo");
00620   }
00621 
00622   if (_nodb) {
00623     // -nodb means don't attempt to read textures.boo; in fact, don't
00624     // even bother reporting this absence to the user.
00625     pal = new Palettizer;
00626 
00627     // And -nodb implies -opt.
00628     _optimal = true;
00629 
00630   } else if (!state_filename.exists()) {
00631     nout << FilenameUnifier::make_user_filename(state_filename)
00632          << " does not exist; starting palettization from scratch.\n";
00633     pal = new Palettizer;
00634 
00635     // By default, the -omitall flag is true from the beginning.
00636     pal->_omit_everything = true;
00637 
00638   } else {
00639     // Read the Palettizer object from the Bam file written
00640     // previously.  This will recover all of the state saved from the
00641     // past session.
00642     nout << "Reading " << FilenameUnifier::make_user_filename(state_filename)
00643          << "\n";
00644 
00645     if (!state_file.open_read(state_filename)) {
00646       nout << FilenameUnifier::make_user_filename(state_filename)
00647            << " exists, but cannot be read.  Perhaps you should "
00648            << "remove it so a new one can be created.\n";
00649       exit(1);
00650     }
00651 
00652     TypedWritable *obj = state_file.read_object();
00653     if (obj == (TypedWritable *)NULL || !state_file.resolve()) {
00654       nout << FilenameUnifier::make_user_filename(state_filename)
00655            << " exists, but appears to be corrupt.  Perhaps you "
00656            << "should remove it so a new one can be created.\n";
00657       exit(1);
00658     }
00659 
00660     if (!obj->is_of_type(Palettizer::get_class_type())) {
00661       nout << FilenameUnifier::make_user_filename(state_filename)
00662            << " exists, but does not appear to be "
00663            << "an egg-palettize output file.  Perhaps you "
00664            << "should remove it so a new one can be created.\n";
00665       exit(1);
00666     }
00667 
00668     state_file.close();
00669 
00670     pal = DCAST(Palettizer, obj);
00671 
00672     if (pal->_read_pi_version > pal->_pi_version) {
00673       nout << FilenameUnifier::make_user_filename(state_filename)
00674            << " was written by a more recent version of egg-palettize "
00675            << "than this one.  You will need to update your egg-palettize.\n";
00676       exit(1);
00677     }
00678 
00679     if (pal->_read_pi_version < pal->_min_pi_version) {
00680       nout << FilenameUnifier::make_user_filename(state_filename)
00681            << " was written by an old version of egg-palettize.\n\n"
00682            << "You will need to make undo-pal (or simply remove the file "
00683            << FilenameUnifier::make_user_filename(state_filename)
00684            << " and try again).\n\n";
00685       exit(1);
00686     }
00687 
00688     if (!pal->is_valid()) {
00689       nout << FilenameUnifier::make_user_filename(state_filename)
00690            << " could not be properly read.  You will need to remove it.\n";
00691       exit(1);
00692     }
00693   }
00694 
00695   pal->set_noabs(_noabs);
00696 
00697   if (_report_pi) {
00698     pal->report_pi();
00699     exit(0);
00700   }
00701 
00702   if (_report_statistics) {
00703     pal->report_statistics();
00704     exit(0);
00705   }
00706 
00707   bool okflag = true;
00708 
00709   if (_got_txa_script) {
00710     istringstream txa_script(_txa_script);
00711     pal->read_txa_file(txa_script, "command line");
00712 
00713   } else {
00714     _txa_filename.set_text();
00715     ifstream txa_file;
00716     if (!_txa_filename.open_read(txa_file)) {
00717       nout << "Unable to open " << _txa_filename << "\n";
00718       exit(1);
00719     }
00720     pal->read_txa_file(txa_file, _txa_filename);
00721   }
00722 
00723   if (_got_generated_image_pattern) {
00724     pal->_generated_image_pattern = _generated_image_pattern;
00725   }
00726 
00727   if (_got_default_groupname) {
00728     pal->_default_groupname = _default_groupname;
00729   } else {
00730     pal->_default_groupname = _txa_filename.get_basename_wo_extension();
00731   }
00732 
00733   if (_got_default_groupdir) {
00734     pal->_default_groupdir = _default_groupdir;
00735   }
00736 
00737   if (_got_map_dirname) {
00738     pal->_map_dirname = _map_dirname;
00739   }
00740   if (_got_shadow_dirname) {
00741     pal->_shadow_dirname = _shadow_dirname;
00742   }
00743   if (_got_rel_dirname) {
00744     pal->_rel_dirname = _rel_dirname;
00745     FilenameUnifier::set_rel_dirname(_rel_dirname);
00746   }
00747 
00748   // We only omit solitary textures from palettes if we're running in
00749   // optimal mode.  Otherwise, we're likely to invalidate old egg
00750   // files by changing a texture from solitary to nonsolitary state or
00751   // vice-versa.
00752   pal->_omit_solitary = _optimal;
00753 
00754   if (_omitall) {
00755     pal->_omit_everything = true;
00756   } else if (_optimal) {
00757     pal->_omit_everything = false;
00758   }
00759 
00760   pal->all_params_set();
00761 
00762   // Remove any files named for removal.
00763   Args::const_iterator ai;
00764   for (ai = _remove_egg_list.begin(); ai != _remove_egg_list.end(); ++ai) {
00765     Filename filename = (*ai);
00766     pal->remove_egg_file(filename.get_basename());
00767   }
00768 
00769   // And process the egg files named for addition.
00770   bool all_eggs_valid = true;
00771 
00772   string egg_comment = get_exec_command();
00773   Eggs::const_iterator ei;
00774   for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
00775     EggData *egg_data = (*ei);
00776     Filename source_filename = egg_data->get_egg_filename();
00777     Filename dest_filename = get_output_filename(source_filename);
00778     string name = source_filename.get_basename();
00779 
00780     EggFile *egg_file = pal->get_egg_file(name);
00781     if (!egg_file->from_command_line(egg_data, source_filename, dest_filename,
00782                                      egg_comment)) {
00783       all_eggs_valid = false;
00784 
00785     } else {
00786       pal->add_command_line_egg(egg_file);
00787     }
00788   }
00789 
00790   if (!all_eggs_valid) {
00791     nout << "Errors reading egg file(s).\n";
00792     exit(1);
00793   }
00794 
00795   if (_optimal) {
00796     // If we're asking for an optimal packing, throw away the old
00797     // packing and start fresh.
00798     pal->reset_images();
00799     _all_textures = true;
00800 
00801     /* Asad: I disagree: unless :round is set to no from textures.txa, we
00802        should always leave the _round_uvs to default.
00803     // Also turn off the rounding-up of UV's for this purpose.
00804     pal->_round_uvs = false;
00805     */
00806   }
00807 
00808   if (_all_textures) {
00809     pal->process_all(_redo_all, state_filename);
00810   } else {
00811     pal->process_command_line_eggs(_redo_all, state_filename);
00812   }
00813 
00814   if (_optimal) {
00815     // If we're asking for optimal packing, this also implies we want
00816     // to resize the big empty palette images down.
00817     pal->optimal_resize();
00818   }
00819 
00820   if (_redo_eggs) {
00821     if (!pal->read_stale_eggs(_redo_all)) {
00822       okflag = false;
00823     }
00824   }
00825 
00826   if (okflag) {
00827     pal->generate_images(_redo_all);
00828 
00829     if (_redo_eggs) {
00830       // generate_images() might have made a few more stale egg files
00831       // (particularly if a texture palette changed filenames).
00832       if (!pal->read_stale_eggs(false)) {
00833         okflag = false;
00834       }
00835     }
00836   }
00837     
00838   if (okflag) {
00839     if (!pal->write_eggs()) {
00840       okflag = false;
00841     }
00842   }
00843 
00844   if (!_nodb) {
00845     // Make up a temporary filename to write the state file to, then
00846     // move the state file into place.  We do this in case the user
00847     // interrupts us (or we core dump) before we're done; that way we
00848     // won't leave the state file incompletely written.
00849     string dirname = state_filename.get_dirname();
00850     if (dirname.empty()) {
00851       dirname = ".";
00852     }
00853     Filename temp_filename = Filename::temporary(dirname, "pi");
00854     
00855     if (!state_file.open_write(temp_filename) ||
00856         !state_file.write_object(pal)) {
00857       nout << "Unable to write palettization information to "
00858            << FilenameUnifier::make_user_filename(temp_filename)
00859            << "\n";
00860       exit(1);
00861     }
00862     
00863     state_file.close();
00864     state_filename.unlink();
00865     if (!temp_filename.rename_to(state_filename)) {
00866       nout << "Unable to rename temporary file "
00867            << FilenameUnifier::make_user_filename(temp_filename) << " to "
00868            << FilenameUnifier::make_user_filename(state_filename) << "\n";
00869       exit(1);
00870     }
00871   }
00872 
00873   if (!okflag) {
00874     exit(1);
00875   }
00876 }
00877 
00878 int
00879 main(int argc, char *argv[]) {
00880   // A call to pystub() to force libpystub.so to be linked in.
00881   pystub();
00882 
00883   EggPalettize prog;
00884   prog.parse_command_line(argc, argv);
00885   prog.run();
00886   return 0;
00887 }
00888 
00889 
 All Classes Functions Variables Enumerations