00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "eggToBam.h"
00016
00017 #include "config_util.h"
00018 #include "bamFile.h"
00019 #include "load_egg_file.h"
00020 #include "config_egg2pg.h"
00021 #include "config_gobj.h"
00022 #include "config_chan.h"
00023 #include "pandaNode.h"
00024 #include "geomNode.h"
00025 #include "renderState.h"
00026 #include "textureAttrib.h"
00027 #include "dcast.h"
00028 #include "graphicsPipeSelection.h"
00029 #include "graphicsEngine.h"
00030 #include "graphicsBuffer.h"
00031 #include "graphicsStateGuardian.h"
00032 #include "load_prc_file.h"
00033 #include "windowProperties.h"
00034 #include "frameBufferProperties.h"
00035 #include "pystub.h"
00036
00037
00038
00039
00040
00041
00042 EggToBam::
00043 EggToBam() :
00044 EggToSomething("Bam", ".bam", true, false)
00045 {
00046 set_program_description
00047 ("This program reads Egg files and outputs Bam files, the binary format "
00048 "suitable for direct loading of animation and models into Panda. Bam "
00049 "files are tied to a particular version of Panda, so should not be "
00050 "considered replacements for egg files, but they tend to be smaller and "
00051 "load much faster than the equivalent egg files.");
00052
00053
00054
00055 remove_option("f");
00056
00057 add_path_replace_options();
00058 add_path_store_options();
00059
00060 add_option
00061 ("flatten", "flag", 0,
00062 "Specifies whether to flatten the egg hierarchy after it is loaded. "
00063 "If flag is zero, the egg hierarchy will not be flattened, but will "
00064 "instead be written to the bam file exactly as it is. If flag is "
00065 "non-zero, the hierarchy will be flattened so that unnecessary nodes "
00066 "(usually group nodes with only one child) are eliminated. The default "
00067 "if this is not specified is taken from the egg-flatten Config.prc "
00068 "variable.",
00069 &EggToBam::dispatch_int, &_has_egg_flatten, &_egg_flatten);
00070
00071 add_option
00072 ("combine-geoms", "flag", 0,
00073 "Specifies whether to combine sibling GeomNodes into a common GeomNode "
00074 "when possible. This flag is only respected if flatten, above, is also "
00075 "enabled (or implicitly true from the Config.prc file). The default if "
00076 "this is not specified is taken from the egg-combine-geoms Config.prc "
00077 "variable.",
00078 &EggToBam::dispatch_int, &_has_egg_combine_geoms, &_egg_combine_geoms);
00079
00080 add_option
00081 ("suppress-hidden", "flag", 0,
00082 "Specifies whether to suppress hidden geometry. If this is nonzero, "
00083 "egg geometry tagged as \"hidden\" will be removed from the final "
00084 "scene graph; otherwise, it will be preserved (but stashed). The "
00085 "default is nonzero, to remove it.",
00086 &EggToBam::dispatch_int, NULL, &_egg_suppress_hidden);
00087
00088 add_option
00089 ("ls", "", 0,
00090 "Writes a scene graph listing to standard output after the egg "
00091 "file has been loaded, showing the nodes that will be written out.",
00092 &EggToBam::dispatch_none, &_ls);
00093
00094 add_option
00095 ("C", "quality", 0,
00096 "Specify the quality level for lossy channel compression. If this "
00097 "is specified, the animation channels will be compressed at this "
00098 "quality level, which is normally an integer value between 0 and 100, "
00099 "inclusive, where higher numbers produce larger files with greater "
00100 "quality. Generally, 95 is the highest useful quality level. Use "
00101 "-NC (described below) to disable channel compression. If neither "
00102 "option is specified, the default comes from the Config.prc file.",
00103 &EggToBam::dispatch_int, &_has_compression_quality, &_compression_quality);
00104
00105 add_option
00106 ("NC", "", 0,
00107 "Turn off lossy compression of animation channels. Channels will be "
00108 "written exactly as they are, losslessly.",
00109 &EggToBam::dispatch_none, &_compression_off);
00110
00111 add_option
00112 ("rawtex", "", 0,
00113 "Record texture data directly in the bam file, instead of storing "
00114 "a reference to the texture elsewhere on disk. The textures are "
00115 "stored uncompressed, unless -ctex is also specified. "
00116 "A particular texture that is encoded into "
00117 "multiple different bam files in this way cannot be unified into "
00118 "the same part of texture memory if the different bam files are loaded "
00119 "together. That being said, this can sometimes be a convenient "
00120 "way to ensure the bam file is completely self-contained.",
00121 &EggToBam::dispatch_none, &_tex_rawdata);
00122
00123 add_option
00124 ("txo", "", 0,
00125 "Rather than writing texture data directly into the bam file, as in "
00126 "-rawtex, create a texture object for each referenced texture. A "
00127 "texture object is a kind of mini-bam file, with a .txo extension, "
00128 "that contains all of the data needed to recreate a texture, including "
00129 "its image contents, filter and wrap settings, and so on. 3-D textures "
00130 "and cube maps can also be represented in a single .txo file. Texture "
00131 "object files, like bam files, are tied to a particular version of "
00132 "Panda.",
00133 &EggToBam::dispatch_none, &_tex_txo);
00134
00135 #ifdef HAVE_ZLIB
00136 add_option
00137 ("txopz", "", 0,
00138 "In addition to writing texture object files as above, compress each "
00139 "one using pzip to a .txo.pz file. In many cases, this will yield a "
00140 "disk file size comparable to that achieved by png compression. This "
00141 "is an on-disk compression only, and does not affect the amount of "
00142 "RAM or texture memory consumed by the texture when it is loaded.",
00143 &EggToBam::dispatch_none, &_tex_txopz);
00144 #endif // HAVE_ZLIB
00145
00146 add_option
00147 ("ctex", "", 0,
00148 #ifdef HAVE_SQUISH
00149 "Pre-compress the texture images using the libsquish library, when "
00150 "using -rawtex or -txo. "
00151 #else
00152 "Asks the graphics card to pre-compress the texture images when using "
00153 "-rawtex or -txo. "
00154 #endif
00155 #ifdef HAVE_ZLIB
00156 "This is unrelated to the on-disk compression achieved "
00157 "via -txopz (and it may be used in conjunction with that parameter). "
00158 #endif
00159 "This will result in a smaller RAM and texture memory footprint for "
00160 "the texture images. The same "
00161 "effect can be achieved at load time by setting compressed-textures in "
00162 "your Config.prc file; but -ctex pre-compresses the "
00163 "textures so that they do not need to be compressed at load time. "
00164 #ifndef HAVE_SQUISH
00165 "Note that, since your Panda is not compiled with the libsquish "
00166 "library, using -ctex will make .txo files that are only guaranteed "
00167 "to load on the particular graphics card that was used to "
00168 "generate them."
00169 #endif
00170 ,
00171 &EggToBam::dispatch_none, &_tex_ctex);
00172
00173 add_option
00174 ("mipmap", "", 0,
00175 "Records the pre-generated mipmap levels in the texture object file "
00176 "when using -rawtex or -txo, regardless of the texture filter mode. This "
00177 "will increase the size of the texture object file by about 33%, but "
00178 "it prevents the need to compute the mipmaps at runtime. The default "
00179 "is to record mipmap levels only when the texture uses a mipmap "
00180 "filter mode.",
00181 &EggToBam::dispatch_none, &_tex_mipmap);
00182
00183 add_option
00184 ("ctexq", "quality", 0,
00185 "Specifies the compression quality to use when performing the "
00186 "texture compression requested by -ctex. This may be one of "
00187 "'default', 'fastest', 'normal', or 'best'. The default is 'best'. "
00188 "Set it to 'default' to use whatever is specified by the Config.prc "
00189 "file. This is a global setting only; individual texture quality "
00190 "settings appearing within the egg file will override this.",
00191 &EggToBam::dispatch_string, NULL, &_ctex_quality);
00192
00193 add_option
00194 ("load-display", "display name", 0,
00195 "Specifies the particular display module to load to perform the texture "
00196 "compression requested by -ctex. If this is omitted, the default is "
00197 "taken from the Config.prc file."
00198 #ifdef HAVE_SQUISH
00199 " Since your Panda has libsquish compiled in, this is not necessary; "
00200 "Panda can compress textures without loading a display module."
00201 #endif
00202 ,
00203 &EggToBam::dispatch_string, NULL, &_load_display);
00204
00205 redescribe_option
00206 ("cs",
00207 "Specify the coordinate system of the resulting " + _format_name +
00208 " file. This may be "
00209 "one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'. The default "
00210 "is z-up.");
00211
00212 _force_complete = true;
00213 _egg_flatten = 0;
00214 _egg_combine_geoms = 0;
00215 _egg_suppress_hidden = 1;
00216 _tex_txopz = false;
00217 _ctex_quality = "best";
00218 }
00219
00220
00221
00222
00223
00224
00225 void EggToBam::
00226 run() {
00227 if (_has_egg_flatten) {
00228
00229
00230 egg_flatten = (_egg_flatten != 0);
00231 }
00232 if (_has_egg_combine_geoms) {
00233
00234 egg_combine_geoms = (_egg_combine_geoms != 0);
00235 }
00236
00237
00238 egg_suppress_hidden = _egg_suppress_hidden;
00239
00240 if (_compression_off) {
00241
00242 compress_channels = false;
00243
00244 } else if (_has_compression_quality) {
00245
00246
00247 compress_channels = true;
00248 compress_chan_quality = _compression_quality;
00249 }
00250
00251 if (_ctex_quality != "default") {
00252
00253
00254 string prc = "texture-quality-level " + _ctex_quality;
00255 load_prc_file_data("prc", prc);
00256 }
00257
00258 if (!_got_coordinate_system) {
00259
00260
00261 _data->set_coordinate_system(CS_zup_right);
00262 }
00263
00264 PT(PandaNode) root = load_egg_data(_data);
00265 if (root == (PandaNode *)NULL) {
00266 nout << "Unable to build scene graph from egg file.\n";
00267 exit(1);
00268 }
00269
00270 if (_tex_ctex) {
00271 #ifndef HAVE_SQUISH
00272 if (!make_buffer()) {
00273 nout << "Unable to initialize graphics context; cannot compress textures.\n";
00274 exit(1);
00275 }
00276 #endif // HAVE_SQUISH
00277 }
00278
00279 if (_tex_txo || _tex_txopz || (_tex_ctex && _tex_rawdata)) {
00280 collect_textures(root);
00281 Textures::iterator ti;
00282 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00283 Texture *tex = (*ti);
00284 tex->get_ram_image();
00285 bool want_mipmaps = (_tex_mipmap || tex->uses_mipmaps());
00286 if (want_mipmaps) {
00287
00288 tex->generate_ram_mipmap_images();
00289 }
00290
00291 if (_tex_ctex) {
00292 #ifdef HAVE_SQUISH
00293 if (!tex->compress_ram_image()) {
00294 nout << " couldn't compress " << tex->get_name() << "\n";
00295 }
00296 tex->set_compression(Texture::CM_on);
00297 #else // HAVE_SQUISH
00298 tex->set_keep_ram_image(true);
00299 bool has_mipmap_levels = (tex->get_num_ram_mipmap_images() > 1);
00300 if (!_engine->extract_texture_data(tex, _gsg)) {
00301 nout << " couldn't compress " << tex->get_name() << "\n";
00302 }
00303 if (!has_mipmap_levels && !want_mipmaps) {
00304
00305
00306 tex->clear_ram_mipmap_images();
00307 }
00308 tex->set_keep_ram_image(false);
00309 #endif // HAVE_SQUISH
00310 }
00311
00312 if (_tex_txo || _tex_txopz) {
00313 convert_txo(tex);
00314 }
00315 }
00316 }
00317
00318 if (_ls) {
00319 root->ls(nout, 0);
00320 }
00321
00322
00323
00324 nassertv(has_output_filename());
00325
00326 Filename filename = get_output_filename();
00327 filename.make_dir();
00328 nout << "Writing " << filename << "\n";
00329 BamFile bam_file;
00330 if (!bam_file.open_write(filename)) {
00331 nout << "Error in writing.\n";
00332 exit(1);
00333 }
00334
00335 if (!bam_file.write_object(root)) {
00336 nout << "Error in writing.\n";
00337 exit(1);
00338 }
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349 bool EggToBam::
00350 handle_args(ProgramBase::Args &args) {
00351
00352
00353
00354 if (_tex_rawdata) {
00355 bam_texture_mode = BamFile::BTM_rawdata;
00356
00357 } else if (_got_path_store) {
00358 bam_texture_mode = BamFile::BTM_unchanged;
00359
00360 } else {
00361
00362
00363 _path_replace->_path_store = PS_absolute;
00364 }
00365
00366 return EggToSomething::handle_args(args);
00367 }
00368
00369
00370
00371
00372
00373
00374
00375 void EggToBam::
00376 collect_textures(PandaNode *node) {
00377 collect_textures(node->get_state());
00378 if (node->is_geom_node()) {
00379 GeomNode *geom_node = DCAST(GeomNode, node);
00380 int num_geoms = geom_node->get_num_geoms();
00381 for (int i = 0; i < num_geoms; ++i) {
00382 collect_textures(geom_node->get_geom_state(i));
00383 }
00384 }
00385
00386 PandaNode::Children children = node->get_children();
00387 int num_children = children.get_num_children();
00388 for (int i = 0; i < num_children; ++i) {
00389 collect_textures(children.get_child(i));
00390 }
00391 }
00392
00393
00394
00395
00396
00397
00398
00399 void EggToBam::
00400 collect_textures(const RenderState *state) {
00401 const TextureAttrib *tex_attrib = DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_type()));
00402 if (tex_attrib != (TextureAttrib *)NULL) {
00403 int num_on_stages = tex_attrib->get_num_on_stages();
00404 for (int i = 0; i < num_on_stages; ++i) {
00405 _textures.insert(tex_attrib->get_on_texture(tex_attrib->get_on_stage(i)));
00406 }
00407 }
00408 }
00409
00410
00411
00412
00413
00414
00415
00416
00417 void EggToBam::
00418 convert_txo(Texture *tex) {
00419 if (!tex->get_loaded_from_txo()) {
00420 Filename fullpath = tex->get_fullpath().get_filename_index(0);
00421 if (_tex_txopz) {
00422 fullpath.set_extension("txo.pz");
00423
00424
00425
00426 fullpath = Filename(fullpath.get_fullpath());
00427 } else {
00428 fullpath.set_extension("txo");
00429 }
00430
00431 if (tex->write(fullpath)) {
00432 nout << " Writing " << fullpath;
00433 if (tex->get_ram_image_compression() != Texture::CM_off) {
00434 nout << " (compressed " << tex->get_ram_image_compression() << ")";
00435 }
00436 nout << "\n";
00437 tex->set_loaded_from_txo();
00438 tex->set_fullpath(fullpath);
00439 tex->clear_alpha_fullpath();
00440
00441 Filename filename = tex->get_filename().get_filename_index(0);
00442 if (_tex_txopz) {
00443 filename.set_extension("txo.pz");
00444 filename = Filename(filename.get_fullpath());
00445 } else {
00446 filename.set_extension("txo");
00447 }
00448
00449 tex->set_filename(filename);
00450 tex->clear_alpha_filename();
00451 }
00452 }
00453 }
00454
00455
00456
00457
00458
00459
00460
00461 bool EggToBam::
00462 make_buffer() {
00463 if (!_load_display.empty()) {
00464
00465 string prc = "load-display " + _load_display;
00466 load_prc_file_data("prc", prc);
00467 }
00468
00469 GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
00470 _pipe = selection->make_default_pipe();
00471 if (_pipe == (GraphicsPipe *)NULL) {
00472 nout << "Unable to create graphics pipe.\n";
00473 return false;
00474 }
00475
00476 _engine = new GraphicsEngine;
00477
00478 FrameBufferProperties fbprops = FrameBufferProperties::get_default();
00479
00480
00481
00482 fbprops.set_back_buffers(0);
00483
00484 WindowProperties winprops;
00485 winprops.set_size(1, 1);
00486 winprops.set_origin(0, 0);
00487 winprops.set_undecorated(true);
00488 winprops.set_open(true);
00489 winprops.set_z_order(WindowProperties::Z_bottom);
00490
00491
00492
00493 _buffer = _engine->make_output(_pipe, "buffer", 0,
00494 fbprops, winprops,
00495 GraphicsPipe::BF_fb_props_optional);
00496 _engine->open_windows();
00497 if (_buffer == (GraphicsOutput *)NULL || !_buffer->is_valid()) {
00498 nout << "Unable to create graphics window.\n";
00499 return false;
00500 }
00501 _gsg = _buffer->get_gsg();
00502
00503 return true;
00504 }
00505
00506
00507 int main(int argc, char *argv[]) {
00508
00509 pystub();
00510
00511 EggToBam prog;
00512 prog.parse_command_line(argc, argv);
00513 prog.run();
00514 return 0;
00515 }