mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-05 03:15:07 -04:00
516 lines
18 KiB
C++
516 lines
18 KiB
C++
// Filename: eggToBam.cxx
|
|
// Created by: drose (28Jun00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "eggToBam.h"
|
|
|
|
#include "config_util.h"
|
|
#include "bamFile.h"
|
|
#include "load_egg_file.h"
|
|
#include "config_egg2pg.h"
|
|
#include "config_gobj.h"
|
|
#include "config_chan.h"
|
|
#include "pandaNode.h"
|
|
#include "geomNode.h"
|
|
#include "renderState.h"
|
|
#include "textureAttrib.h"
|
|
#include "dcast.h"
|
|
#include "graphicsPipeSelection.h"
|
|
#include "graphicsEngine.h"
|
|
#include "graphicsBuffer.h"
|
|
#include "graphicsStateGuardian.h"
|
|
#include "load_prc_file.h"
|
|
#include "windowProperties.h"
|
|
#include "frameBufferProperties.h"
|
|
#include "pystub.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
EggToBam::
|
|
EggToBam() :
|
|
EggToSomething("Bam", ".bam", true, false)
|
|
{
|
|
set_program_description
|
|
("This program reads Egg files and outputs Bam files, the binary format "
|
|
"suitable for direct loading of animation and models into Panda. Bam "
|
|
"files are tied to a particular version of Panda, so should not be "
|
|
"considered replacements for egg files, but they tend to be smaller and "
|
|
"load much faster than the equivalent egg files.");
|
|
|
|
// -f is always in effect for egg2bam. It doesn't make sense to
|
|
// provide it as an option to the user.
|
|
remove_option("f");
|
|
|
|
add_path_replace_options();
|
|
add_path_store_options();
|
|
|
|
add_option
|
|
("flatten", "flag", 0,
|
|
"Specifies whether to flatten the egg hierarchy after it is loaded. "
|
|
"If flag is zero, the egg hierarchy will not be flattened, but will "
|
|
"instead be written to the bam file exactly as it is. If flag is "
|
|
"non-zero, the hierarchy will be flattened so that unnecessary nodes "
|
|
"(usually group nodes with only one child) are eliminated. The default "
|
|
"if this is not specified is taken from the egg-flatten Config.prc "
|
|
"variable.",
|
|
&EggToBam::dispatch_int, &_has_egg_flatten, &_egg_flatten);
|
|
|
|
add_option
|
|
("combine-geoms", "flag", 0,
|
|
"Specifies whether to combine sibling GeomNodes into a common GeomNode "
|
|
"when possible. This flag is only respected if flatten, above, is also "
|
|
"enabled (or implicitly true from the Config.prc file). The default if "
|
|
"this is not specified is taken from the egg-combine-geoms Config.prc "
|
|
"variable.",
|
|
&EggToBam::dispatch_int, &_has_egg_combine_geoms, &_egg_combine_geoms);
|
|
|
|
add_option
|
|
("suppress-hidden", "flag", 0,
|
|
"Specifies whether to suppress hidden geometry. If this is nonzero, "
|
|
"egg geometry tagged as \"hidden\" will be removed from the final "
|
|
"scene graph; otherwise, it will be preserved (but stashed). The "
|
|
"default is nonzero, to remove it.",
|
|
&EggToBam::dispatch_int, NULL, &_egg_suppress_hidden);
|
|
|
|
add_option
|
|
("ls", "", 0,
|
|
"Writes a scene graph listing to standard output after the egg "
|
|
"file has been loaded, showing the nodes that will be written out.",
|
|
&EggToBam::dispatch_none, &_ls);
|
|
|
|
add_option
|
|
("C", "quality", 0,
|
|
"Specify the quality level for lossy channel compression. If this "
|
|
"is specified, the animation channels will be compressed at this "
|
|
"quality level, which is normally an integer value between 0 and 100, "
|
|
"inclusive, where higher numbers produce larger files with greater "
|
|
"quality. Generally, 95 is the highest useful quality level. Use "
|
|
"-NC (described below) to disable channel compression. If neither "
|
|
"option is specified, the default comes from the Config.prc file.",
|
|
&EggToBam::dispatch_int, &_has_compression_quality, &_compression_quality);
|
|
|
|
add_option
|
|
("NC", "", 0,
|
|
"Turn off lossy compression of animation channels. Channels will be "
|
|
"written exactly as they are, losslessly.",
|
|
&EggToBam::dispatch_none, &_compression_off);
|
|
|
|
add_option
|
|
("rawtex", "", 0,
|
|
"Record texture data directly in the bam file, instead of storing "
|
|
"a reference to the texture elsewhere on disk. The textures are "
|
|
"stored uncompressed, unless -ctex is also specified. "
|
|
"A particular texture that is encoded into "
|
|
"multiple different bam files in this way cannot be unified into "
|
|
"the same part of texture memory if the different bam files are loaded "
|
|
"together. That being said, this can sometimes be a convenient "
|
|
"way to ensure the bam file is completely self-contained.",
|
|
&EggToBam::dispatch_none, &_tex_rawdata);
|
|
|
|
add_option
|
|
("txo", "", 0,
|
|
"Rather than writing texture data directly into the bam file, as in "
|
|
"-rawtex, create a texture object for each referenced texture. A "
|
|
"texture object is a kind of mini-bam file, with a .txo extension, "
|
|
"that contains all of the data needed to recreate a texture, including "
|
|
"its image contents, filter and wrap settings, and so on. 3-D textures "
|
|
"and cube maps can also be represented in a single .txo file. Texture "
|
|
"object files, like bam files, are tied to a particular version of "
|
|
"Panda.",
|
|
&EggToBam::dispatch_none, &_tex_txo);
|
|
|
|
#ifdef HAVE_ZLIB
|
|
add_option
|
|
("txopz", "", 0,
|
|
"In addition to writing texture object files as above, compress each "
|
|
"one using pzip to a .txo.pz file. In many cases, this will yield a "
|
|
"disk file size comparable to that achieved by png compression. This "
|
|
"is an on-disk compression only, and does not affect the amount of "
|
|
"RAM or texture memory consumed by the texture when it is loaded.",
|
|
&EggToBam::dispatch_none, &_tex_txopz);
|
|
#endif // HAVE_ZLIB
|
|
|
|
add_option
|
|
("ctex", "", 0,
|
|
#ifdef HAVE_SQUISH
|
|
"Pre-compress the texture images using the libsquish library, when "
|
|
"using -rawtex or -txo. "
|
|
#else
|
|
"Asks the graphics card to pre-compress the texture images when using "
|
|
"-rawtex or -txo. "
|
|
#endif // HAVE_SQUISH
|
|
#ifdef HAVE_ZLIB
|
|
"This is unrelated to the on-disk compression achieved "
|
|
"via -txopz (and it may be used in conjunction with that parameter). "
|
|
#endif // HAVE_ZLIB
|
|
"This will result in a smaller RAM and texture memory footprint for "
|
|
"the texture images. The same "
|
|
"effect can be achieved at load time by setting compressed-textures in "
|
|
"your Config.prc file; but -ctex pre-compresses the "
|
|
"textures so that they do not need to be compressed at load time. "
|
|
#ifndef HAVE_SQUISH
|
|
"Note that, since your Panda is not compiled with the libsquish "
|
|
"library, using -ctex will make .txo files that are only guaranteed "
|
|
"to load on the particular graphics card that was used to "
|
|
"generate them."
|
|
#endif // HAVE_SQUISH
|
|
,
|
|
&EggToBam::dispatch_none, &_tex_ctex);
|
|
|
|
add_option
|
|
("mipmap", "", 0,
|
|
"Records the pre-generated mipmap levels in the texture object file "
|
|
"when using -rawtex or -txo, regardless of the texture filter mode. This "
|
|
"will increase the size of the texture object file by about 33%, but "
|
|
"it prevents the need to compute the mipmaps at runtime. The default "
|
|
"is to record mipmap levels only when the texture uses a mipmap "
|
|
"filter mode.",
|
|
&EggToBam::dispatch_none, &_tex_mipmap);
|
|
|
|
add_option
|
|
("ctexq", "quality", 0,
|
|
"Specifies the compression quality to use when performing the "
|
|
"texture compression requested by -ctex. This may be one of "
|
|
"'default', 'fastest', 'normal', or 'best'. The default is 'best'. "
|
|
"Set it to 'default' to use whatever is specified by the Config.prc "
|
|
"file. This is a global setting only; individual texture quality "
|
|
"settings appearing within the egg file will override this.",
|
|
&EggToBam::dispatch_string, NULL, &_ctex_quality);
|
|
|
|
add_option
|
|
("load-display", "display name", 0,
|
|
"Specifies the particular display module to load to perform the texture "
|
|
"compression requested by -ctex. If this is omitted, the default is "
|
|
"taken from the Config.prc file."
|
|
#ifdef HAVE_SQUISH
|
|
" Since your Panda has libsquish compiled in, this is not necessary; "
|
|
"Panda can compress textures without loading a display module."
|
|
#endif // HAVE_SQUISH
|
|
,
|
|
&EggToBam::dispatch_string, NULL, &_load_display);
|
|
|
|
redescribe_option
|
|
("cs",
|
|
"Specify the coordinate system of the resulting " + _format_name +
|
|
" file. This may be "
|
|
"one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'. The default "
|
|
"is z-up.");
|
|
|
|
_force_complete = true;
|
|
_egg_flatten = 0;
|
|
_egg_combine_geoms = 0;
|
|
_egg_suppress_hidden = 1;
|
|
_tex_txopz = false;
|
|
_ctex_quality = "best";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::run
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void EggToBam::
|
|
run() {
|
|
if (_has_egg_flatten) {
|
|
// If the user specified some -flatten, we need to set the
|
|
// corresponding Config.prc variable.
|
|
egg_flatten = (_egg_flatten != 0);
|
|
}
|
|
if (_has_egg_combine_geoms) {
|
|
// Ditto with -combine_geoms.
|
|
egg_combine_geoms = (_egg_combine_geoms != 0);
|
|
}
|
|
|
|
// We always set egg_suppress_hidden.
|
|
egg_suppress_hidden = _egg_suppress_hidden;
|
|
|
|
if (_compression_off) {
|
|
// If the user specified -NC, turn off channel compression.
|
|
compress_channels = false;
|
|
|
|
} else if (_has_compression_quality) {
|
|
// Otherwise, if the user specified a compression quality with -C,
|
|
// use that quality level.
|
|
compress_channels = true;
|
|
compress_chan_quality = _compression_quality;
|
|
}
|
|
|
|
if (_ctex_quality != "default") {
|
|
// Override the user's config file with the command-line parameter
|
|
// for texture compression.
|
|
string prc = "texture-quality-level " + _ctex_quality;
|
|
load_prc_file_data("prc", prc);
|
|
}
|
|
|
|
if (!_got_coordinate_system) {
|
|
// If the user didn't specify otherwise, ensure the coordinate
|
|
// system is Z-up.
|
|
_data->set_coordinate_system(CS_zup_right);
|
|
}
|
|
|
|
PT(PandaNode) root = load_egg_data(_data);
|
|
if (root == (PandaNode *)NULL) {
|
|
nout << "Unable to build scene graph from egg file.\n";
|
|
exit(1);
|
|
}
|
|
|
|
if (_tex_ctex) {
|
|
#ifndef HAVE_SQUISH
|
|
if (!make_buffer()) {
|
|
nout << "Unable to initialize graphics context; cannot compress textures.\n";
|
|
exit(1);
|
|
}
|
|
#endif // HAVE_SQUISH
|
|
}
|
|
|
|
if (_tex_txo || _tex_txopz || (_tex_ctex && _tex_rawdata)) {
|
|
collect_textures(root);
|
|
Textures::iterator ti;
|
|
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
|
|
Texture *tex = (*ti);
|
|
tex->get_ram_image();
|
|
bool want_mipmaps = (_tex_mipmap || tex->uses_mipmaps());
|
|
if (want_mipmaps) {
|
|
// Generate mipmap levels.
|
|
tex->generate_ram_mipmap_images();
|
|
}
|
|
|
|
if (_tex_ctex) {
|
|
#ifdef HAVE_SQUISH
|
|
if (!tex->compress_ram_image()) {
|
|
nout << " couldn't compress " << tex->get_name() << "\n";
|
|
}
|
|
tex->set_compression(Texture::CM_on);
|
|
#else // HAVE_SQUISH
|
|
tex->set_keep_ram_image(true);
|
|
bool has_mipmap_levels = (tex->get_num_ram_mipmap_images() > 1);
|
|
if (!_engine->extract_texture_data(tex, _gsg)) {
|
|
nout << " couldn't compress " << tex->get_name() << "\n";
|
|
}
|
|
if (!has_mipmap_levels && !want_mipmaps) {
|
|
// Make sure we didn't accidentally introduce mipmap levels
|
|
// by rendezvousing through the graphics card.
|
|
tex->clear_ram_mipmap_images();
|
|
}
|
|
tex->set_keep_ram_image(false);
|
|
#endif // HAVE_SQUISH
|
|
}
|
|
|
|
if (_tex_txo || _tex_txopz) {
|
|
convert_txo(tex);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_ls) {
|
|
root->ls(nout, 0);
|
|
}
|
|
|
|
// This should be guaranteed because we pass false to the
|
|
// constructor, above.
|
|
nassertv(has_output_filename());
|
|
|
|
Filename filename = get_output_filename();
|
|
filename.make_dir();
|
|
nout << "Writing " << filename << "\n";
|
|
BamFile bam_file;
|
|
if (!bam_file.open_write(filename)) {
|
|
nout << "Error in writing.\n";
|
|
exit(1);
|
|
}
|
|
|
|
if (!bam_file.write_object(root)) {
|
|
nout << "Error in writing.\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::handle_args
|
|
// Access: Protected, Virtual
|
|
// Description: Does something with the additional arguments on the
|
|
// command line (after all the -options have been
|
|
// parsed). Returns true if the arguments are good,
|
|
// false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool EggToBam::
|
|
handle_args(ProgramBase::Args &args) {
|
|
// If the user specified a path store option, we need to set the
|
|
// bam-texture-mode Config.prc variable directly to support this
|
|
// (otherwise the bam code will do what it wants to do anyway).
|
|
if (_tex_rawdata) {
|
|
bam_texture_mode = BamFile::BTM_rawdata;
|
|
|
|
} else if (_got_path_store) {
|
|
bam_texture_mode = BamFile::BTM_unchanged;
|
|
|
|
} else {
|
|
// Otherwise, the default path store is absolute; then the
|
|
// bam-texture-mode can do the appropriate thing to it.
|
|
_path_replace->_path_store = PS_absolute;
|
|
}
|
|
|
|
return EggToSomething::handle_args(args);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::collect_textures
|
|
// Access: Private
|
|
// Description: Recursively walks the scene graph, looking for
|
|
// Texture references.
|
|
////////////////////////////////////////////////////////////////////
|
|
void EggToBam::
|
|
collect_textures(PandaNode *node) {
|
|
collect_textures(node->get_state());
|
|
if (node->is_geom_node()) {
|
|
GeomNode *geom_node = DCAST(GeomNode, node);
|
|
int num_geoms = geom_node->get_num_geoms();
|
|
for (int i = 0; i < num_geoms; ++i) {
|
|
collect_textures(geom_node->get_geom_state(i));
|
|
}
|
|
}
|
|
|
|
PandaNode::Children children = node->get_children();
|
|
int num_children = children.get_num_children();
|
|
for (int i = 0; i < num_children; ++i) {
|
|
collect_textures(children.get_child(i));
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::collect_textures
|
|
// Access: Private
|
|
// Description: Recursively walks the scene graph, looking for
|
|
// Texture references.
|
|
////////////////////////////////////////////////////////////////////
|
|
void EggToBam::
|
|
collect_textures(const RenderState *state) {
|
|
const TextureAttrib *tex_attrib = DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_type()));
|
|
if (tex_attrib != (TextureAttrib *)NULL) {
|
|
int num_on_stages = tex_attrib->get_num_on_stages();
|
|
for (int i = 0; i < num_on_stages; ++i) {
|
|
_textures.insert(tex_attrib->get_on_texture(tex_attrib->get_on_stage(i)));
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::convert_txo
|
|
// Access: Private
|
|
// Description: If the indicated Texture was not already loaded from
|
|
// a txo file, writes it to a txo file and updates the
|
|
// Texture object to reference the new file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void EggToBam::
|
|
convert_txo(Texture *tex) {
|
|
if (!tex->get_loaded_from_txo()) {
|
|
Filename fullpath = tex->get_fullpath().get_filename_index(0);
|
|
if (_tex_txopz) {
|
|
fullpath.set_extension("txo.pz");
|
|
// We use this clumsy syntax so that the new extension appears to be
|
|
// two separate extensions, .txo followed by .pz, which is what
|
|
// Texture::write() expects to find.
|
|
fullpath = Filename(fullpath.get_fullpath());
|
|
} else {
|
|
fullpath.set_extension("txo");
|
|
}
|
|
|
|
if (tex->write(fullpath)) {
|
|
nout << " Writing " << fullpath;
|
|
if (tex->get_ram_image_compression() != Texture::CM_off) {
|
|
nout << " (compressed " << tex->get_ram_image_compression() << ")";
|
|
}
|
|
nout << "\n";
|
|
tex->set_loaded_from_txo();
|
|
tex->set_fullpath(fullpath);
|
|
tex->clear_alpha_fullpath();
|
|
|
|
Filename filename = tex->get_filename().get_filename_index(0);
|
|
if (_tex_txopz) {
|
|
filename.set_extension("txo.pz");
|
|
filename = Filename(filename.get_fullpath());
|
|
} else {
|
|
filename.set_extension("txo");
|
|
}
|
|
|
|
tex->set_filename(filename);
|
|
tex->clear_alpha_filename();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: EggToBam::make_buffer
|
|
// Access: Private
|
|
// Description: Creates a GraphicsBuffer for communicating with the
|
|
// graphics card.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool EggToBam::
|
|
make_buffer() {
|
|
if (!_load_display.empty()) {
|
|
// Override the user's config file with the command-line parameter.
|
|
string prc = "load-display " + _load_display;
|
|
load_prc_file_data("prc", prc);
|
|
}
|
|
|
|
GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
|
|
_pipe = selection->make_default_pipe();
|
|
if (_pipe == (GraphicsPipe *)NULL) {
|
|
nout << "Unable to create graphics pipe.\n";
|
|
return false;
|
|
}
|
|
|
|
_engine = new GraphicsEngine;
|
|
|
|
FrameBufferProperties fbprops = FrameBufferProperties::get_default();
|
|
|
|
// Some graphics drivers can only create single-buffered offscreen
|
|
// buffers. So request that.
|
|
fbprops.set_back_buffers(0);
|
|
|
|
WindowProperties winprops;
|
|
winprops.set_size(1, 1);
|
|
winprops.set_origin(0, 0);
|
|
winprops.set_undecorated(true);
|
|
winprops.set_open(true);
|
|
winprops.set_z_order(WindowProperties::Z_bottom);
|
|
|
|
// We don't care how big the buffer is; we just need it to manifest
|
|
// the GSG.
|
|
_buffer = _engine->make_output(_pipe, "buffer", 0,
|
|
fbprops, winprops,
|
|
GraphicsPipe::BF_fb_props_optional);
|
|
_engine->open_windows();
|
|
if (_buffer == (GraphicsOutput *)NULL || !_buffer->is_valid()) {
|
|
nout << "Unable to create graphics window.\n";
|
|
return false;
|
|
}
|
|
_gsg = _buffer->get_gsg();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
// A call to pystub() to force libpystub.so to be linked in.
|
|
pystub();
|
|
|
|
EggToBam prog;
|
|
prog.parse_command_line(argc, argv);
|
|
prog.run();
|
|
return 0;
|
|
}
|