Panda3D
|
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