Panda3D
|
00001 // Filename: eggToBam.cxx 00002 // Created by: drose (28Jun00) 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 "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 // Function: EggToBam::Constructor 00039 // Access: Public 00040 // Description: 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 // -f is always in effect for egg2bam. It doesn't make sense to 00054 // provide it as an option to the user. 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 // HAVE_SQUISH 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 // HAVE_ZLIB 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 // HAVE_SQUISH 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 // HAVE_SQUISH 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 // Function: EggToBam::run 00222 // Access: Public 00223 // Description: 00224 //////////////////////////////////////////////////////////////////// 00225 void EggToBam:: 00226 run() { 00227 if (_has_egg_flatten) { 00228 // If the user specified some -flatten, we need to set the 00229 // corresponding Config.prc variable. 00230 egg_flatten = (_egg_flatten != 0); 00231 } 00232 if (_has_egg_combine_geoms) { 00233 // Ditto with -combine_geoms. 00234 egg_combine_geoms = (_egg_combine_geoms != 0); 00235 } 00236 00237 // We always set egg_suppress_hidden. 00238 egg_suppress_hidden = _egg_suppress_hidden; 00239 00240 if (_compression_off) { 00241 // If the user specified -NC, turn off channel compression. 00242 compress_channels = false; 00243 00244 } else if (_has_compression_quality) { 00245 // Otherwise, if the user specified a compression quality with -C, 00246 // use that quality level. 00247 compress_channels = true; 00248 compress_chan_quality = _compression_quality; 00249 } 00250 00251 if (_ctex_quality != "default") { 00252 // Override the user's config file with the command-line parameter 00253 // for texture compression. 00254 string prc = "texture-quality-level " + _ctex_quality; 00255 load_prc_file_data("prc", prc); 00256 } 00257 00258 if (!_got_coordinate_system) { 00259 // If the user didn't specify otherwise, ensure the coordinate 00260 // system is Z-up. 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 // Generate mipmap levels. 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 // Make sure we didn't accidentally introduce mipmap levels 00305 // by rendezvousing through the graphics card. 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 // This should be guaranteed because we pass false to the 00323 // constructor, above. 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 // Function: EggToBam::handle_args 00343 // Access: Protected, Virtual 00344 // Description: Does something with the additional arguments on the 00345 // command line (after all the -options have been 00346 // parsed). Returns true if the arguments are good, 00347 // false otherwise. 00348 //////////////////////////////////////////////////////////////////// 00349 bool EggToBam:: 00350 handle_args(ProgramBase::Args &args) { 00351 // If the user specified a path store option, we need to set the 00352 // bam-texture-mode Config.prc variable directly to support this 00353 // (otherwise the bam code will do what it wants to do anyway). 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 // Otherwise, the default path store is absolute; then the 00362 // bam-texture-mode can do the appropriate thing to it. 00363 _path_replace->_path_store = PS_absolute; 00364 } 00365 00366 return EggToSomething::handle_args(args); 00367 } 00368 00369 //////////////////////////////////////////////////////////////////// 00370 // Function: EggToBam::collect_textures 00371 // Access: Private 00372 // Description: Recursively walks the scene graph, looking for 00373 // Texture references. 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 // Function: EggToBam::collect_textures 00395 // Access: Private 00396 // Description: Recursively walks the scene graph, looking for 00397 // Texture references. 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 // Function: EggToBam::convert_txo 00412 // Access: Private 00413 // Description: If the indicated Texture was not already loaded from 00414 // a txo file, writes it to a txo file and updates the 00415 // Texture object to reference the new file. 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 // We use this clumsy syntax so that the new extension appears to be 00424 // two separate extensions, .txo followed by .pz, which is what 00425 // Texture::write() expects to find. 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 // Function: EggToBam::make_buffer 00457 // Access: Private 00458 // Description: Creates a GraphicsBuffer for communicating with the 00459 // graphics card. 00460 //////////////////////////////////////////////////////////////////// 00461 bool EggToBam:: 00462 make_buffer() { 00463 if (!_load_display.empty()) { 00464 // Override the user's config file with the command-line parameter. 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 // Some graphics drivers can only create single-buffered offscreen 00481 // buffers. So request that. 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 // We don't care how big the buffer is; we just need it to manifest 00492 // the GSG. 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 // A call to pystub() to force libpystub.so to be linked in. 00509 pystub(); 00510 00511 EggToBam prog; 00512 prog.parse_command_line(argc, argv); 00513 prog.run(); 00514 return 0; 00515 }