*** empty log message ***

This commit is contained in:
David Rose 2001-02-24 04:12:27 +00:00
parent 1fa8b1c6c0
commit a2d31d523c
43 changed files with 1009 additions and 6429 deletions

View File

@ -166,10 +166,7 @@ build_joint_hierarchy(EggNode *egg_node, PartGroup *part) {
matd = LMatrix4d::ident_mat();
}
LMatrix4f matf(matd(0,0), matd(0,1), matd(0,2), matd(0,3),
matd(1,0), matd(1,1), matd(1,2), matd(1,3),
matd(2,0), matd(2,1), matd(2,2), matd(2,3),
matd(3,0), matd(3,1), matd(3,2), matd(3,3));
LMatrix4f matf = LCAST(float, matd);
CharacterJoint *joint =
new CharacterJoint(part, egg_group->get_name(), matf);

View File

@ -19,7 +19,9 @@
factoryParams.cxx factoryParams.h \
globPattern.I globPattern.cxx \
globPattern.h globalPointerRegistry.I globalPointerRegistry.cxx \
globalPointerRegistry.h ioPtaDatagramFloat.cxx ioPtaDatagramFloat.h \
globalPointerRegistry.h indirectCompareNames.I \
indirectCompareNames.h indirectCompareTo.I \
indirectCompareTo.h ioPtaDatagramFloat.cxx ioPtaDatagramFloat.h \
ioPtaDatagramInt.cxx ioPtaDatagramInt.h ioPtaDatagramShort.cxx \
ioPtaDatagramShort.h keyboardButton.cxx keyboardButton.h \
lineStream.I lineStream.cxx lineStream.h lineStreamBuf.I \
@ -51,7 +53,8 @@
factoryBase.I factoryBase.h factoryParam.I factoryParam.h \
factoryParams.I factoryParams.h \
globPattern.I globPattern.h \
globalPointerRegistry.I globalPointerRegistry.h indirectCompareTo.I \
globalPointerRegistry.I globalPointerRegistry.h \
indirectCompareNames.I indirectCompareNames.h indirectCompareTo.I \
indirectCompareTo.h ioPtaDatagramFloat.h ioPtaDatagramInt.h \
ioPtaDatagramShort.h iterator_types.h keyboardButton.h lineStream.I \
lineStream.h lineStreamBuf.I lineStreamBuf.h modifierButtons.I \

View File

@ -0,0 +1,16 @@
// Filename: indirectCompareNames.I
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: IndirectCompareNames::operator ()
// Access: Public
// Description: Returns true if a sorts before b, false otherwise.
////////////////////////////////////////////////////////////////////
template<class ObjectType>
INLINE bool IndirectCompareNames<ObjectType>::
operator () (const ObjectType *a, const ObjectType *b) const {
return (a != b && a->get_name() < b->get_name());
}

View File

@ -0,0 +1,29 @@
// Filename: indirectCompareNames.h
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#ifndef INDIRECTCOMPARENAMES_H
#define INDIRECTCOMPARENAMES_H
#include <pandabase.h>
////////////////////////////////////////////////////////////////////
// Class : IndirectCompareNames
// Description : An STL function object class, this is intended to be
// used on any ordered collection of pointers to classes
// that define a get_name() method, particularly for
// things that derive from Namable. It defines the
// order of the pointers by case-sensitive name
// comparison.
////////////////////////////////////////////////////////////////////
template<class ObjectType>
class IndirectCompareNames {
public:
INLINE bool operator () (const ObjectType *a, const ObjectType *b) const;
};
#include "indirectCompareNames.I"
#endif

View File

@ -3,6 +3,7 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: IndirectCompareTo::operator ()
// Access: Public

View File

@ -1,22 +0,0 @@
// #begin bin_target
// #define TARGET egg-palettize-new
// #define LOCAL_LIBS \
// eggbase progbase
// #define OTHER_LIBS \
// egg:c linmath:c putil:c express:c pnmimage:c pnmimagetypes:c \
// pandaegg:m panda:m pandaexpress:m \
// dtoolutil:c dconfig:c dtool:m pystub
// #define SOURCES \
// attribFile.cxx attribFile.h config_egg_palettize.cxx \
// eggPalettize.cxx eggPalettize.h \
// palette.cxx palette.h paletteGroup.cxx \
// paletteGroup.h pTexture.cxx pTexture.h sourceEgg.cxx \
// sourceEgg.h string_utils.cxx string_utils.h \
// textureEggRef.cxx textureEggRef.h textureOmitReason.h \
// texturePacking.cxx \
// texturePacking.h userAttribLine.cxx userAttribLine.h
// #define INSTALL_HEADERS
// #end bin_target

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +0,0 @@
// Filename: attribFile.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef ATTRIBFILE_H
#define ATTRIBFILE_H
#include <pandatoolbase.h>
#include <filename.h>
#include <vector_string.h>
#include <map>
#include <vector>
class UserAttribLine;
class PTexture;
class TexturePacking;
class PaletteGroup;
class Palette;
class SourceEgg;
class EggPalettize;
class PNMImage;
class PNMFileType;
////////////////////////////////////////////////////////////////////
// Class : AttribFile
// Description : The primary driving object behind egg-palettize, this
// class represents both a .txa file and its associated
// .pi file, as well as containing pointers to all the
// textures, palettes, and egg files referenced.
////////////////////////////////////////////////////////////////////
class AttribFile {
public:
AttribFile(const Filename &filename);
string get_name() const;
bool open_and_lock(bool lock);
bool close_and_unlock();
bool rewind_and_truncate();
bool read(bool force_redo_all);
bool write();
Filename write_pi_filename(Filename filename) const;
Filename read_pi_filename(Filename filename) const;
Filename write_egg_filename(Filename filename) const;
void update_params(EggPalettize *prog);
PaletteGroup *get_group(const string &group_name);
void set_default_group(PaletteGroup *default_group);
PaletteGroup *get_default_group();
void get_egg_group_requests();
void get_size_requests();
void update_texture_flags();
void repack_all_textures();
void repack_some_textures();
void optimal_resize();
void finalize_palettes();
void remove_unused_lines();
bool prepare_repack(bool force_optimal);
void touch_dirty_egg_files(bool force_redo_all,
bool eggs_include_images);
PTexture *get_texture(const string &name);
void get_eligible_textures(vector<TexturePacking *> &textures);
SourceEgg *get_egg(Filename name);
bool generate_palette_images();
bool transfer_unplaced_images(bool force_redo_all);
void check_dup_textures(map<string, PTexture *> &textures,
map<string, int> &dup_textures) const;
void collect_statistics(int &num_textures, int &num_placed,
int &num_palettes,
int &orig_size, int &resized_size,
int &palette_size, int &unplaced_size) const;
Filename make_color_filename(const Filename &filename) const;
Filename make_alpha_filename(const Filename &filename) const;
bool write_image_file(PNMImage &image, const Filename &filename,
const Filename &alpha_filename) const;
bool read_image_file(PNMImage &image, const Filename &filename,
const Filename &alpha_filename) const;
private:
typedef vector<UserAttribLine *> UserLines;
UserLines _user_lines;
typedef map<string, SourceEgg *> Eggs;
Eggs _eggs;
typedef map<string, PaletteGroup *> Groups;
Groups _groups;
typedef map<string, PTexture *> PTextures;
PTextures _textures;
typedef vector<TexturePacking *> Packing;
Packing _packing;
bool read_txa(istream &outfile);
bool read_pi(istream &outfile, bool force_redo_all);
bool write_txa(ostream &outfile) const;
bool write_pi(ostream &outfile) const;
bool parse_params(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_packing(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_texture(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_pathname(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_egg(const vector_string &words, istream &infile,
string &line, int &line_num, bool force_redo_all);
bool parse_group(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_palette(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_unplaced(const vector_string &words, istream &infile,
string &line, int &line_num);
bool parse_surprises(const vector_string &words, istream &infile,
string &line, int &line_num);
bool _optimal;
bool _txa_needs_rewrite;
string _name;
Filename _txa_filename;
Filename _pi_filename;
Filename _pi_dir;
PaletteGroup *_default_group;
public:
// These parameter values come from the command line, or from the
// .pi file if omitted from the command line. These are the
// parameter values that specifically refer to textures and
// palettes, and thus should be stored in the .pi file for future
// reference.
string _map_dirname;
Filename _rel_dirname;
int _pal_xsize, _pal_ysize;
int _default_margin;
bool _force_power_2;
bool _aggressively_clean_mapdir;
PNMFileType *_color_type;
PNMFileType *_alpha_type;
int _txa_fd;
fstream _txa_fstrm;
friend class PTexture;
};
#endif

View File

@ -1,14 +0,0 @@
// Filename: config_egg_palettize.cxx
// Created by: drose (02Nov00)
//
////////////////////////////////////////////////////////////////////
#include <dconfig.h>
#include "sourceEgg.h"
Configure(config_egg_palettize);
ConfigureFn(config_egg_palettize) {
SourceEgg::init_type();
}

View File

@ -1,737 +0,0 @@
// Filename: eggPalettize.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "eggPalettize.h"
#include "attribFile.h"
#include "pTexture.h"
#include "string_utils.h"
#include "sourceEgg.h"
#include "textureOmitReason.h"
#include <pnmImage.h>
#include <pnmFileTypeRegistry.h>
#include <pnmFileType.h>
#include <stdio.h>
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggPalettize::
EggPalettize() : EggMultiFilter(true) {
set_program_description
("egg-palettize attempts to pack several texture maps from various models "
"together into one or more palette images, for improved rendering performance "
"and ease of texture management. It can also resize textures on the fly, "
"whether or not they are actually placed on a palette.\n\n"
"egg-palettize reads and writes an AttributesFile, which contains instructions "
"from the user about resizing particular textures, as well as the complete "
"information necessary to reconstruct the palettization from past runs, "
"including references to other egg files that may share this palette. This "
"is designed to allow multiple egg files to use the same palette, without "
"having to process them all at once.\n\n"
"Note that it is not even necessary to specify any egg files at all on the "
"command line; egg-palettize can be run on an existing AttributesFiles by "
"itself to freshen up a palette when necessary.");
clear_runlines();
add_runline("[opts] attribfile.txa file.egg [file.egg ...]");
// We always have EggMultiBase's -f on: force complete load. In
// fact, we use -f for our own purposes, below.
remove_option("f");
_force_complete = true;
add_option
("s", "", 0,
"Do not process anything, but report statistics on all palette "
"information files read.",
&EggPalettize::dispatch_none, &_statistics_only);
// We redefine -d using add_option() instead of redescribe_option()
// so it gets listed along with these other options that relate.
add_option
("d", "dirname", 0,
"The directory in which to write the palettized egg files. This is "
"only necessary if more than one egg file is processed at the same "
"time; if it is included, each egg file will be processed and written "
"into the indicated directory.",
&EggPalettize::dispatch_filename, &_got_output_dirname, &_output_dirname);
add_option
("dm", "dirname", 0,
"The directory in which to place all maps: generated palettes, "
"as well as images which were not placed on palettes "
"(but may have been resized). If this contains the string %s, "
"this will be replaced with the \"dir\" string associated with a "
"palette group.",
&EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
add_option
("dr", "dirname", 0,
"The directory to make map filenames relative to when writing egg "
"files. If specified, this should be an initial substring of -dm.",
&EggPalettize::dispatch_filename, &_got_rel_dirname, &_rel_dirname);
add_option
("g", "group", 0,
"The default palette group that egg files will be assigned to if they "
"are not explicitly assigned to any other group.",
&EggPalettize::dispatch_string, &_got_default_groupname, &_default_groupname);
add_option
("gdir", "name", 0,
"The \"dir\" string to associate with the default palette group "
"specified with -g, if no other dir name is given in the .txa file.",
&EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
add_option
("f", "", 0,
"Force an optimal packing. By default, textures are added to "
"existing palettes without disturbing them, which can lead to "
"suboptimal packing. Including this switch forces the palettes "
"to be rebuilt if necessary to optimize the packing, but this "
"may invalidate other egg files which share this palette.",
&EggPalettize::dispatch_none, &_force_optimal);
add_option
("F", "", 0,
"Force a redo of everything. This is useful in case something "
"has gotten out of sync and the old palettes are just bad.",
&EggPalettize::dispatch_none, &_force_redo_all);
add_option
("R", "", 0,
"Resize mostly-empty palettes to their minimal size.",
&EggPalettize::dispatch_none, &_optimal_resize);
add_option
("t", "", 0,
"Touch any additional egg files that share this palette and will "
"need to be refreshed, but were not included on the command "
"line. Presumably a future make pass will cause them to be run "
"through egg-palettize again.",
&EggPalettize::dispatch_none, &_touch_eggs);
add_option
("T", "", 0,
"When touching egg files, consider an egg file to be invalidated "
"if textures have changed in any way, rather than simply moving "
"around within their palettes. You should use this switch "
"if the texture images themselves are to be stored within bam files "
"generated from the eggs, or some such nonsense.",
&EggPalettize::dispatch_none, &_eggs_include_images);
add_option
("C", "", 0,
"Aggressively keep the map directory clean by deleting unused "
"textures from previous passes.",
&EggPalettize::dispatch_none, &_got_aggressively_clean_mapdir);
add_option
("type", "imagetype[,alphatype]", 0,
"Specify the type of image file to output. All image files, whether "
"palettes or unplaced textures, will be converted to files of this "
"type. If the optional alpha type is specified, then an alpha channel, "
"if present, will be written as a separate file of the indicated "
"type--useful if the primary image type does not support alpha. "
"Use '-type list' to show the available image types.",
&EggPalettize::dispatch_string, &_got_image_type, &_image_type);
add_option
("r", "", 0,
"Respect any repeat/clamp flags given in the egg files. The "
"default is to override a repeat flag if a texture's UV's don't "
"exceed the range [0..1].",
&EggPalettize::dispatch_none, &_dont_override_repeats);
add_option
("z", "fuzz", 0,
"The fuzz factor when applying the above repeat test. UV's are "
"considered to be in the range [0..1] if they are actually in "
"the range [0-fuzz .. 1+fuzz]. The default is 0.01.",
&EggPalettize::dispatch_double, NULL, &_fuzz_factor);
add_option
("m", "margin", 0,
"Specify the default margin size.",
&EggPalettize::dispatch_int, &_got_default_margin, &_default_margin);
add_option
("P", "x,y", 0,
"Specify the default palette size.",
&EggPalettize::dispatch_int_pair, &_got_palette_size, _pal_size);
add_option
("2", "", 0,
"Force textures that have been left out of the palette to a size "
"which is an integer power of 2. They will be scaled down to "
"achieve this.",
&EggPalettize::dispatch_none, &_got_force_power_2);
add_option
("nolock", "", 0,
"Don't attempt to lock the .pi file before rewriting it. Use "
"with extreme caution, as multiple processes running on the same "
".pi file may overwrite each other. Use this only if the lock "
"cannot be achieved for some reason.",
&EggPalettize::dispatch_none, &_dont_lock_pi);
add_option
("k", "", 0,
"Kill lines from the attributes file that aren't used on any "
"texture.",
&EggPalettize::dispatch_none, &_remove_unused_lines);
add_option
("H", "", 0,
"Describe the syntax of the attributes file.",
&EggPalettize::dispatch_none, &_describe_input_file);
_fuzz_factor = 0.01;
_aggressively_clean_mapdir = false;
_force_power_2 = false;
_color_type = (PNMFileType *)NULL;
_alpha_type = (PNMFileType *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::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 EggPalettize::
handle_args(ProgramBase::Args &args) {
if (_describe_input_file) {
describe_input_file();
exit(1);
}
if (_got_image_type) {
PNMFileTypeRegistry *registry = PNMFileTypeRegistry::get_ptr();
if (_image_type == "list") {
nout << "Known image types are:\n";
registry->write_types(nout, 2);
nout << "\n";
exit(1);
}
string color_name = _image_type;
string alpha_name;
size_t comma = _image_type.find(',');
if (comma != string::npos) {
// If we have a comma in the image_type, it's two types: a color
// type and an alpha type.
color_name = _image_type.substr(0, comma);
alpha_name = _image_type.substr(comma + 1);
}
bool okflag = true;
if (!color_name.empty()) {
_color_type = registry->get_type_from_extension(color_name);
if (_color_type == (PNMFileType *)NULL) {
nout << "Image file type '" << color_name << "' is unknown.\n";
okflag = false;
}
}
if (!alpha_name.empty()) {
_alpha_type = registry->get_type_from_extension(alpha_name);
if (_alpha_type == (PNMFileType *)NULL) {
nout << "Image file type '" << alpha_name << "' is unknown.\n";
okflag = false;
}
}
if (!okflag) {
nout << "\nKnown image types are:\n";
registry->write_types(nout, 2);
nout << "\n";
exit(1);
}
/*
if (_color_type != (PNMFileType *)NULL &&
_alpha_type != (PNMFileType *)NULL) {
nout << "Writing color components to " << _color_type->get_name()
<< " and alpha components to " << _alpha_type->get_name()
<< " files.\n";
} else if (_color_type != (PNMFileType *)NULL) {
nout << "Converting images to " << _color_type->get_name()
<< " files.\n";
}
*/
}
Args egg_names;
Args txa_names;
// First, collect all the filenames.
Args::iterator ai;
for (ai = args.begin(); ai != args.end(); ++ai) {
Filename filename = (*ai);
string ext = filename.get_extension();
if (ext == "egg") {
egg_names.push_back(filename);
} else if (ext == "txa" || ext == "pi") {
txa_names.push_back(filename);
} else {
nout << "Don't know what kind of file " << filename << " is.\n";
return false;
}
}
// Now sanity check them. Either we have zero egg files, and one or
// more txa files, or we have some egg files and exactly one txa
// file.
if (egg_names.empty()) {
if (txa_names.empty()) {
nout << "No files specified.\n";
return false;
}
for (ai = txa_names.begin(); ai != txa_names.end(); ++ai) {
AttribFile *af = new AttribFile(*ai);
_attrib_files.push_back(af);
}
} else {
if (txa_names.empty()) {
nout << "Must include an attribs.txa file.\n";
return false;
}
if (txa_names.size() != 1) {
nout << "Can only read one attribs.txa file when processing egg files.\n";
return false;
}
AttribFile *af = new AttribFile(txa_names[0]);
_attrib_files.push_back(af);
}
return EggMultiFilter::handle_args(egg_names);
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::read_egg
// Access: Protected, Virtual
// Description: Allocates and returns a new EggData structure that
// represents the indicated egg file. If the egg file
// cannot be read for some reason, returns NULL.
//
// This can be overridden by derived classes to control
// how the egg files are read, or to extend the
// information stored with each egg structure, by
// deriving from EggData.
////////////////////////////////////////////////////////////////////
EggData *EggPalettize::
read_egg(const Filename &filename) {
// We should only call this function if we have exactly one .txa file.
nassertr(_attrib_files.size() == 1, (EggData *)NULL);
SourceEgg *data = _attrib_files[0]->get_egg(filename);
if (!data->read(filename)) {
// Failure reading.
delete data;
return (EggData *)NULL;
}
if (_force_complete) {
if (!data->resolve_externals()) {
delete data;
return (EggData *)NULL;
}
}
return data;
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::describe_input_file
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EggPalettize::
describe_input_file() {
nout <<
"An attributes file consists mostly of lines describing desired sizes of "
"texture maps. The format resembles, but is not identical to, that of "
"the qtess input file. Examples:\n\n"
" texturename.rgb : 64 64\n"
" texture-a.rgb texture-b.rgb : 32 16 2\n"
" *.rgb : 50%\n"
" eyelids.rgb : 16 16 omit\n\n"
"In general, each line consists of one or more filenames (and can "
"contain shell globbing characters like '*' or '?'), and a colon "
"followed by a size request. For each texture appearing in an egg "
"file, the input list is scanned from the beginning and the first "
"line that matches the filename defines the size of the texture.\n\n"
"A size request may be either a pair of numbers, giving a specific x y "
"size of the texture, or it may be a scale factor in the form of a "
"percentage. It may also include an additional number, giving a margin "
"for this particular texture (otherwise the default margin is "
"applied). Finally, the keyword 'omit' may be included along with the "
"size to specify that the texture should not be placed in a palette.\n\n"
"The attributes file may also assign certain egg files into various "
"named palette groups. The syntax is similar to the above:\n\n"
" car-blue.egg : main\n"
" road.egg house.egg : main\n"
" plane.egg : phase2 main\n"
" car*.egg : phase2\n\n"
"Any number of egg files may be named on one line, and the group of "
"egg files may be simultaneously assigned to one or more groups. Each "
"named group represents a semi-independent collection of textures; a "
"different set of palette images will be created for each group. Each "
"texture that is referenced by a given egg file will be palettized "
"in one of the groups assigned to the egg file. Also see the "
":group command, below, which defines relationships between the "
"different groups.\n\n"
"There are some other special lines that may appear in this second, "
"along with the resize commands. They begin with a colon to "
"distinguish them from the resize commands. They are:\n\n"
" :palette xsize ysize\n\n"
"This specifies the size of the palette file(s) to be created. It "
"overrides the -s command-line option.\n\n"
" :margin msize\n\n"
"This specifies the size of the default margin for all subsequent "
"resize commands. This may appear several times in a given file.\n\n"
" :group groupname1 with groupname2 [groupname3 ...]\n\n"
"This indicates that the palette group named by groupname1 should "
"be allowed to shared textures with those on groupname2 or groupname3, "
"etc. In other words, that whenever palette group groupname1 is in "
"texture memory, we can assume that palette groups groupname2 and "
"groupname3 will also be in memory. Textures that already exist on "
"groupname2 and other dependent groups will not be added to groupname1; "
"instead, egg files will reference the textures directly from the "
"other palettes.\n\n"
"Comments may appear freely throughout the file, and are set off by a "
"hash mark (#).\n";
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::format_space
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
string EggPalettize::
format_space(int size_pixels, bool verbose) {
int size_bytes = size_pixels * 4;
int size_k = (size_bytes + 512) / 1024;
int mm_size_k = (size_bytes * 4 / 3 + 512) / 1024;
char str[128];
if (verbose) {
sprintf(str, "%dk, w/mm %dk", size_k, mm_size_k);
} else {
sprintf(str, "%dk, %dk", size_k, mm_size_k);
}
assert(strlen(str) < 128);
return str;
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::report_statistics
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EggPalettize::
report_statistics() {
/*
// Look for textures in common.
map<string, PTexture *> textures;
map<string, int> dup_textures;
AttribFiles::iterator afi;
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
AttribFile &af = *(*afi);
af.check_dup_textures(textures, dup_textures);
}
if (dup_textures.empty()) {
cout << "\nEach texture appears in only one palette group.\n";
} else {
cout << "\nThe following textures appear in more than one palette group:\n";
int net_wasted_size = 0;
map<string, int>::const_iterator di;
for (di = dup_textures.begin(); di != dup_textures.end(); ++di) {
cout << " " << (*di).first << " ("
<< format_space((*di).second) << ")\n";
net_wasted_size += (*di).second;
}
cout << "Total wasted memory from common textures is "
<< format_space(net_wasted_size, true) << "\n";
}
int net_palette_size = 0;
int net_num_palettes = 0;
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
AttribFile &af = *(*afi);
int num_textures, num_placed, num_palettes;
int orig_size, resized_size, palette_size, unplaced_size;
af.collect_statistics(num_textures, num_placed, num_palettes,
orig_size, resized_size,
palette_size, unplaced_size);
cout << "\nPalette group " << af.get_name() << ":\n"
<< " " << num_textures << " textures, "
<< num_placed << " of which are packed onto "
<< num_palettes << " palettes (" << num_textures - num_placed
<< " unplaced)"
<< "\n Original texture memory: "
<< format_space(orig_size, true)
<< "\n After resizing: "
<< format_space(resized_size, true)
<< "\n After palettizing: "
<< format_space(palette_size + unplaced_size, true)
<< "\n";
net_palette_size += palette_size;
net_num_palettes += num_palettes;
}
int net_num_textures = textures.size();
int net_num_placed = 0;
int net_orig_size = 0;
int net_resized_size = 0;
int net_unplaced_size = 0;
typedef map<TextureOmitReason, pair<int, int> > UnplacedReasons;
UnplacedReasons unplaced_reasons;
map<string, PTexture *>::iterator ti;
for (ti = textures.begin(); ti != textures.end(); ++ti) {
PTexture *texture = (*ti).second;
int xsize, ysize, zsize;
int rxsize, rysize;
int rsize = 0;
if (texture->get_size(xsize, ysize, zsize) &&
texture->get_last_req(rxsize, rysize)) {
net_orig_size += xsize * ysize;
net_resized_size += rxsize * rysize;
rsize = rxsize * rysize;
}
if (texture->is_really_packed()) {
net_num_placed++;
} else {
net_unplaced_size += rsize;
// Here's an unplaced texture; add its size to the unplaced
// reasons table.
UnplacedReasons::iterator uri =
unplaced_reasons.find(texture->get_omit());
if (uri == unplaced_reasons.end()) {
unplaced_reasons.insert
(UnplacedReasons::value_type(texture->get_omit(),
pair<int, int>(1, rsize)));
} else {
(*uri).second.first++;
(*uri).second.second += rsize;
}
}
}
cout << "\nOverall:\n"
<< " " << net_num_textures << " textures, "
<< net_num_placed << " of which are packed onto "
<< net_num_palettes << " palettes ("
<< net_num_textures - net_num_placed << " unplaced)"
<< "\n Original texture memory: "
<< format_space(net_orig_size, true)
<< "\n After resizing: "
<< format_space(net_resized_size, true)
<< "\n After palettizing: "
<< format_space(net_palette_size + net_unplaced_size, true)
<< "\n\n";
UnplacedReasons::iterator uri;
for (uri = unplaced_reasons.begin();
uri != unplaced_reasons.end();
++uri) {
TextureOmitReason reason = (*uri).first;
int count = (*uri).second.first;
int size = (*uri).second.second;
cout << count << " textures (" << format_space(size)
<< ") unplaced because ";
switch (reason) {
case PTexture::OR_none:
cout << "of no reason--textures should have been placed\n";
break;
case PTexture::OR_size:
cout << "size was too large for palette\n";
break;
case PTexture::OR_repeats:
cout << "repeating\n";
break;
case PTexture::OR_omitted:
cout << "explicitly omitted\n";
break;
case PTexture::OR_unused:
cout << "unused by any egg file\n";
break;
case PTexture::OR_unknown:
cout << "texture file is missing\n";
break;
case PTexture::OR_cmdline:
cout << "-x was given on command line\n";
break;
case PTexture::OR_solitary:
cout << "texture was alone on a palette\n";
break;
default:
cout << "unknown reason\n";
}
}
cout << "\n";
*/
}
////////////////////////////////////////////////////////////////////
// Function: EggPalettize::run
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EggPalettize::
run() {
_force_power_2 = _got_force_power_2;
_aggressively_clean_mapdir = _got_aggressively_clean_mapdir;
bool okflag = true;
// We'll repeat the processing steps for each attrib file. If we
// have multiple attrib files, then we have no egg files, so all of
// the egg loops below fall out. On the other hand, if we do have
// egg files, then we have only one attrib file, so this outer loop
// falls out.
AttribFiles::iterator afi;
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
AttribFile &af = *(*afi);
if (_got_default_groupname) {
PaletteGroup *group = af.get_group(_default_groupname);
if (_got_default_groupdir) {
group->set_dirname(_default_groupdir);
}
af.set_default_group(group);
}
if (!af.open_and_lock(!_dont_lock_pi)) {
// Failing to open, or failing to grab the write lock, on the
// attribute file is a fatal error.
exit(1);
}
if (_statistics_only) {
nout << "Reading " << af.get_name() << "\n";
okflag = af.read(false);
} else {
nout << "Processing " << af.get_name() << "\n";
if (!af.read(_force_redo_all)) {
// Failing to read the attribute file is a fatal error.
exit(1);
}
af.update_params(this);
// Get all the texture references out of the egg files.
Eggs::iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = DCAST(SourceEgg, *ei);
egg->get_textures(this);
}
// Assign textures into the appropriate groups.
af.get_egg_group_requests();
// Apply the user's requested size changes onto the textures.
af.get_size_requests();
af.update_texture_flags();
if (af.prepare_repack(_force_optimal) || _force_redo_all) {
if (_force_redo_all || _force_optimal) {
af.repack_all_textures();
} else {
af.repack_some_textures();
}
} else {
nout << "No changes to palette arrangement are required.\n";
}
af.finalize_palettes();
if (_optimal_resize) {
af.optimal_resize();
}
if (_remove_unused_lines) {
af.remove_unused_lines();
}
if (!af.write()) {
// Failing to rewrite the attribute file is a fatal error.
exit(1);
}
// And rebuild whatever images are necessary.
okflag = af.generate_palette_images() && okflag;
okflag = af.transfer_unplaced_images(_force_redo_all) && okflag;
// Now apply the palettization effects to the egg files.
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = DCAST(SourceEgg, *ei);
egg->update_trefs();
}
// Write out the egg files.
write_eggs();
if (_touch_eggs) {
af.touch_dirty_egg_files(_force_redo_all, _eggs_include_images);
}
}
okflag = af.close_and_unlock() && okflag;
}
if (_statistics_only) {
report_statistics();
}
if (!okflag) {
exit(1);
}
}
int
main(int argc, char *argv[]) {
EggPalettize prog;
prog.parse_command_line(argc, argv);
prog.run();
return 0;
}

View File

@ -1,82 +0,0 @@
// Filename: eggPalettize.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef EGGPALETTIZE_H
#define EGGPALETTIZE_H
#include <pandatoolbase.h>
#include "attribFile.h"
#include <eggMultiFilter.h>
#include <vector>
class PTexture;
class PNMFileType;
////////////////////////////////////////////////////////////////////
// Class : EggPalettize
// Description : This is the heart of the program.
////////////////////////////////////////////////////////////////////
class EggPalettize : public EggMultiFilter {
public:
EggPalettize();
virtual bool handle_args(Args &args);
virtual EggData *read_egg(const Filename &filename);
void describe_input_file();
string format_space(int size_pixels, bool verbose = false);
void report_statistics();
void run();
typedef vector<AttribFile *> AttribFiles;
AttribFiles _attrib_files;
// The following parameter values specifically relate to textures
// and palettes. These values are stored in the .pi file for future
// reference.
string _map_dirname;
bool _got_map_dirname;
Filename _rel_dirname;
bool _got_rel_dirname;
string _default_groupname;
bool _got_default_groupname;
string _default_groupdir;
bool _got_default_groupdir;
int _pal_size[2];
bool _got_palette_size;
int _default_margin;
bool _got_default_margin;
bool _force_power_2;
bool _got_force_power_2;
bool _aggressively_clean_mapdir;
bool _got_aggressively_clean_mapdir;
string _image_type;
bool _got_image_type;
PNMFileType *_color_type;
PNMFileType *_alpha_type;
// The following values relate specifically to egg files. They're
// not saved for future sessions.
double _fuzz_factor;
bool _dont_override_repeats;
// The following values control behavior specific to this session.
// They're not saved either.
bool _statistics_only;
bool _force_optimal;
bool _force_redo_all;
bool _optimal_resize;
bool _touch_eggs;
bool _eggs_include_images;
bool _dont_lock_pi;
bool _remove_unused_lines;
bool _describe_input_file;
};
#endif

View File

@ -1,436 +0,0 @@
// Filename: pTexture.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "pTexture.h"
#include "texturePacking.h"
#include "palette.h"
#include "attribFile.h"
#include <pnmImage.h>
#include <pnmReader.h>
////////////////////////////////////////////////////////////////////
// Function: PTexture::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PTexture::
PTexture(AttribFile *attrib_file, const Filename &name) :
_name(name),
_attrib_file(attrib_file)
{
_got_filename = false;
_file_exists = false;
_texture_changed = false;
_matched_anything = false;
_got_size = false;
_got_req = false;
_got_last_req = false;
_margin = _attrib_file->_default_margin;
_omit = false;
_read_header = false;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PTexture::
~PTexture() {
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
delete (*pi).second;
}
}
Filename PTexture::
get_name() const {
return _name;
}
void PTexture::
add_filename(Filename filename) {
filename.make_absolute();
if (!filename.exists()) {
// Store the filename, even though it doesn't exist.
if (!_got_filename) {
_got_filename = true;
_filename = filename;
_file_exists = false;
}
} else {
bool inserted = _filenames.insert(filename).second;
if (!_got_filename || !_file_exists) {
// This was the first filename we encountered; no sweat.
_got_filename = true;
_file_exists = true;
_filename = filename;
check_size();
} else if (inserted) {
// We had been using a different filename previously. Now we've
// got a new one. Maybe this one is better?
// First, read the headers. We'll consider the larger image to
// be the better choice.
if (!_got_size) {
read_header();
}
int oxsize, oysize, ozsize;
bool got_other_size =
read_image_header(filename, oxsize, oysize, ozsize);
if (got_other_size) {
if (!_got_size || oxsize * oysize > _xsize * _ysize) {
_filename = filename;
_xsize = oxsize;
_ysize = oysize;
_zsize = ozsize;
_got_size = true;
_texture_changed = true;
} else if (oxsize * oysize == _xsize * _ysize) {
// If the sizes are the same, we'll consider the newer image
// to be the better choice.
if (filename.compare_timestamps(_filename, false, false) > 0) {
_filename = filename;
_texture_changed = true;
}
}
}
}
}
}
bool PTexture::
get_size(int &xsize, int &ysize, int &zsize) {
if (!_got_size) {
read_header();
}
if (!_got_size) {
return false;
}
xsize = _xsize;
ysize = _ysize;
zsize = _zsize;
return true;
}
void PTexture::
set_size(int xsize, int ysize, int zsize) {
// If we've already read the file header, don't let anyone tell us
// differently.
if (!_read_header) {
_xsize = xsize;
_ysize = ysize;
_zsize = zsize;
_got_size = true;
}
}
bool PTexture::
get_req(int &xsize, int &ysize) {
if (!_got_req) {
int zsize;
return get_size(xsize, ysize, zsize);
}
xsize = _req_xsize;
ysize = _req_ysize;
return true;
}
bool PTexture::
get_last_req(int &xsize, int &ysize) {
if (!_got_last_req) {
int zsize;
return get_size(xsize, ysize, zsize);
}
xsize = _last_req_xsize;
ysize = _last_req_ysize;
return true;
}
void PTexture::
set_last_req(int xsize, int ysize) {
_last_req_xsize = xsize;
_last_req_ysize = ysize;
_got_last_req = true;
}
void PTexture::
reset_req(int xsize, int ysize) {
if (_got_last_req &&
(_last_req_xsize != xsize || _last_req_ysize != ysize)) {
// We've changed the requested size from the last time we ran.
// That constitutes a change to the texture.
_texture_changed = true;
}
_req_xsize = xsize;
_req_ysize = ysize;
_got_req = true;
}
void PTexture::
scale_req(double scale_pct) {
if (!_got_size) {
read_header();
}
if (_got_size) {
reset_req(_xsize * scale_pct / 100.0,
_ysize * scale_pct / 100.0);
}
}
void PTexture::
clear_req() {
_got_req = false;
_margin = _attrib_file->_default_margin;
}
double PTexture::
get_scale_pct() {
if (!_got_size) {
read_header();
}
if (_got_size && _got_req) {
return
100.0 * (((double)_req_xsize / (double)_xsize) +
((double)_req_ysize / (double)_ysize)) / 2.0;
} else {
return 100.0;
}
}
int PTexture::
get_margin() const {
return _margin;
}
void PTexture::
set_margin(int margin) {
_margin = margin;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::user_omit
// Access: Public
// Description: Omits the texture from all palettes, based on a
// user-supplied "omit" parameter (presumably read from
// the .txa file).
////////////////////////////////////////////////////////////////////
void PTexture::
user_omit() {
_omit = true;
// Also omit each of our already-packed textures.
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
(*pi).second->set_omit(OR_omitted);
}
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::add_to_group
// Access: Public
// Description: Adds the texture to the indicated PaletteGroup, if it
// has not already been added. Returns the
// TexturePacking pointer that represents the addition.
////////////////////////////////////////////////////////////////////
TexturePacking *PTexture::
add_to_group(PaletteGroup *group) {
Packing::iterator pi;
pi = _packing.find(group);
if (pi != _packing.end()) {
return (*pi).second;
}
TexturePacking *packing = new TexturePacking(this, group);
_packing[group] = packing;
_attrib_file->_packing.push_back(packing);
return packing;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::check_group
// Access: Public
// Description: If the texture has already been added to the
// indicated PaletteGroup, returns the associated
// TexturePacking object. If it is not yet been added,
// returns NULL.
////////////////////////////////////////////////////////////////////
TexturePacking *PTexture::
check_group(PaletteGroup *group) const {
Packing::const_iterator pi;
pi = _packing.find(group);
if (pi != _packing.end()) {
return (*pi).second;
}
return (TexturePacking *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::set_changed
// Access: Public
// Description: Sets the state of the changed flag. If this is true,
// the texture is known to have changed in some way such
// that files that depend on it will need to be rebuilt.
////////////////////////////////////////////////////////////////////
void PTexture::
set_changed(bool changed) {
_texture_changed = changed;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::matched_anything
// Access: Public
// Description: Returns true if the texture matched at least one line
// in the .txa file, false otherwise.
////////////////////////////////////////////////////////////////////
bool PTexture::
matched_anything() const {
return _matched_anything;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::set_matched_anything
// Access: Public
// Description: Sets the state of the matched_anything flag. See
// matched_anything().
////////////////////////////////////////////////////////////////////
void PTexture::
set_matched_anything(bool matched_anything) {
_matched_anything = matched_anything;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::is_unused
// Access: Public
// Description: Returns true if the texture is not used by any egg
// file in any palette group, false if it is used by at
// least one in at least one group.
////////////////////////////////////////////////////////////////////
bool PTexture::
is_unused() const {
Packing::const_iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
if (!(*pi).second->unused()) {
return false;
}
}
return true;
}
void PTexture::
write_size(ostream &out) {
if (!is_unused()) {
if (!_got_size) {
read_header();
}
out << " " << _name;
if (_got_size) {
out << " orig " << _xsize << " " << _ysize << " " << _zsize;
}
if (_got_req) {
out << " new " << _req_xsize << " " << _req_ysize;
}
out << " " << get_scale_pct() << "%\n";
}
}
void PTexture::
write_pathname(ostream &out) const {
if (_got_filename && !is_unused()) {
if (!_file_exists) {
nout << "Warning: texture " << _filename << " does not exist.\n";
}
out << " " << _name << " " << _attrib_file->write_pi_filename(_filename)
<< "\n";
// Also write out all the alternate filenames.
Filenames::const_iterator fi;
for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
if ((*fi) != _filename) {
// Indent by the same number of spaces to line up the filenames.
for (int i = 0; i < (int)_name.length() + 3; i++) {
out << ' ';
}
out << _attrib_file->write_pi_filename(*fi) << "\n";
}
}
}
}
PNMImage *PTexture::
read_image() {
if (!_got_filename || !_file_exists) {
return NULL;
}
PNMImage *image = new PNMImage;
if (_attrib_file->read_image_file(*image, _filename, Filename())) {
return image;
}
// Hmm, it wasn't able to read the image successfully. Oh well.
delete image;
return NULL;
}
void PTexture::
check_size() {
// Make sure the file has the size it claims to have.
if (_got_size) {
int old_xsize = _xsize;
int old_ysize = _ysize;
read_header();
if (_xsize != old_xsize && _ysize != old_ysize) {
nout << "Source texture " << _name << " has changed size, from "
<< old_xsize << " " << old_ysize << " to "
<< _xsize << " " << _ysize << "\n";
_texture_changed = true;
}
}
}
void PTexture::
read_header() {
// Open the file and read its header to determine its size.
if (_got_filename && _file_exists) {
if (!_read_header) {
_read_header = true;
_got_size = read_image_header(_filename, _xsize, _ysize, _zsize);
}
_read_header = true;
}
}
bool PTexture::
read_image_header(const Filename &filename, int &xsize, int &ysize,
int &zsize) {
PNMImageHeader header;
if (!header.read_header(filename)) {
nout << "Warning: cannot read texture " << filename << "\n";
return false;
}
xsize = header.get_x_size();
ysize = header.get_y_size();
zsize = header.get_num_channels();
return true;
}

View File

@ -1,111 +0,0 @@
// Filename: pTexture.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef PTEXTURE_H
#define PTEXTURE_H
#include <pandatoolbase.h>
#include <filename.h>
#include <set>
#include <map>
class Palette;
class PNMImage;
class AttribFile;
class PaletteGroup;
class TexturePacking;
class TextureEggRef;
////////////////////////////////////////////////////////////////////
// Class : PTexture
// Description : A single texture filename, as read from an egg file
// or from a .txa file. This may be considered for
// palettization on a number of different groups, but it
// must have the same size in each group.
////////////////////////////////////////////////////////////////////
class PTexture {
public:
PTexture(AttribFile *attrib_file, const Filename &name);
~PTexture();
Filename get_name() const;
void add_filename(Filename filename);
bool get_size(int &xsize, int &ysize, int &zsize);
void set_size(int xsize, int ysize, int zsize);
bool get_req(int &xsize, int &ysize);
bool get_last_req(int &xsize, int &ysize);
void set_last_req(int xsize, int ysize);
void reset_req(int xsize, int ysize);
void scale_req(double scale_pct);
void clear_req();
double get_scale_pct();
int get_margin() const;
void set_margin(int margin);
void user_omit();
TexturePacking *add_to_group(PaletteGroup *group);
TexturePacking *check_group(PaletteGroup *group) const;
void set_changed(bool changed);
bool matched_anything() const;
void set_matched_anything(bool matched_anything);
bool is_unused() const;
void write_size(ostream &out);
void write_pathname(ostream &out) const;
PNMImage *read_image();
typedef set<TextureEggRef *> Eggs;
Eggs _eggs;
private:
void check_size();
void read_header();
bool read_image_header(const Filename &filename,
int &xsize, int &ysize, int &zsize);
Filename _name;
typedef set<Filename> Filenames;
Filenames _filenames;
bool _got_filename;
Filename _filename;
bool _file_exists;
bool _texture_changed;
bool _matched_anything;
bool _got_size;
int _xsize, _ysize;
int _zsize;
bool _got_req;
int _req_xsize, _req_ysize;
bool _got_last_req;
int _last_req_xsize, _last_req_ysize;
int _margin;
bool _omit;
bool _read_header;
AttribFile *_attrib_file;
typedef map<PaletteGroup *, TexturePacking *> Packing;
Packing _packing;
friend class TexturePacking;
};
#endif

View File

@ -1,591 +0,0 @@
// Filename: palette.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "palette.h"
#include "paletteGroup.h"
#include "pTexture.h"
#include "texturePacking.h"
#include "attribFile.h"
#include "string_utils.h"
#include <notify.h>
#include <pnmImage.h>
#include <pnmFileType.h>
bool Palette::TexturePlacement::
intersects(int hleft, int htop, int xsize, int ysize) const {
int hright = hleft + xsize;
int hbot = htop + ysize;
int mright = _left + _xsize;
int mbot = _top + _ysize;
return
!(hleft >= mright || hright <= _left ||
htop >= mbot || hbot <= _top);
}
PNMImage *Palette::TexturePlacement::
resize_image(PNMImage *source) const {
// Compute need_*size to account for the (interior) margins.
// However, if the size of the texture if very small, we want to
// make exterior margins, so we won't reduce the size past a certain
// point.
int need_xsize = min(_xsize, max(_xsize - 2 * _margin, 8));
int need_ysize = min(_ysize, max(_ysize - 2 * _margin, 8));
if (source->get_x_size() == need_xsize && source->get_y_size() == need_ysize) {
// The image is already the right size.
return source;
}
PNMImage *new_image =
new PNMImage(need_xsize, need_ysize, source->get_color_type());
new_image->gaussian_filter_from(0.5, *source);
delete source;
return new_image;
}
PNMImage *Palette::TexturePlacement::
add_margins(PNMImage *source) const {
if (_margin == 0) {
// No margins to add.
return source;
}
int orig_xsize = source->get_x_size();
int orig_ysize = source->get_y_size();
int need_xsize = orig_xsize + _margin * 2;
int need_ysize = orig_ysize + _margin * 2;
PNMImage *new_image =
new PNMImage(need_xsize, need_ysize, source->get_color_type());
new_image->copy_sub_image(*source, _margin, _margin);
// Now extend the margin out to the sides.
for (int i = 0; i < _margin; i++) {
for (int x = 0; x < orig_xsize; x++) {
// top edge
new_image->set_xel(x + _margin, i, source->get_xel(x, 0));
// bottom edge
new_image->set_xel(x + _margin, need_ysize - 1 - i,
source->get_xel(x, orig_ysize - 1));
}
for (int y = 0; y < orig_ysize; y++) {
// left edge
new_image->set_xel(i, y + _margin, source->get_xel(0, y));
// right edge
new_image->set_xel(need_xsize - 1 - i, y + _margin,
source->get_xel(orig_xsize - 1, y));
}
for (int j = 0; j < _margin; j++) {
// top-left corner
new_image->set_xel(i, j, source->get_xel(0, 0));
// top-right corner
new_image->set_xel(need_xsize - 1 - i, j,
source->get_xel(orig_xsize - 1, 0));
// bottom-left corner
new_image->set_xel(i, need_ysize - 1 - j,
source->get_xel(0, orig_ysize - 1));
// bottom-right corner
new_image->set_xel(need_xsize - 1 - i, need_ysize - 1 - j,
source->get_xel(orig_xsize - 1, orig_ysize - 1));
}
}
if (source->has_alpha()) {
// Now do the same thing in the alpha channel.
for (int i = 0; i < _margin; i++) {
for (int x = 0; x < orig_xsize; x++) {
// top edge
new_image->set_alpha(x + _margin, i, source->get_alpha(x, 0));
// bottom edge
new_image->set_alpha(x + _margin, need_ysize - 1 - i,
source->get_alpha(x, orig_ysize - 1));
}
for (int y = 0; y < orig_ysize; y++) {
// left edge
new_image->set_alpha(i, y + _margin, source->get_alpha(0, y));
// right edge
new_image->set_alpha(need_xsize - 1 - i, y + _margin,
source->get_alpha(orig_xsize - 1, y));
}
for (int j = 0; j < _margin; j++) {
// top-left corner
new_image->set_alpha(i, j, source->get_alpha(0, 0));
// top-right corner
new_image->set_alpha(need_xsize - 1 - i, j,
source->get_alpha(orig_xsize - 1, 0));
// bottom-left corner
new_image->set_alpha(i, need_ysize - 1 - j,
source->get_alpha(0, orig_ysize - 1));
// bottom-right corner
new_image->set_alpha(need_xsize - 1 - i, need_ysize - 1 - j,
source->get_alpha(orig_xsize - 1, orig_ysize - 1));
}
}
}
delete source;
return new_image;
}
Palette::
Palette(const Filename &filename, PaletteGroup *group,
int xsize, int ysize, int components, AttribFile *attrib_file) :
_filename(filename),
_group(group),
_xsize(xsize),
_ysize(ysize),
_components(components),
_attrib_file(attrib_file)
{
_index = -1;
_palette_changed = false;
_new_palette = false;
_uses_alpha = (_components == 2 || _components == 4);
}
Palette::
Palette(PaletteGroup *group, int index,
int xsize, int ysize, int components, AttribFile *attrib_file) :
_group(group),
_index(index),
_xsize(xsize),
_ysize(ysize),
_components(components),
_attrib_file(attrib_file)
{
_palette_changed = false;
_new_palette = true;
_uses_alpha = (_components == 2 || _components == 4);
}
Palette::
~Palette() {
// Unmark any textures we've had packed.
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
(*ti)._packing->mark_unpacked();
}
}
Filename Palette::
get_filename() const {
return _filename;
}
////////////////////////////////////////////////////////////////////
// Function: Palette::has_alpha_filename
// Access: Public
// Description: Returns true if this texture requires a separate
// alpha image file.
////////////////////////////////////////////////////////////////////
bool Palette::
has_alpha_filename() const {
return _uses_alpha && (_attrib_file->_alpha_type != (PNMFileType *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: Palette::get_alpha_filename
// Access: Public
// Description: Returns the filename for the alpha channel to which
// this texture will be copied, assuming it is not
// placed on a palette.
////////////////////////////////////////////////////////////////////
Filename Palette::
get_alpha_filename() const {
return _attrib_file->make_alpha_filename(_filename);
}
Filename Palette::
get_basename() const {
return _basename;
}
bool Palette::
changed() const {
return _palette_changed;
}
bool Palette::
new_palette() const {
return _new_palette;
}
int Palette::
get_num_textures() const {
return _texplace.size();
}
bool Palette::
check_uses_alpha() const {
// Returns true if any texture in the palette uses alpha.
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
if ((*ti)._packing->uses_alpha()) {
return true;
}
}
return false;
}
void Palette::
get_size(int &xsize, int &ysize) const {
xsize = _xsize;
ysize = _ysize;
}
void Palette::
place_texture_at(TexturePacking *packing, int left, int top,
int xsize, int ysize, int margin) {
nassertv(_group != (PaletteGroup *)NULL);
TexturePlacement tp;
tp._packing = packing;
tp._left = left;
tp._top = top;
tp._xsize = xsize;
tp._ysize = ysize;
tp._margin = margin;
_texplace.push_back(tp);
packing->mark_pack_location(this, left, top, xsize, ysize, margin);
}
bool Palette::
pack_texture(TexturePacking *packing) {
PTexture *texture = packing->get_texture();
int xsize, ysize;
if (!texture->get_req(xsize, ysize)) {
return false;
}
int left, top;
if (find_home(left, top, xsize, ysize)) {
place_texture_at(packing, left, top, xsize, ysize, texture->get_margin());
_palette_changed = true;
return true;
}
return false;
}
bool Palette::
unpack_texture(TexturePacking *packing) {
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
if ((*ti)._packing == packing) {
_texplace.erase(ti);
packing->mark_unpacked();
return true;
}
}
return false;
}
// Attempt to shrink the palette down to the smallest possible (power
// of 2) size that includes all its textures.
void Palette::
optimal_resize() {
if (_texplace.size() < 2) {
// If we don't have at least two textures, there's no point in
// shrinking the palette because we won't be using it anyway.
// Might as well keep it full-sized so we can better pack future
// textures.
return;
}
int max_y = get_max_y();
assert(max_y > 0);
bool resized = false;
while (max_y <= _ysize / 2) {
// If at least half the palette is empty, we can try resizing.
if (max_y <= _ysize / 4) {
// Wow, three-quarters of the palette is empty.
Palette *np = try_resize(_xsize / 2, _ysize / 2);
if (np != NULL) {
// Excellent! We just reduced the palette size by half in
// both dimensions.
_texplace = np->_texplace;
_xsize = _xsize / 2;
_ysize = _ysize / 2;
_palette_changed = true;
delete np;
} else {
// Well, we couldn't reduce in both dimensions, but we can
// always reduce twice in the y dimension.
_ysize = _ysize / 4;
}
} else {
_ysize = _ysize / 2;
}
resized = true;
max_y = get_max_y();
}
assert(get_max_y() <= _ysize);
if (resized) {
nout << "Resizing " << get_basename() << " to " << _xsize << " "
<< _ysize << "\n";
// If we've resized the palette, all the egg files that reference
// these textures will need to be rebuilt.
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
(*ti)._packing->set_changed(true);
}
// And we'll have to mark the palette as a new image.
_new_palette = true;
}
}
void Palette::
finalize_palette() {
if (_filename.empty()) {
// Generate a filename based on the index number.
char index_str[128];
sprintf(index_str, "%03d", _index);
_basename = _group->get_name() + "-palette." + index_str + ".rgb";
_basename = _attrib_file->make_color_filename(_basename);
Filename dirname = _group->get_full_dirname(_attrib_file);
_filename = _basename;
_filename.set_dirname(dirname.get_fullpath());
} else {
_filename = _attrib_file->make_color_filename(_filename);
_basename = _filename.get_basename();
}
_uses_alpha = check_uses_alpha();
_components = _uses_alpha ? 4 : 3;
if (_texplace.size() == 1) {
// If we packed exactly one texture, never mind.
TexturePacking *packing = (*_texplace.begin())._packing;
// This is a little odd: we mark the texture as being omitted, but
// we don't actually unpack it. That way it will still be
// recorded as belonging to this palette (for future
// palettizations), but it will also be copied to the map
// directory, and any egg files that reference it will use the
// texture and not the palette.
packing->set_omit(OR_solitary);
}
}
void Palette::
write(ostream &out) const {
out << "palette " << _attrib_file->write_pi_filename(_filename)
<< " in " << _group->get_name()
<< " size " << _xsize << " " << _ysize << " " << _components
<< "\n";
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
out << " " << (*ti)._packing->get_texture()->get_name()
<< " at " << (*ti)._left << " " << (*ti)._top
<< " size " << (*ti)._xsize << " " << (*ti)._ysize
<< " margin " << (*ti)._margin
<< "\n";
}
}
bool Palette::
generate_image() {
bool okflag = true;
PNMImage palette(_xsize, _ysize, _components);
nout << "Generating " << _filename << "\n";
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
TexturePacking *packing = (*ti)._packing;
PTexture *texture = packing->get_texture();
nout << " " << texture->get_name() << "\n";
okflag = copy_texture_image(palette, *ti) && okflag;
}
_filename.make_dir();
if (!_attrib_file->write_image_file(palette, _filename, get_alpha_filename())) {
nout << "Error in writing.\n";
okflag = false;
}
return okflag;
}
bool Palette::
refresh_image() {
if (!_filename.exists()) {
nout << "Palette image " << _filename << " does not exist, rebuilding.\n";
return generate_image();
}
Filename alpha_filename = get_alpha_filename();
if (has_alpha_filename() && !alpha_filename.exists()) {
nout << "Palette alpha image " << alpha_filename
<< " does not exist, rebuilding.\n";
return generate_image();
}
bool okflag = true;
PNMImage palette;
if (!_attrib_file->read_image_file(palette, _filename, alpha_filename)) {
nout << "Unable to read old palette image " << _filename
<< ", rebuilding.\n";
return generate_image();
}
bool any_changed = false;
if (_components == 4 && !palette.has_alpha()) {
palette.add_alpha();
palette.alpha_fill(1.0);
any_changed = true;
} else if (_components == 3 && palette.has_alpha()) {
palette.remove_alpha();
any_changed = true;
}
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
TexturePacking *packing = (*ti)._packing;
PTexture *texture = packing->get_texture();
if (packing->needs_refresh()) {
if (!any_changed) {
nout << "Refreshing " << _filename << "\n";
any_changed = true;
}
nout << " " << texture->get_name() << "\n";
okflag = copy_texture_image(palette, *ti) && okflag;
}
}
if (any_changed) {
if (!_attrib_file->write_image_file(palette, _filename, alpha_filename)) {
nout << "Error in writing.\n";
okflag = false;
}
}
return okflag;
}
Palette *Palette::
try_resize(int new_xsize, int new_ysize) const {
Palette *np =
new Palette(_group, _index, new_xsize, new_ysize,
_components, _attrib_file);
bool okflag = true;
TexPlace::const_iterator ti;
for (ti = _texplace.begin();
ti != _texplace.end() && okflag;
++ti) {
okflag = np->pack_texture((*ti)._packing);
}
if (okflag) {
return np;
} else {
delete np;
return NULL;
}
}
int Palette::
get_max_y() const {
int max_y = 0;
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
max_y = max(max_y, (*ti)._top + (*ti)._ysize);
}
return max_y;
}
bool Palette::
find_home(int &left, int &top, int xsize, int ysize) const {
top = 0;
while (top + ysize <= _ysize) {
int next_y = _ysize;
// Scan along the row at 'top'.
left = 0;
while (left + xsize <= _xsize) {
int next_x = left;
// Consider the spot at left, top.
// Can we place it here?
bool place_ok = true;
TexPlace::const_iterator ti;
for (ti = _texplace.begin();
ti != _texplace.end() && place_ok;
++ti) {
if ((*ti).intersects(left, top, xsize, ysize)) {
// Nope.
place_ok = false;
next_x = (*ti)._left + (*ti)._xsize;
next_y = min(next_y, (*ti)._top + (*ti)._ysize);
}
}
if (place_ok) {
// Hooray!
return true;
}
assert(next_x > left);
left = next_x;
}
assert(next_y > top);
top = next_y;
}
// Nope, wouldn't fit anywhere.
return false;
}
bool Palette::
copy_texture_image(PNMImage &palette, const TexturePlacement &tp) {
bool okflag = true;
PTexture *texture = tp._packing->get_texture();
PNMImage *image = texture->read_image();
if (image == NULL) {
nout << " *** Unable to read " << texture->get_name() << "\n";
okflag = false;
// Create a solid red texture for images we can't read.
image = new PNMImage(tp._xsize, tp._ysize);
image->fill(1.0, 0.0, 0.0);
} else {
image = tp.add_margins(tp.resize_image(image));
}
if (_components == 4 && !image->has_alpha()) {
// We need to add an alpha channel for the image if the
// palette has an alpha channel.
image->add_alpha();
image->alpha_fill(1.0);
}
palette.copy_sub_image(*image, tp._left, tp._top);
delete image;
return okflag;
}

View File

@ -1,100 +0,0 @@
// Filename: palette.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef PALETTE_H
#define PALETTE_H
#include <pandatoolbase.h>
#include <filename.h>
#include <vector>
class PaletteGroup;
class TexturePacking;
class PNMImage;
class AttribFile;
////////////////////////////////////////////////////////////////////
// Class : Palette
// Description : A single palettized image file within a palette
// group. This represents one page of all the
// palettized textures within this group; there might be
// multiple Palette images within a single group,
// depending on the number and size of the palettized
// textures.
////////////////////////////////////////////////////////////////////
class Palette {
public:
Palette(const Filename &filename, PaletteGroup *group,
int xsize, int ysize, int components, AttribFile *attrib_file);
Palette(PaletteGroup *group, int index,
int xsize, int ysize, int components, AttribFile *attrib_file);
~Palette();
Filename get_filename() const;
bool has_alpha_filename() const;
Filename get_alpha_filename() const;
Filename get_basename() const;
PaletteGroup *get_group() const;
bool changed() const;
bool new_palette() const;
int get_num_textures() const;
bool check_uses_alpha() const;
void get_size(int &xsize, int &ysize) const;
void place_texture_at(TexturePacking *packing, int left, int top,
int xsize, int ysize, int margin);
bool pack_texture(TexturePacking *packing);
bool unpack_texture(TexturePacking *packing);
void optimal_resize();
void finalize_palette();
void write(ostream &out) const;
bool generate_image();
bool refresh_image();
private:
class TexturePlacement {
public:
bool intersects(int left, int top, int xsize, int ysize) const;
PNMImage *resize_image(PNMImage *source) const;
PNMImage *add_margins(PNMImage *source) const;
TexturePacking *_packing;
int _left, _top;
int _xsize, _ysize, _margin;
};
Palette *try_resize(int new_xsize, int new_ysize) const;
int get_max_y() const;
bool find_home(int &left, int &top, int xsize, int ysize) const;
bool copy_texture_image(PNMImage &palette, const TexturePlacement &tp);
typedef vector<TexturePlacement> TexPlace;
TexPlace _texplace;
Filename _filename;
Filename _basename;
PaletteGroup *_group;
int _index;
int _xsize, _ysize, _components;
bool _uses_alpha;
bool _palette_changed;
bool _new_palette;
AttribFile *_attrib_file;
};
#endif

View File

@ -1,316 +0,0 @@
// Filename: paletteGroup.cxx
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#include "paletteGroup.h"
#include "texturePacking.h"
#include "palette.h"
#include "attribFile.h"
#include <stdio.h> // for sprintf()
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PaletteGroup::
PaletteGroup(const string &name) : Namable(name)
{
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PaletteGroup::
~PaletteGroup() {
clear_palettes();
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::get_num_parents
// Access: Public
// Description: Returns the number of dependent PaletteGroup this
// PaletteGroup can share its textures with. See
// get_parent().
////////////////////////////////////////////////////////////////////
int PaletteGroup::
get_num_parents() const {
return _parents.size();
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::get_parent
// Access: Public
// Description: Returns the nth dependent PaletteGroup this
// PaletteGroup can share its textures with. If a
// texture is added to this group that already appears
// one of these groups, the reference to the shared
// texture is used instead of adding a new texture.
////////////////////////////////////////////////////////////////////
PaletteGroup *PaletteGroup::
get_parent(int n) const {
nassertr(n >= 0 && n < (int)_parents.size(), (PaletteGroup *)NULL);
return _parents[n];
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::add_parent
// Access: Public
// Description: Adds a new PaletteGroup to the set of PaletteGroups
// this one can share its textures with. See
// get_parent().
////////////////////////////////////////////////////////////////////
void PaletteGroup::
add_parent(PaletteGroup *parent) {
_parents.push_back(parent);
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::get_dirname
// Access: Public
// Description: Returns the directory name to which palettes and
// textures associated with this group will be written.
////////////////////////////////////////////////////////////////////
const string &PaletteGroup::
get_dirname() const {
return _dirname;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::set_dirname
// Access: Public
// Description: Sets the directory name to which palettes and
// textures associated with this group will be written.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
set_dirname(const string &dirname) {
_dirname = dirname;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::get_full_dirname
// Access: Public
// Description: Computes the actual directory name to which palettes
// and textures should be written, based on
// get_dirname() and on the -dm parameter.
////////////////////////////////////////////////////////////////////
string PaletteGroup::
get_full_dirname(AttribFile *attrib_file) const {
char buffer[4096];
sprintf(buffer, attrib_file->_map_dirname.c_str(), _dirname.c_str());
return buffer;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::pack_texture
// Access: Public
// Description: Adds the texture to some suitable palette image
// within the PaletteGroup. Returns true if
// successfully packed, false otherwise.
////////////////////////////////////////////////////////////////////
bool PaletteGroup::
pack_texture(TexturePacking *packing, AttribFile *attrib_file) {
// Now try to place it in each of our existing palettes.
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
if ((*pi)->pack_texture(packing)) {
return true;
}
}
// It didn't place anywhere; create a new palette for it.
Palette *palette =
new Palette(this, _palettes.size() + 1,
attrib_file->_pal_xsize, attrib_file->_pal_ysize,
0, attrib_file);
if (!palette->pack_texture(packing)) {
// Hmm, it didn't fit on an empty palette. Must be too big.
packing->set_omit(OR_size);
delete palette;
return false;
}
_palettes.push_back(palette);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::generate_palette_images
// Access: Public
// Description: After all of the textures has been placed, generates
// the actual image files for each palette image within
// the group. Returns true if successful, false on
// error.
////////////////////////////////////////////////////////////////////
bool PaletteGroup::
generate_palette_images() {
bool okflag = true;
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
Palette *palette = (*pi);
if (palette->new_palette()) {
// If the palette is a new palette, we'll have to generate a new
// image from scratch.
okflag = palette->generate_image() && okflag;
} else {
// Otherwise, we can probably get by with just updating
// whichever images, if any, have changed.
okflag = palette->refresh_image() && okflag;
}
}
return okflag;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::optimal_resize
// Access: Public
// Description: Resizes each palette image within the group downward,
// if possible, to the smallest power-of-two size that
// holds all of its images.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
optimal_resize() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
(*pi)->optimal_resize();
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::finalize_palettes
// Access: Public
// Description: Performs some finalization of the palette images,
// such as generating filenames.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
finalize_palettes() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
(*pi)->finalize_palette();
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::add_palette
// Access: Public
// Description: Adds the indicated already-created Palette image to
// the group. This is mainly intended to be called from
// AttribFile when reading in a previously-built .pi
// file.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
add_palette(Palette *palette) {
_palettes.push_back(palette);
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::remove_palette_files
// Access: Public
// Description: Removes all the image files generated for the Palette
// images within the group.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
remove_palette_files() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
// Remove the old palette file?
Palette *palette = *pi;
if (!palette->get_filename().empty()) {
if (palette->get_filename().exists()) {
nout << "Deleting " << palette->get_filename() << "\n";
palette->get_filename().unlink();
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::clear_palettes
// Access: Public
// Description: Deletes all of the Palette objects within the group.
// This loses all information about where textures are
// packed.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
clear_palettes() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
delete *pi;
}
_palettes.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::complete_groups
// Access: Public, Static
// Description: Expands the set of PaletteGroups to include all
// parents and parents of parents.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
complete_groups(PaletteGroups &groups) {
// Make a copy so we can safely modify the original set as we
// traverse the copy.
PaletteGroups groups_copy = groups;
PaletteGroups::const_iterator gi;
for (gi = groups_copy.begin(); gi != groups_copy.end(); ++gi) {
(*gi)->add_ancestors(groups);
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::add_ancestors
// Access: Public
// Description: Adds all the ancestors of this PaletteGroup to the
// indicated set.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
add_ancestors(PaletteGroups &groups) {
Parents::const_iterator pri;
for (pri = _parents.begin(); pri != _parents.end(); ++pri) {
PaletteGroup *parent = *pri;
bool inserted = groups.insert(parent).second;
if (inserted) {
parent->add_ancestors(groups);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::write_pi
// Access: Public
// Description: Writes out a .pi file description of the group.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
write_pi(ostream &out) const {
if (_dirname.empty() && _palettes.empty()) {
// No real reason to write this group out.
return;
}
out << "group " << get_name();
if (!_dirname.empty()) {
out << " dir " << _dirname;
}
out << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::write_palettes
// Access: Public
// Description: Writes out a .pi file description of all of the
// individual Palette images.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
write_palettes_pi(ostream &out) const {
Palettes::const_iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
out << "\n";
(*pi)->write(out);
}
}

View File

@ -1,87 +0,0 @@
// Filename: paletteGroup.h
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#ifndef PALETTEGROUP_H
#define PALETTEGROUP_H
#include <pandatoolbase.h>
#include <namable.h>
#include <vector>
#include <set>
class Palette;
class TexturePacking;
class AttribFile;
class PaletteGroup;
typedef set<PaletteGroup *> PaletteGroups;
////////////////////////////////////////////////////////////////////
// Class : PaletteGroup
// Description : A named collection of textures. This is a group of
// textures that are to be palettized together as a
// unit; all of these textures are expected to be loaded
// into texture memory at one time. The PaletteGroup
// consists of a number of discrete Palette images, as
// many as are necessary to represent all of the
// textures within the PaletteGroup that are to be
// palettized.
//
// A PaletteGroup is also associated with one or more
// other PaletteGroups, which are assumed to be
// available in texture memory whenever this
// PaletteGroup is. If a texture is to be placed on
// this PaletteGroup that already exists on one of the
// dependent PaletteGroups, it is not placed; instead,
// it is referenced directly from the other
// PaletteGroup. This allows an intelligent sharing of
// textures between palettes with a minimum of wasted
// space.
////////////////////////////////////////////////////////////////////
class PaletteGroup : public Namable {
public:
PaletteGroup(const string &name);
~PaletteGroup();
int get_num_parents() const;
PaletteGroup *get_parent(int n) const;
void add_parent(PaletteGroup *parent);
const string &get_dirname() const;
void set_dirname(const string &dirname);
string get_full_dirname(AttribFile *attrib_file) const;
bool pack_texture(TexturePacking *packing, AttribFile *attrib_file);
bool generate_palette_images();
void optimal_resize();
void finalize_palettes();
void add_palette(Palette *palette);
void remove_palette_files();
void clear_palettes();
static void complete_groups(PaletteGroups &groups);
void add_ancestors(PaletteGroups &groups);
void write_pi(ostream &out) const;
void write_palettes_pi(ostream &out) const;
private:
typedef vector<PaletteGroup *> Parents;
Parents _parents;
typedef vector<Palette *> Palettes;
Palettes _palettes;
string _dirname;
};
#endif

View File

@ -1,496 +0,0 @@
// Filename: sourceEgg.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "sourceEgg.h"
#include "pTexture.h"
#include "textureEggRef.h"
#include "texturePacking.h"
#include "paletteGroup.h"
#include "eggPalettize.h"
#include "string_utils.h"
#include "palette.h"
#include <eggAttributes.h>
#include <eggVertex.h>
#include <eggNurbsSurface.h>
#include <eggPrimitive.h>
#include <eggTextureCollection.h>
#include <eggAlphaMode.h>
#include <algorithm>
TypeHandle SourceEgg::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
SourceEgg::
SourceEgg(AttribFile *attrib_file) : _attrib_file(attrib_file) {
_matched_anything = false;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::add_texture
// Access: Public
// Description: Adds a new texture to the set of textures known by
// the egg file.
////////////////////////////////////////////////////////////////////
TextureEggRef *SourceEgg::
add_texture(PTexture *texture, TexturePacking *packing,
bool repeats, bool alpha) {
TextureEggRef *ref =
new TextureEggRef(this, texture, packing, repeats, alpha);
_texrefs.insert(ref);
texture->_eggs.insert(ref);
return ref;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::get_textures
// Access: Public
// Description: Examines the egg file for all of the texture
// references and assigns each texture to a suitable
// palette group.
////////////////////////////////////////////////////////////////////
void SourceEgg::
get_textures(EggPalettize *prog) {
TexRefs::iterator tri;
for (tri = _texrefs.begin(); tri != _texrefs.end(); ++tri) {
TextureEggRef *texref = (*tri);
texref->_texture->_eggs.erase(texref);
}
_texrefs.clear();
EggTextureCollection tc;
tc.find_used_textures(this);
EggTextureCollection::iterator ti;
for (ti = tc.begin(); ti != tc.end(); ++ti) {
EggTexture *eggtex = (*ti);
string name = eggtex->get_filename().get_basename();
PTexture *texture = _attrib_file->get_texture(name);
texture->add_filename(eggtex->get_filename());
bool repeats =
eggtex->get_wrap_mode() == EggTexture::WM_repeat ||
eggtex->get_wrap_u() == EggTexture::WM_repeat ||
eggtex->get_wrap_v() == EggTexture::WM_repeat;
bool repeat_unspecified =
eggtex->get_wrap_mode() == EggTexture::WM_unspecified &&
eggtex->get_wrap_u() == EggTexture::WM_unspecified &&
eggtex->get_wrap_v() == EggTexture::WM_unspecified;
bool alpha = false;
EggAlphaMode::AlphaMode alpha_mode = eggtex->get_alpha_mode();
if (alpha_mode == EggAlphaMode::AM_unspecified) {
int xsize, ysize, zsize;
if (texture->get_size(xsize, ysize, zsize)) {
alpha = eggtex->has_alpha_channel(zsize);
}
} else if (alpha_mode == EggAlphaMode::AM_off) {
alpha = false;
} else {
alpha = true;
}
// Check the range of UV's actually used within the egg file.
bool any_uvs = false;
TexCoordd min_uv, max_uv;
get_uv_range(this, eggtex, any_uvs, min_uv, max_uv);
// Now we need to apply the texture matrix, if there is one, to
// our bounding box UV's.
if (eggtex->has_transform()) {
// Transforming a bounding box by a matrix requires transforming
// all four corners.
TexCoordd a(min_uv[0], min_uv[1]);
TexCoordd b(min_uv[0], max_uv[1]);
TexCoordd c(max_uv[0], max_uv[1]);
TexCoordd d(max_uv[0], min_uv[1]);
LMatrix3d transform = eggtex->get_transform();
a = a * transform;
b = b * transform;
c = c * transform;
d = d * transform;
// Now boil down these four corners into a new bounding box.
min_uv.set(min(min(a[0], b[0]), min(c[0], d[0])),
min(min(a[1], b[1]), min(c[1], d[1])));
max_uv.set(max(max(a[0], b[0]), max(c[0], d[0])),
max(max(a[1], b[1]), max(c[1], d[1])));
}
bool truly_repeats =
(max_uv[0] > 1.0 + prog->_fuzz_factor ||
min_uv[0] < 0.0 - prog->_fuzz_factor ||
max_uv[1] > 1.0 + prog->_fuzz_factor ||
min_uv[1] < 0.0 - prog->_fuzz_factor);
if (repeat_unspecified) {
// If the egg file didn't give us any advice regarding
// repeating, we can go entirely based on what we saw in the
// UV's.
repeats = truly_repeats;
} else {
if (repeats && !truly_repeats) {
// The egg file specified that the texture repeats, but we
// discovered that it doesn't really. Quietly mark it so.
if (!prog->_dont_override_repeats) {
repeats = false;
}
} else if (!repeats && truly_repeats) {
// The egg file specified that the texture doesn't repeat, but
// its UV's were outside the normal range! That's almost
// certainly a modeling error.
// We won't trouble the user with this kind of spammy message,
// though.
/*
nout << "Warning: texture " << texture->get_name() << " (tref "
<< eggtex->name << ") marked clamped, but UV's range from ("
<< min_uv << ") to (" << max_uv << ")\n";
*/
} else if (repeats && truly_repeats) {
// Well, it really does repeat. Or does it?
if (fabs(max_uv[0] - min_uv[0]) <= 1.0 + prog->_fuzz_factor &&
fabs(max_uv[1] - min_uv[1]) <= 1.0 + prog->_fuzz_factor) {
// It really shouldn't! The UV's fit totally within a unit
// square, just not the square (0,0) - (1,1). This is a
// modeling problem; inform the user.
nout << "Warning: texture " << texture->get_name()
<< " cannot be clamped because UV's range from ("
<< min_uv << ") to (" << max_uv << ")\n";
}
}
}
TextureEggRef *texref = add_texture(texture, (TexturePacking *)NULL,
repeats, alpha);
texref->_eggtex = eggtex;
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::add_group
// Access: Public
// Description: Adds the indicated group to the set of groups known
// to be required for the egg file. Typically this is
// done from the .pi file.
////////////////////////////////////////////////////////////////////
void SourceEgg::
add_group(PaletteGroup *group) {
_groups.insert(group);
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::require_groups
// Access: Public
// Description: Ensures that each texture in the egg file is packed
// into at least one of the indicated groups.
////////////////////////////////////////////////////////////////////
void SourceEgg::
require_groups(PaletteGroup *preferred, const PaletteGroups &groups) {
_groups = groups;
TexRefs::iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
(*ti)->require_groups(preferred, groups);
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::all_textures_assigned
// Access: Public
// Description: Ensures that each texture in the egg file is packed
// into at least one group, any group.
////////////////////////////////////////////////////////////////////
void SourceEgg::
all_textures_assigned() {
PaletteGroup *default_group;
if (_groups.empty()) {
default_group = _attrib_file->get_default_group();
_groups.insert(default_group);
} else {
default_group = *(_groups.begin());
}
TexRefs::iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
(*ti)->require_groups(default_group, _groups);
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::mark_texture_flags
// Access: Public
// Description: Updates each PTexture with the flags stored in the
// various egg files. Also marks textures as used.
////////////////////////////////////////////////////////////////////
void SourceEgg::
mark_texture_flags() {
TexRefs::iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
TexturePacking *packing = (*ti)->_packing;
packing->set_unused(false);
if ((*ti)->_alpha) {
packing->set_uses_alpha(true);
}
if ((*ti)->_repeats) {
packing->set_omit(OR_repeats);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::update_trefs
// Access: Public
// Description: Updates the egg file to point to the new palettes.
////////////////////////////////////////////////////////////////////
void SourceEgg::
update_trefs() {
TexRefs::iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
TexturePacking *packing = (*ti)->_packing;
PTexture *texture = packing->get_texture();
EggTexture *eggtex = (*ti)->_eggtex;
if (eggtex != NULL) {
// Make the alpha mode explicit if it isn't already.
if (eggtex->get_alpha_mode() == EggTexture::AM_unspecified) {
int xsize, ysize, zsize;
if (texture->get_size(xsize, ysize, zsize)) {
if (eggtex->has_alpha_channel(zsize)) {
eggtex->set_alpha_mode(EggAlphaMode::AM_on);
} else {
eggtex->set_alpha_mode(EggAlphaMode::AM_off);
}
}
}
if (!packing->is_packed() ||
packing->get_omit() != OR_none) {
// This texture wasn't palettized, so just rename the
// reference to the new one.
eggtex->set_filename(_attrib_file->write_egg_filename(packing->get_new_filename()));
if (packing->has_alpha_filename()) {
eggtex->set_alpha_file(_attrib_file->write_egg_filename(packing->get_alpha_filename()));
} else {
eggtex->clear_alpha_file();
}
} else {
// This texture was palettized, so redirect the tref to point
// within the palette.
Palette *palette = packing->get_palette();
eggtex->set_filename(_attrib_file->write_egg_filename(palette->get_filename()));
if (palette->has_alpha_filename()) {
eggtex->set_alpha_file(_attrib_file->write_egg_filename(palette->get_alpha_filename()));
} else {
eggtex->clear_alpha_file();
}
// Set the texture attributes to be uniform across all palettes.
eggtex->set_minfilter(EggTexture::FT_linear_mipmap_linear);
eggtex->set_magfilter(EggTexture::FT_linear);
eggtex->set_format(EggTexture::F_rgba8);
eggtex->set_wrap_mode(EggTexture::WM_clamp);
eggtex->set_wrap_u(EggTexture::WM_clamp);
eggtex->set_wrap_v(EggTexture::WM_clamp);
// Build a matrix that will scale the UV's to their new place
// on the palette.
int left, top, xsize, ysize, margin;
packing->get_packed_location(left, top);
packing->get_packed_size(xsize, ysize, margin);
// Shrink the box to be within the margins.
top += margin;
left += margin;
xsize -= margin*2;
ysize -= margin*2;
// Now determine the relative size and position within the
// palette.
int bottom = top + ysize;
int palx, paly;
palette->get_size(palx, paly);
LVecBase2d t((double)left / (double)palx,
(double)(paly - bottom) / (double)paly);
LVecBase2d s((double)xsize / (double)palx,
(double)ysize / (double)paly);
LMatrix3d texmat
(s[0], 0.0, 0.0,
0.0, s[1], 0.0,
t[0], t[1], 1.0);
// Do we already have a texture matrix? If so, compose them.
if (eggtex->has_transform()) {
eggtex->set_transform(eggtex->get_transform() * texmat);
} else {
// Otherwise, just store it.
eggtex->set_transform(texmat);
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::needs_rebuild
// Access: Public
// Description: Returns true if any of the textures referenced by the
// egg file have been adjusted this pass, implying that
// the egg file will have to be re-run through
// egg-palettize, and/or re-pfb'ed.
////////////////////////////////////////////////////////////////////
bool SourceEgg::
needs_rebuild(bool force_redo_all,
bool eggs_include_images) const {
// if (!_wrote_egg) {
TexRefs::const_iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
bool dirty =
eggs_include_images ?
(*ti)->_packing->needs_refresh() :
(*ti)->_packing->packing_changed();
if (force_redo_all || dirty) {
return true;
}
}
// }
return false;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::matched_anything
// Access: Public
// Description: Returns true if the egg file matched at least one
// line in the .txa file, false otherwise.
////////////////////////////////////////////////////////////////////
bool SourceEgg::
matched_anything() const {
return _matched_anything;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::set_matched_anything
// Access: Public
// Description: Sets the state of the matched_anything flag. See
// matched_anything().
////////////////////////////////////////////////////////////////////
void SourceEgg::
set_matched_anything(bool matched_anything) {
_matched_anything = matched_anything;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::write_pi
// Access: Public
// Description: Writes the entry for this egg file to the .pi file.
////////////////////////////////////////////////////////////////////
void SourceEgg::
write_pi(ostream &out) const {
Filename filename = _attrib_file->write_pi_filename(get_egg_filename());
out << "egg " << filename << " in";
PaletteGroups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
out << " " << (*gi)->get_name();
}
out << "\n";
TexRefs::const_iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
out << " " << (*ti)->_packing->get_texture()->get_name()
<< " in " << (*ti)->_packing->get_group()->get_name();
if ((*ti)->_repeats) {
out << " repeats";
}
if ((*ti)->_alpha) {
out << " alpha";
}
out << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::get_uv_range
// Access: Private
// Description: Checks the geometry in the egg file to see what range
// of UV's are requested for this particular texture
// reference.
////////////////////////////////////////////////////////////////////
void SourceEgg::
get_uv_range(EggGroupNode *group, EggTexture *tref,
bool &any_uvs, TexCoordd &min_uv, TexCoordd &max_uv) {
EggGroupNode::iterator ci;
for (ci = group->begin(); ci != group->end(); ci++) {
EggNode *child = (*ci);
if (child->is_of_type(EggNurbsSurface::get_class_type())) {
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, child);
if (nurbs->has_texture() && nurbs->get_texture() == tref) {
// Here's a NURBS surface that references the texture. Unlike
// other kinds of geometries, NURBS don't store UV's; they're
// implicit in the surface. NURBS UV's will always run in the
// range (0,0) - (1,1).
if (any_uvs) {
min_uv.set(min(min_uv[0], 0.0), min(min_uv[1], 0.0));
max_uv.set(max(max_uv[0], 1.0), max(max_uv[1], 1.0));
} else {
min_uv.set(0.0, 0.0);
max_uv.set(1.0, 1.0);
any_uvs = true;
}
}
} else if (child->is_of_type(EggPrimitive::get_class_type())) {
EggPrimitive *geom = DCAST(EggPrimitive, child);
if (geom->has_texture() && geom->get_texture() == tref) {
// Here's a piece of geometry that references this texture.
// Walk through its vertices at get its UV's.
EggPrimitive::iterator pi;
for (pi = geom->begin(); pi != geom->end(); ++pi) {
EggVertex *vtx = (*pi);
if (vtx->has_uv()) {
const TexCoordd &uv = vtx->get_uv();
if (any_uvs) {
min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
} else {
// The first UV.
min_uv = uv;
max_uv = uv;
any_uvs = true;
}
}
}
}
} else if (child->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *cg = DCAST(EggGroupNode, child);
get_uv_range(cg, tref, any_uvs, min_uv, max_uv);
}
}
}

View File

@ -1,79 +0,0 @@
// Filename: sourceEgg.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef SOURCEGG_H
#define SOURCEGG_H
#include <pandatoolbase.h>
#include "paletteGroup.h"
#include <eggData.h>
#include <luse.h>
class PTexture;
class TexturePacking;
class AttribFile;
class EggPalettize;
class EggTexture;
class EggGroup;
class TextureEggRef;
class SourceEgg : public EggData {
public:
SourceEgg(AttribFile *attrib_file);
TextureEggRef *add_texture(PTexture *texture, TexturePacking *packing,
bool repeats, bool alpha);
void get_textures(EggPalettize *prog);
void add_group(PaletteGroup *group);
void require_groups(PaletteGroup *preferred, const PaletteGroups &groups);
void all_textures_assigned();
void mark_texture_flags();
void update_trefs();
bool needs_rebuild(bool force_redo_all,
bool eggs_include_images) const;
bool matched_anything() const;
void set_matched_anything(bool matched_anything);
void write_pi(ostream &out) const;
typedef set<TextureEggRef *> TexRefs;
TexRefs _texrefs;
private:
void get_uv_range(EggGroupNode *group, EggTexture *tref,
bool &any_uvs, TexCoordd &min_uv, TexCoordd &max_uv);
AttribFile *_attrib_file;
bool _matched_anything;
PaletteGroups _groups;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
EggData::init_type();
register_type(_type_handle, "SourceEgg",
EggData::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#endif

View File

@ -1,80 +0,0 @@
// Filename: string_utils.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "string_utils.h"
string
trim_left(const string &str) {
size_t begin = 0;
while (begin < str.size() && isspace(str[begin])) {
begin++;
}
return str.substr(begin);
}
string
trim_right(const string &str) {
size_t begin = 0;
size_t end = str.size();
while (end > begin && isspace(str[end - 1])) {
end--;
}
return str.substr(begin, end - begin);
}
vector_string
extract_words(const string &str) {
vector_string result;
size_t pos = 0;
while (pos < str.length() && isspace(str[pos])) {
pos++;
}
while (pos < str.length()) {
size_t word_start = pos;
while (pos < str.length() && !isspace(str[pos])) {
pos++;
}
result.push_back(str.substr(word_start, pos - word_start));
while (pos < str.length() && isspace(str[pos])) {
pos++;
}
}
return result;
}
// Extracts the first word of the string into param, and the remainder
// of the line into value.
void
extract_param_value(const string &str, string &param, string &value) {
size_t i = 0;
// First, skip all whitespace at the beginning.
while (i < str.length() && isspace(str[i])) {
i++;
}
size_t start = i;
// Now skip to the end of the whitespace.
while (i < str.length() && !isspace(str[i])) {
i++;
}
size_t end = i;
param = str.substr(start, end - start);
// Skip a little bit further to the start of the value.
while (i < str.length() && isspace(str[i])) {
i++;
}
value = trim_right(str.substr(i));
}

View File

@ -1,21 +0,0 @@
// Filename: string_utils.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <pandatoolbase.h>
#include <vector_string.h>
#include <vector>
string trim_left(const string &str);
string trim_right(const string &str);
vector_string extract_words(const string &str);
void extract_param_value(const string &str, string &param, string &value);
#endif

View File

@ -1,69 +0,0 @@
// Filename: textureEggRef.cxx
// Created by: drose (08Nov00)
//
////////////////////////////////////////////////////////////////////
#include "textureEggRef.h"
#include "texturePacking.h"
#include "pTexture.h"
#include <notify.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////
// Function: TextureEggRef::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TextureEggRef::
TextureEggRef(SourceEgg *egg, PTexture *texture, TexturePacking *packing,
bool repeats, bool alpha) :
_egg(egg),
_texture(texture),
_packing(packing),
_repeats(repeats),
_alpha(alpha)
{
_eggtex = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: TextureEggRef::require_groups
// Access: Public
// Description: Indicates the set of PaletteGroups that this texture
// (as appearing on this egg file) wants to be listed
// on. If the texture is already listed on one of these
// groups, does nothing; otherwise, moves the texture.
////////////////////////////////////////////////////////////////////
void TextureEggRef::
require_groups(PaletteGroup *preferred, const PaletteGroups &groups) {
nassertv(!groups.empty());
if (_packing != (TexturePacking *)NULL) {
PaletteGroup *now_on = _packing->get_group();
if (groups.count(now_on) != 0) {
// The group we're on is on the list; no problem.
return;
}
}
// Has the texture already been packed into any of the groups?
PaletteGroups::const_iterator gi;
for (gi = groups.begin(); gi != groups.end(); ++gi) {
TexturePacking *packing = _texture->check_group(*gi);
if (packing != (TexturePacking *)NULL) {
// It has, use that group.
_packing = packing;
return;
}
}
// We need to pack the texture into one of the requested groups.
// For now, we arbitrarily pick the preferred group. Later, maybe
// we'll try to be smart about this and do some kind of graph
// minimization to choose the group that leads to the least
// redundant packing.
_packing = _texture->add_to_group(preferred);
}

View File

@ -1,43 +0,0 @@
// Filename: textureEggRef.h
// Created by: drose (08Nov00)
//
////////////////////////////////////////////////////////////////////
#ifndef TEXTUREEGGREF_H
#define TEXTUREEGGREF_H
#include <pandatoolbase.h>
#include "paletteGroup.h"
#include <vector>
class SourceEgg;
class PTexture;
class TexturePacking;
class EggTexture;
////////////////////////////////////////////////////////////////////
// Class : TextureEggRef
// Description : This associates a texture with the egg files it is
// placed on, and also associates an egg file with the
// various textures it contains.
////////////////////////////////////////////////////////////////////
class TextureEggRef {
public:
TextureEggRef(SourceEgg *egg, PTexture *texture,
TexturePacking *packing,
bool repeats, bool alpha);
void require_groups(PaletteGroup *preferred, const PaletteGroups &groups);
SourceEgg *_egg;
PTexture *_texture;
TexturePacking *_packing;
bool _repeats;
bool _alpha;
EggTexture *_eggtex;
};
#endif

View File

@ -1,26 +0,0 @@
// Filename: textureOmitReason.h
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#ifndef TEXTUREOMITREASON_H
#define TEXTUREOMITREASON_H
#include <pandatoolbase.h>
////////////////////////////////////////////////////////////////////
// Enum : TextureOmitReason
// Description : This enumerates the various reasons a texture might
// have been omitted from a palette.
////////////////////////////////////////////////////////////////////
enum TextureOmitReason {
OR_none, // No reason: not omitted
OR_size, // Too big to put on a palette
OR_repeats, // The texture repeats and can't be palettized
OR_omitted, // Explicitly omitted by user in .txa file
OR_unused, // Not used by any egg files
OR_unknown, // The texture file is unknown, so can't determine its size
OR_solitary // Would be palettized, but no other textures are on the palette.
};
#endif

View File

@ -1,614 +0,0 @@
// Filename: texturePacking.cxx
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#include "texturePacking.h"
#include "paletteGroup.h"
#include "attribFile.h"
#include "palette.h"
#include "pTexture.h"
#include <pnmImage.h>
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TexturePacking::
TexturePacking(PTexture *texture, PaletteGroup *group) :
_texture(texture),
_group(group)
{
_attrib_file = _texture->_attrib_file;
_omit = _texture->_omit ? OR_omitted : OR_none;
_unused = true;
_uses_alpha = false;
_is_packed = false;
_palette = NULL;
_packing_changed = false;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TexturePacking::
~TexturePacking() {
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_texture
// Access: Public
// Description: Returns the texture this TexturePacking object refers
// to.
////////////////////////////////////////////////////////////////////
PTexture *TexturePacking::
get_texture() const {
return _texture;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_group
// Access: Public
// Description: Returns the palette group this TexturePacking object
// refers to.
////////////////////////////////////////////////////////////////////
PaletteGroup *TexturePacking::
get_group() const {
return _group;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_omit
// Access: Public
// Description: Returns the reason this texture was omitted from the
// particular palette group, if it was.
////////////////////////////////////////////////////////////////////
TextureOmitReason TexturePacking::
get_omit() const {
return _omit;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::set_omit
// Access: Public
// Description: Indicates the reason this texture was omitted from
// the particular palette group, if it was.
////////////////////////////////////////////////////////////////////
void TexturePacking::
set_omit(TextureOmitReason omit) {
_omit = omit;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::unused
// Access: Public
// Description: Returns true if this particular texture/group
// combination seems to be unused by any egg files,
// false if at least one egg file uses it.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
unused() const {
return _unused;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::set_unused
// Access: Public
// Description: Sets the state of the unused flag. See unused().
////////////////////////////////////////////////////////////////////
void TexturePacking::
set_unused(bool unused) {
_unused = unused;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::uses_alpha
// Access: Public
// Description: Returns true if this texture seems to require alpha;
// that is, at least one egg file that references the
// texture specifies an alpha mode.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
uses_alpha() const {
return _uses_alpha;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::set_uses_alpha
// Access: Public
// Description: Sets the state of the uses_alpha flag. See
// uses_alpha().
////////////////////////////////////////////////////////////////////
void TexturePacking::
set_uses_alpha(bool uses_alpha) {
_uses_alpha = uses_alpha;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::pack
// Access: Public
// Description: Adds this texture to its appropriate palette group,
// if it has not already been packed. Returns true if
// anything has changed (e.g. it has been packed), false
// otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
pack() {
if (!_is_packed) {
return _group->pack_texture(this, _attrib_file);
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::unpack
// Access: Public
// Description: Removes this texture from its palette image if it is
// on one. Returns true if anything has changed
// (e.g. it has been unpacked), false otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
unpack() {
if (_is_packed) {
return _palette->unpack_texture(this);
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::prepare_repack
// Access: Public
// Description: Checks if the texture needs to be repacked into a
// different location on the palette (for instance,
// because it has changed size). If so, unpacks it and
// returns true; otherwise, leaves it alone and returns
// false.
//
// If unpacking it will leave a hole or some similar
// nonsense, also sets optimal to false.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
prepare_repack(bool &optimal) {
bool needs_repack = false;
if (get_omit() == OR_none) {
// Here's a texture that thinks it wants to be packed. Does it?
int xsize, ysize;
int pal_xsize = _attrib_file->_pal_xsize;
int pal_ysize = _attrib_file->_pal_ysize;
if (!_texture->get_req(xsize, ysize)) {
// If we don't know the texture's size, we can't place it.
nout << "Warning! Can't determine size of " << _texture->get_name()
<< "\n";
set_omit(OR_unknown);
} else if ((xsize > pal_xsize || ysize > pal_ysize) ||
(xsize == pal_xsize && ysize == pal_ysize)) {
// If the texture is too big for the palette (or exactly fills the
// palette), we can't place it.
set_omit(OR_size);
} else {
// Ok, this texture really does want to be packed. Is it?
int px, py, m;
if (get_packed_size(px, py, m)) {
// The texture is packed. Does it have the right size?
if (px != xsize || py != ysize) {
// Oops, we'll have to repack it.
unpack();
optimal = false;
needs_repack = true;
}
if (m != _texture->get_margin()) {
// The margin has changed, although not the size. We
// won't have to repack it, but we do need to update it.
_texture->set_changed(true);
}
} else {
// The texture isn't packed. Need to pack it.
needs_repack = true;
}
}
}
if (get_omit() != OR_none) {
// Here's a texture that doesn't want to be packed. Is it?
if (unpack()) {
// It was! Not any more.
optimal = false;
}
}
return needs_repack;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::mark_pack_location
// Access: Public
// Description: Records the location at which the texture has been
// packed.
////////////////////////////////////////////////////////////////////
void TexturePacking::
mark_pack_location(Palette *palette, int left, int top,
int xsize, int ysize, int margin) {
_is_packed = true;
_palette = palette;
_pleft = left;
_ptop = top;
_pxsize = xsize;
_pysize = ysize;
_pmargin = margin;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::mark_unpacked
// Access: Public
// Description: Records that the texture has not been packed.
////////////////////////////////////////////////////////////////////
void TexturePacking::
mark_unpacked() {
_is_packed = false;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::is_packed
// Access: Public
// Description: Returns true if the texture has been packed, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
is_packed() const {
return _is_packed;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::is_really_packed
// Access: Public
// Description: Returns the same thing as is_packed(), except it
// doesn't consider a texture that has been left alone
// on a palette to be packed.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
is_really_packed() const {
return _is_packed && _omit != OR_solitary;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_palette
// Access: Public
// Description: Returns the particular palette image the texture has
// been packed into, or NULL if the texture is unpacked.
////////////////////////////////////////////////////////////////////
Palette *TexturePacking::
get_palette() const {
return _is_packed ? _palette : (Palette *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_packed_location
// Access: Public
// Description: Fills left and top with the upper-left corner of the
// rectangle in which the texture has been packed within
// its Palette image. Returns true if the texture has
// been packed, false otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
get_packed_location(int &left, int &top) const {
left = _pleft;
top = _ptop;
return _is_packed;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_packed_size
// Access: Public
// Description: Fills xsize, ysize, and margin with the size of the
// rectangle in which the texture has been packed within
// its Palette image. The margin is an interior margin.
// Returns true if the texture has been packed, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
get_packed_size(int &xsize, int &ysize, int &margin) const {
xsize = _pxsize;
ysize = _pysize;
margin = _pmargin;
return _is_packed;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::record_orig_state
// Access: Public
// Description: Records the current packing state, storing it aside
// as the state at load time. Later, when the packing
// state may have changed, packing_changed() will return
// true if it has or false if it has not.
////////////////////////////////////////////////////////////////////
void TexturePacking::
record_orig_state() {
_orig_is_packed = _is_packed;
if (_is_packed) {
_orig_palette_name = _palette->get_filename();
_opleft = _pleft;
_optop = _ptop;
_opxsize = _pxsize;
_opysize = _pysize;
_opmargin = _pmargin;
}
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::packing_changed
// Access: Public
// Description: Returns true if the packing has changed in any way
// since the last call to record_orig_state(), false
// otherwise.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
packing_changed() const {
if (_orig_is_packed != _is_packed) {
return true;
}
if (_is_packed) {
return _orig_palette_name != _palette->get_filename() ||
_opleft != _pleft ||
_optop != _ptop ||
_opxsize != _pxsize ||
_opysize != _pysize ||
_opmargin != _pmargin;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::set_changed
// Access: Public
// Description: Sets the state of the changed flag. If this is true,
// the state of this texture on this group is known to
// have changed in some way such that files that depend
// on it will need to be rebuilt.
////////////////////////////////////////////////////////////////////
void TexturePacking::
set_changed(bool changed) {
_packing_changed = changed;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::needs_refresh
// Access: Public
// Description: Returns true if the texture has changed in any
// significant way and the palette it's placed on needs
// to be regenerated.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
needs_refresh() {
bool any_change =
_texture->_texture_changed || _packing_changed;
if (!any_change) {
// We consider the texture to be out-of-date if it's moved around
// in the palette.
any_change = packing_changed();
}
if (!any_change && _texture->_file_exists) {
// Compare the texture's timestamp to that of its palette (or
// resized copy). If it's newer, it's changed and must be
// replaced.
Filename target_filename;
if (is_packed() && _omit == OR_none) {
// Compare to the palette file.
target_filename = _palette->get_filename();
if (_palette->new_palette()) {
// It's a brand new palette; don't even bother comparing
// timestamps.
any_change = true;
}
} else {
// Compare to the resized file.
target_filename = get_new_filename();
if (has_alpha_filename()) {
// If we have an alpha file, compare to the older of the two
// files.
Filename alpha_filename = get_alpha_filename();
if (target_filename.compare_timestamps(alpha_filename, true, true) > 0) {
target_filename = alpha_filename;
}
}
}
if (!any_change) {
any_change =
(target_filename.compare_timestamps(get_old_filename(), true, false) < 0);
}
}
return any_change;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::write_unplaced
// Access: Public
// Description: Writes an "unplaced" entry to the .pi file if this
// texture has not been placed on the group, describing
// the reason for the failure.
////////////////////////////////////////////////////////////////////
void TexturePacking::
write_unplaced(ostream &out) const {
if (_omit != OR_none && _omit != OR_unused) {
out << "unplaced " << _texture->get_name()
<< " in " << _group->get_name() << " because ";
switch (_omit) {
case OR_size:
out << "size";
break;
case OR_repeats:
out << "repeats";
break;
case OR_omitted:
out << "omitted";
break;
case OR_unused:
out << "unused";
break;
case OR_unknown:
out << "unknown";
break;
case OR_solitary:
out << "solitary";
break;
default:
nout << "Invalid type: " << (int)_omit << "\n";
abort();
}
out << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_new_filename
// Access: Public
// Description: Returns the filename to which this texture will be
// copied, assuming it is not placed on a palette.
////////////////////////////////////////////////////////////////////
Filename TexturePacking::
get_new_filename() const {
Filename dirname = _group->get_full_dirname(_attrib_file);
Filename new_filename(dirname, _texture->get_name());
new_filename = _attrib_file->make_color_filename(new_filename);
new_filename.standardize();
return new_filename;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::has_alpha_filename
// Access: Public
// Description: Returns true if this texture requires a separate
// alpha image file.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
has_alpha_filename() const {
return _uses_alpha && (_attrib_file->_alpha_type != (PNMFileType *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_alpha_filename
// Access: Public
// Description: Returns the filename for the alpha channel to which
// this texture will be copied, assuming it is not
// placed on a palette.
////////////////////////////////////////////////////////////////////
Filename TexturePacking::
get_alpha_filename() const {
return _attrib_file->make_alpha_filename(get_new_filename());
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::get_old_filename
// Access: Public
// Description: Returns the original filename of this texture.
////////////////////////////////////////////////////////////////////
Filename TexturePacking::
get_old_filename() const {
Filename old_filename = _texture->_filename;
old_filename.standardize();
return old_filename;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::transfer
// Access: Public
// Description: Copies an unpalettized image to the install
// directory, if it is not already there. The
// particular directory it is installed into may depend
// on the PaletteGroup to which it has been added.
// Returns true if successful, false if there is an
// error.
////////////////////////////////////////////////////////////////////
bool TexturePacking::
transfer() {
bool okflag = true;
Filename old_filename = get_old_filename();
Filename new_filename = get_new_filename();
if (new_filename == old_filename) {
nout << "*** Texture " << _texture->get_name()
<< " is already in the map directory!\n"
<< " Cannot modify texture in place!\n";
return false;
}
int nx, ny;
if (!_texture->get_req(nx, ny)) {
nout << "Unknown size for image " << _texture->get_name() << "\n";
nx = 16;
ny = 16;
}
if (_attrib_file->_force_power_2) {
int newx = to_power_2(nx);
int newy = to_power_2(ny);
if (newx != nx || newy != ny) {
nx = newx;
ny = newy;
}
}
PNMImage *image = _texture->read_image();
if (image == NULL) {
nout << "*** Unable to read " << _texture->get_name() << "\n";
okflag = false;
// Create a solid red texture for images we can't read.
image = new PNMImage(nx, ny);
image->fill(1.0, 0.0, 0.0);
} else {
// Should we scale it?
if (nx != image->get_x_size() || ny != image->get_y_size()) {
nout << "Resizing " << new_filename << " to "
<< nx << " " << ny << "\n";
PNMImage *new_image =
new PNMImage(nx, ny, image->get_color_type());
new_image->gaussian_filter_from(0.5, *image);
delete image;
image = new_image;
} else {
nout << "Copying " << new_filename
<< " (size " << nx << " " << ny << ")\n";
}
}
new_filename.make_dir();
if (!_attrib_file->write_image_file(*image, new_filename, get_alpha_filename())) {
nout << "Error in writing.\n";
okflag = false;
}
delete image;
return okflag;
}
////////////////////////////////////////////////////////////////////
// Function: TexturePacking::to_power_2
// Access: Public, Static
// Description: Returns the largest power of 2 less than or equal to
// value.
////////////////////////////////////////////////////////////////////
int TexturePacking::
to_power_2(int value) {
int x = 1;
while ((x << 1) <= value) {
x = (x << 1);
}
return x;
}

View File

@ -1,90 +0,0 @@
// Filename: texturePacking.h
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#ifndef TEXTUREPACKING_H
#define TEXTUREPACKING_H
#include <pandatoolbase.h>
#include "textureOmitReason.h"
#include <filename.h>
class PTexture;
class PaletteGroup;
class Palette;
class AttribFile;
////////////////////////////////////////////////////////////////////
// Class : TexturePacking
// Description : This structure defines how a particular texture is
// packed into a Palette image, or even whether it is
// packed at all.
////////////////////////////////////////////////////////////////////
class TexturePacking {
public:
TexturePacking(PTexture *texture, PaletteGroup *group);
~TexturePacking();
PTexture *get_texture() const;
PaletteGroup *get_group() const;
TextureOmitReason get_omit() const;
void set_omit(TextureOmitReason omit);
bool unused() const;
void set_unused(bool unused);
bool uses_alpha() const;
void set_uses_alpha(bool uses_alpha);
bool pack();
bool unpack();
bool prepare_repack(bool &optimal);
void mark_pack_location(Palette *palette, int left, int top,
int xsize, int ysize, int margin);
void mark_unpacked();
bool is_packed() const;
bool is_really_packed() const;
Palette *get_palette() const;
bool get_packed_location(int &left, int &top) const;
bool get_packed_size(int &xsize, int &ysize, int &margin) const;
void record_orig_state();
bool packing_changed() const;
void set_changed(bool changed);
bool needs_refresh();
void write_unplaced(ostream &out) const;
Filename get_new_filename() const;
bool has_alpha_filename() const;
Filename get_alpha_filename() const;
Filename get_old_filename() const;
bool transfer();
private:
static int to_power_2(int value);
AttribFile *_attrib_file;
PTexture *_texture;
PaletteGroup *_group;
TextureOmitReason _omit;
bool _unused;
bool _uses_alpha;
bool _is_packed;
Palette *_palette;
int _pleft, _ptop, _pxsize, _pysize, _pmargin;
bool _orig_is_packed;
Filename _orig_palette_name;
int _opleft, _optop, _opxsize, _opysize, _opmargin;
bool _packing_changed;
};
#endif

View File

@ -1,504 +0,0 @@
// Filename: userAttribLine.cxx
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#include "userAttribLine.h"
#include "string_utils.h"
#include "pTexture.h"
#include "paletteGroup.h"
#include "sourceEgg.h"
#include "attribFile.h"
#include "textureEggRef.h"
#include <notify.h>
#include <ctype.h>
UserAttribLine::
UserAttribLine(const string &cline, AttribFile *af) : _attrib_file(af) {
_is_old_style = false;
// By default, all lines are marked 'was_used'. That means they'll
// be preserved across a -k on the command line. Lines that name
// textures will have _was_used set false until a texture actually
// matches that line.
_was_used = true;
_got_dirname = false;
string line = cline;
// First, strip off the comment.
if (!line.empty()) {
if (line[0] == '#') {
_comment = line;
line = "";
} else {
size_t pos = line.find(" #");
if (pos != string::npos) {
while (pos > 0 && isspace(line[pos])) {
pos--;
}
_comment = line.substr(pos + 1);
line = line.substr(0, pos);
}
}
}
// Now, analyze the line.
_line_type = LT_invalid;
_xsize = 0;
_ysize = 0;
_scale_pct = 0.0;
_msize = -1;
_omit = false;
bool is_valid = true;
if (line.empty()) {
_line_type = LT_comment;
} else if (line[0] == ':') {
is_valid = keyword_line(line);
} else {
is_valid = texture_line(line);
}
if (!is_valid) {
_line_type = LT_invalid;
}
}
UserAttribLine::
~UserAttribLine() {
}
bool UserAttribLine::
is_valid() const {
return _line_type != LT_invalid;
}
bool UserAttribLine::
is_old_style() const {
return _is_old_style;
}
bool UserAttribLine::
was_used() const {
return _was_used;
}
void UserAttribLine::
write(ostream &out) const {
int i;
switch (_line_type) {
case LT_invalid:
out << "*** invalid line ***\n";
break;
case LT_comment:
break;
case LT_margin:
out << ":margin " << _msize;
break;
case LT_palette:
out << ":palette " << _xsize << " " << _ysize;
break;
case LT_group_relate:
out << ":group " << _names[0];
if (!_got_dirname) {
out << " dir " << _dirname;
}
if (!_names.empty()) {
out << " with";
for (i = 1; i < (int)_names.size(); i++) {
out << " " << _names[i];
}
}
out << "\n";
break;
case LT_size:
list_patterns(out) << " : " << _xsize << " " << _ysize;
if (_msize > 0) {
out << " " << _msize;
}
if (_omit) {
out << " omit";
}
break;
case LT_scale:
list_patterns(out) << " : " << _scale_pct << "%";
if (_msize > 0) {
out << " " << _msize;
}
if (_omit) {
out << " omit";
}
break;
case LT_name:
list_patterns(out) << " :";
if (_omit) {
out << " omit";
}
break;
case LT_group_assign:
list_patterns(out) << " : ";
list_names(out);
break;
default:
nout << "Unexpected type: " << (int)_line_type << "\n";
abort();
};
out << _comment << "\n";
}
bool UserAttribLine::
get_size_request(PTexture *texture, int &margin) {
if (_line_type == LT_group_assign) {
// We don't care about group lines for the size check.
return false;
}
// See if the texture name matches any of the filename patterns on
// this line.
string texture_name = texture->get_name();
bool matched_any = false;
Patterns::const_iterator pi;
for (pi = _patterns.begin();
pi != _patterns.end() && !matched_any;
++pi) {
if ((*pi).matches(texture_name)) {
matched_any = true;
}
// Also check if it matches any of the egg files this texture is
// on.
PTexture::Eggs::const_iterator ei;
for (ei = texture->_eggs.begin();
ei != texture->_eggs.end() && !matched_any;
++ei) {
string egg_name = (*ei)->_egg->get_egg_filename().get_basename();
if ((*pi).matches(egg_name)) {
matched_any = true;
}
}
}
if (matched_any) {
_was_used = true;
// It does! So do the right thing with this line.
switch (_line_type) {
case LT_invalid:
case LT_comment:
case LT_palette:
case LT_group_relate:
return false;
case LT_margin:
margin = _msize;
return false;
case LT_size:
texture->reset_req(_xsize, _ysize);
texture->set_margin(_msize < 0 ? margin : _msize);
if (_omit) {
texture->user_omit();
}
return true;
case LT_scale:
texture->scale_req(_scale_pct);
texture->set_margin(_msize < 0 ? margin : _msize);
if (_omit) {
texture->user_omit();
}
return true;
case LT_name:
if (_omit) {
texture->user_omit();
}
return true;
default:
nout << "Unexpected type: " << (int)_line_type << "\n";
abort();
}
}
return false;
}
bool UserAttribLine::
get_group_request(SourceEgg *egg) {
if (_line_type != LT_group_assign) {
// We're only looking for group lines now.
return false;
}
// See if the egg filename matches any of the filename patterns on
// this line.
string egg_name = egg->get_egg_filename().get_basename();
bool matched_any = false;
Patterns::const_iterator pi;
for (pi = _patterns.begin();
pi != _patterns.end() && !matched_any;
++pi) {
if ((*pi).matches(egg_name)) {
matched_any = true;
}
}
if (matched_any) {
_was_used = true;
// It does! Record the list of required groups with all of the
// textures on this egg file.
nassertr(!_names.empty(), false);
PaletteGroups groups;
Names::const_iterator ni = _names.begin();
PaletteGroup *group = _attrib_file->get_group(*ni);
groups.insert(group);
// The first-named group is preferred for any textures not already
// on another group.
PaletteGroup *preferred = group;
while (ni != _names.end()) {
group = _attrib_file->get_group(*ni);
groups.insert(group);
++ni;
}
PaletteGroup::complete_groups(groups);
egg->require_groups(preferred, groups);
return true;
}
return false;
}
ostream &UserAttribLine::
list_patterns(ostream &out) const {
if (!_patterns.empty()) {
out << _patterns[0];
for (int i = 1; i < (int)_patterns.size(); i++) {
out << " " << _patterns[i];
}
}
return out;
}
ostream &UserAttribLine::
list_names(ostream &out) const {
if (!_names.empty()) {
out << _names[0];
for (int i = 1; i < (int)_names.size(); i++) {
out << " " << _names[i];
}
}
return out;
}
bool UserAttribLine::
keyword_line(const string &line) {
vector_string words = extract_words(line);
assert(!words.empty());
if (words[0] == ":margin") {
_line_type = LT_margin;
if (words.size() != 2) {
nout << "Expected margin size.\n";
return false;
}
_msize = atoi(words[1].c_str());
} else if (words[0] == ":palette") {
_line_type = LT_palette;
if (words.size() != 3) {
nout << "Expected :palette xsize ysize.\n";
return false;
}
_xsize = atoi(words[1].c_str());
_ysize = atoi(words[2].c_str());
_attrib_file->_pal_xsize = _xsize;
_attrib_file->_pal_ysize = _ysize;
} else if (words[0] == ":group") {
_line_type = LT_group_relate;
if (words.size() < 2) {
nout << "Expected :group name.\n";
return false;
}
PaletteGroup *group = _attrib_file->get_group(words[1]);
int kw = 2;
while (kw < (int)words.size()) {
if (words[kw] == "with") {
kw++;
while (kw < (int)words.size()) {
group->add_parent(_attrib_file->get_group(words[kw]));
kw++;
}
} else if (words[kw] == "dir") {
kw++;
_got_dirname = true;
_dirname = words[kw];
group->set_dirname(_dirname);
kw++;
} else {
nout << "Invalid keyword: " << words[kw] << "\n";
return false;
}
}
} else {
nout << "Unknown keyword: " << words[0] << "\n";
return false;
}
return true;
}
bool UserAttribLine::
texture_line(const string &line) {
_was_used = false;
// Scan for a colon followed by a space.
size_t colon = line.find(": ");
if (colon == string::npos) {
return old_style_line(line);
}
// Split the line into two parts at the colon.
vector_string names = extract_words(line.substr(0, colon));
vector_string params = extract_words(line.substr(colon + 2));
vector_string::const_iterator ni;
for (ni = names.begin(); ni != names.end(); ++ni) {
_patterns.push_back(GlobPattern(*ni));
}
if (!params.empty() && params[params.size() - 1] == "omit") {
// If the last word is "omit", set the omit flag and remove the
// word.
_omit = true;
params.pop_back();
}
if (params.empty()) {
_line_type = LT_name;
return true;
}
// Is it a group name? If it is, this is an assignment of a texture
// or egg file to one or more groups.
if (!params[0].empty() && isalpha(params[0][0])) {
_names = params;
_line_type = LT_group_assign;
return true;
}
// Is it a percentage?
if (!params[0].empty() && params[0][params[0].size() - 1] == '%') {
_line_type = LT_scale;
_scale_pct = atof(params[0].c_str());
if (_scale_pct <= 0.0) {
nout << "Invalid scale percentage for ";
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
nout << ": " << _scale_pct << "%\n";
return false;
}
return true;
}
// It must be xsize ysize [margin]
if (params.size() == 2) {
_line_type = LT_size;
_xsize = atoi(params[0].c_str());
_ysize = atoi(params[1].c_str());
if (_xsize <= 0 || _ysize <= 0) {
nout << "Invalid texture size for ";
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
nout << ": " << _xsize << " " << _ysize << "\n";
return false;
}
return true;
}
if (params.size() == 3) {
_line_type = LT_size;
_xsize = atoi(params[0].c_str());
_ysize = atoi(params[1].c_str());
_msize = atoi(params[2].c_str());
if (_xsize <= 0 || _ysize <= 0) {
nout << "Invalid texture size for ";
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
nout << ": " << _xsize << " " << _ysize << "\n";
return false;
}
return true;
}
nout << "Expected xsize ysize [msize]\n";
return false;
}
bool UserAttribLine::
old_style_line(const string &line) {
vector_string words = extract_words(line);
assert(!words.empty());
if (words.size() != 3 && words.size() != 4) {
nout << "Colon omitted.\n";
return false;
}
_is_old_style = true;
_line_type = LT_size;
_patterns.push_back(GlobPattern(words[0]));
_xsize = atoi(words[1].c_str());
_ysize = atoi(words[2].c_str());
if (words.size() > 3) {
_msize = atoi(words[3].c_str());
if (_msize < 0) {
_msize = -1;
_omit = true;
} else if (_msize == _attrib_file->_default_margin) {
_msize = -1;
}
} else {
_msize = -1;
}
if (_xsize <= 0 || _ysize <= 0) {
nout << "Invalid texture size for " << words[0] << ": "
<< _xsize << " " << _ysize << "\n";
return false;
}
return true;
}

View File

@ -1,91 +0,0 @@
// Filename: userAttribLine.h
// Created by: drose (02Sep99)
//
////////////////////////////////////////////////////////////////////
#ifndef USERATTRIBLINE_H
#define USERATTRIBLINE_H
#include <pandatoolbase.h>
#include <globPattern.h>
#include <vector_string.h>
#include <vector>
class AttribFile;
class PTexture;
class SourceEgg;
////////////////////////////////////////////////////////////////////
// Class : UserAttribLine
// Description : A single entry in the .txa file, this defines how the
// user would like some particular texture to be scaled.
////////////////////////////////////////////////////////////////////
//
// This might be a line of any of the following forms:
//
// (blank line)
// # Comment
// :margin msize
// :palette xsize ysize
// :group groupname [dir dirname] [with groupname [groupname ...]]
// texturename xsize ysize msize
// texturename [texturename ...] : xsize ysize [msize] [omit]
// texturename [texturename ...] : scale% [msize] [omit]
// texturename [texturename ...] : [omit]
// texturename [texturename ...] : groupname [groupname ...]
// eggname [eggname ...] : groupname [groupname ...]
//
class UserAttribLine {
public:
UserAttribLine(const string &line, AttribFile *af);
~UserAttribLine();
bool is_valid() const;
bool is_old_style() const;
bool was_used() const;
void write(ostream &out) const;
bool get_size_request(PTexture *texture, int &margin);
bool get_group_request(SourceEgg *egg);
private:
enum LineType {
LT_invalid,
LT_comment,
LT_margin, LT_palette, LT_group_relate,
LT_size, LT_scale, LT_name,
LT_group_assign
};
typedef vector<GlobPattern> Patterns;
Patterns _patterns;
typedef vector_string Names;
Names _names;
ostream &list_patterns(ostream &out) const;
ostream &list_names(ostream &out) const;
bool keyword_line(const string &line);
bool texture_line(const string &line);
bool old_style_line(const string &line);
string _comment;
LineType _line_type;
int _xsize, _ysize;
double _scale_pct;
int _msize;
string _dirname;
bool _got_dirname;
bool _omit;
bool _is_old_style;
bool _was_used;
AttribFile *_attrib_file;
};
#endif

View File

@ -6,16 +6,25 @@
egg:c panda:m
#define SOURCES \
eggBase.cxx eggBase.h eggConverter.cxx eggConverter.h eggFilter.cxx \
eggFilter.h eggMultiBase.cxx eggMultiBase.h \
eggBase.cxx eggBase.h \
eggCharacterData.cxx eggCharacterData.h eggCharacterData.I \
eggCharacterFilter.cxx eggCharacterFilter.h \
eggConverter.cxx eggConverter.h eggFilter.cxx \
eggFilter.h eggJointData.cxx eggJointData.h eggJointData.I \
eggMultiBase.cxx eggMultiBase.h \
eggMultiFilter.cxx eggMultiFilter.h \
eggReader.cxx eggReader.h eggToSomething.cxx \
eggReader.cxx eggReader.h \
eggToSomething.cxx \
eggToSomething.h eggWriter.cxx eggWriter.h somethingToEgg.cxx \
somethingToEgg.h
#define INSTALL_HEADERS \
eggBase.h eggConverter.h eggFilter.h eggMultiBase.h eggMultiFilter.h \
eggReader.h eggToSomething.h eggWriter.h somethingToEgg.h
eggBase.h eggCharacterData.h \
eggCharacterData.I eggCharacterFilter.h \
eggConverter.h eggFilter.h eggJointData.h eggJointData.I \
eggMultiBase.h eggMultiFilter.h \
eggReader.h \
eggToSomething.h eggWriter.h somethingToEgg.h
#end ss_lib_target

View File

@ -0,0 +1,27 @@
// Filename: eggCharacterData.I
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_num_eggs
// Access: Public
// Description: Returns the number of egg files that have
// successfully been added to the Character table.
////////////////////////////////////////////////////////////////////
INLINE int EggCharacterData::
get_num_eggs() const {
return _eggs.size();
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_egg
// Access: Public
// Description: Returns the ith egg file.
////////////////////////////////////////////////////////////////////
INLINE EggData *EggCharacterData::
get_egg(int i) const {
nassertr(i >= 0 && i < (int)_eggs.size(), (EggData *)NULL);
return _eggs[i]._egg;
}

View File

@ -0,0 +1,452 @@
// Filename: eggCharacterData.cxx
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#include "eggCharacterData.h"
#include "eggJointData.h"
#include "eggGroup.h"
#include "eggTable.h"
#include <indirectCompareNames.h>
#include <indent.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterData::
EggCharacterData() {
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterData::
~EggCharacterData() {
Characters::iterator ci;
for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
CharacterInfo &character_info = (*ci);
delete character_info._root_joint;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::add_egg
// Access: Public
// Description: Adds a new egg file to the list of models and
// animation files for this particular character.
// Returns true if the file is successfully added, or
// false if there is some problem (for instance, it does
// not contain a character model or animation table).
//
// If the joint hierarchy does not match the existing
// joint hierarchy, a best match is attempted.
////////////////////////////////////////////////////////////////////
bool EggCharacterData::
add_egg(EggData *egg) {
_top_egg_nodes.clear();
if (!scan_hierarchy(egg)) {
return false;
}
int egg_index = _eggs.size();
_eggs.push_back(EggInfo());
EggInfo &egg_info = _eggs.back();
egg_info._egg = egg;
// Now, for each model, add an entry in the egg_info and match the
// joint hierarchy to the known joints.
TopEggNodesByName::iterator tni;
for (tni = _top_egg_nodes.begin(); tni != _top_egg_nodes.end(); ++tni) {
string character_name = (*tni).first;
TopEggNodes &top_nodes = (*tni).second;
TopEggNodes::iterator ti;
for (ti = top_nodes.begin(); ti != top_nodes.end(); ++ti) {
EggNode *model = (*ti).first;
EggNodeList &egg_nodes = (*ti).second;
int model_index = egg_info._models.size();
egg_info._models.push_back(model);
match_egg_nodes(get_root_joint(character_name),
egg_nodes, egg_index, model_index);
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_root_joint
// Access: Protected
// Description: Returns the root of the joint hierarchy for the named
// character. This is actually a fictitious node that
// does not correspond to any particular nodes in the
// character hierarchy; it exists to hold all of the top
// joints (if any) read from the character hierarchy.
////////////////////////////////////////////////////////////////////
EggJointData *EggCharacterData::
get_root_joint(const string &character_name) {
// Does the named character exist yet?
Characters::iterator ci;
for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
CharacterInfo &character_info = (*ci);
if (character_info._name == character_name) {
return character_info._root_joint;
}
}
// Define a new character.
_characters.push_back(CharacterInfo());
CharacterInfo &character_info = _characters.back();
character_info._name = character_name;
character_info._root_joint = make_joint_data();
return character_info._root_joint;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::make_joint_data
// Access: Protected, Virtual
// Description: Allocates and returns a new EggJointData structure.
// This is primarily intended as a hook so derived
// classes can customize the type of EggJointData nodes
// used to represent the joint hierarchy.
////////////////////////////////////////////////////////////////////
EggJointData *EggCharacterData::
make_joint_data() {
return new EggJointData;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::scan_hierarchy
// Access: Private
// Description: Walks the given egg data's hierarchy, looking for
// either the start of an animation channel or the start
// of a character model. Returns true if either (or
// both) is found, false if the model appears to have
// nothing to do with characters.
//
// Fills up the _top_egg_nodes according to the nodes
// found.
////////////////////////////////////////////////////////////////////
bool EggCharacterData::
scan_hierarchy(EggNode *egg_node) {
if (egg_node->is_of_type(EggGroup::get_class_type())) {
EggGroup *group = DCAST(EggGroup, egg_node);
if (group->get_dart_type() != EggGroup::DT_none) {
// A group with a <Dart> flag begins a character model.
scan_for_top_joints(group, group, group->get_name());
return true;
}
} else if (egg_node->is_of_type(EggTable::get_class_type())) {
EggTable *table = DCAST(EggTable, egg_node);
if (table->get_table_type() == EggTable::TT_bundle) {
// A <Bundle> begins an animation table.
scan_for_top_tables(table, table, table->get_name());
return true;
}
}
bool character_found = false;
if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *group = DCAST(EggGroupNode, egg_node);
EggGroupNode::iterator gi;
for (gi = group->begin(); gi != group->end(); ++gi) {
if (scan_hierarchy(*gi)) {
character_found = true;
}
}
}
return character_found;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::scan_for_top_joints
// Access: Private
// Description: Once a character model has been found, continue
// scanning the egg hierarchy to look for the topmost
// <Joint> nodes encountered.
////////////////////////////////////////////////////////////////////
void EggCharacterData::
scan_for_top_joints(EggNode *egg_node, EggNode *model_root,
const string &character_name) {
if (egg_node->is_of_type(EggGroup::get_class_type())) {
EggGroup *group = DCAST(EggGroup, egg_node);
if (group->has_lod()) {
// This flag has an LOD specification.
model_root = group;
}
if (group->get_group_type() == EggGroup::GT_joint) {
// A <Joint> node begins a model hierarchy.
_top_egg_nodes[character_name][model_root].push_back(group);
return;
}
}
if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *group = DCAST(EggGroupNode, egg_node);
EggGroupNode::iterator gi;
for (gi = group->begin(); gi != group->end(); ++gi) {
scan_for_top_joints(*gi, model_root, character_name);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::scan_for_top_tables
// Access: Private
// Description: Once an animation has been found, continue scanning
// the egg hierarchy to look for the topmost <Table>
// nodes encountered.
////////////////////////////////////////////////////////////////////
void EggCharacterData::
scan_for_top_tables(EggTable *bundle, EggNode *model_root,
const string &character_name) {
// We really only need to check the immediate children of the bundle
// for a table node called "<skeleton>".
EggGroupNode::iterator gi;
for (gi = bundle->begin(); gi != bundle->end(); ++gi) {
EggNode *child = (*gi);
if (child->is_of_type(EggTable::get_class_type())) {
EggTable *table = DCAST(EggTable, child);
if (table->get_name() == "<skeleton>") {
// Here it is! Now the immediate children of this node are
// the top tables.
EggGroupNode::iterator cgi;
for (cgi = table->begin(); cgi != table->end(); ++cgi) {
EggNode *grandchild = (*cgi);
if (grandchild->is_of_type(EggTable::get_class_type())) {
_top_egg_nodes[character_name][model_root].push_back(grandchild);
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::match_egg_nodes
// Access: Private
// Description: Attempts to match up the indicated list of egg_nodes
// with the children of the given joint_data, by name if
// possible.
//
// Also recurses on each matched joint to build up the
// entire joint hierarchy.
////////////////////////////////////////////////////////////////////
void EggCharacterData::
match_egg_nodes(EggJointData *joint_data, EggNodeList &egg_nodes,
int egg_index, int model_index) {
// Sort the list of egg_nodes in order by name. This will make the
// matching up by names easier and more reliable.
sort(egg_nodes.begin(), egg_nodes.end(), IndirectCompareNames<Namable>());
if (joint_data->_children.empty()) {
// If the EggJointData has no children yet, we must be the first.
// Gleefully define all the joints.
EggNodeList::iterator ei;
for (ei = egg_nodes.begin(); ei != egg_nodes.end(); ++ei) {
EggNode *egg_node = (*ei);
EggJointData *data = make_joint_data();
joint_data->_children.push_back(data);
found_egg_node_match(data, egg_node, egg_index, model_index);
}
} else {
// The EggJointData already has children; therefore, we have to
// match our joints up with the already-existing ones.
EggNodeList extra_egg_nodes;
EggJointData::Children extra_data;
EggNodeList::iterator ei;
EggJointData::Children::iterator di;
ei = egg_nodes.begin();
di = joint_data->_children.begin();
while (ei != egg_nodes.end() && di != joint_data->_children.end()) {
EggNode *egg_node = (*ei);
EggJointData *data = (*di);
if (egg_node->get_name() < data->get_name()) {
// Here's a joint in the egg file, unmatched in the data.
extra_egg_nodes.push_back(egg_node);
++ei;
} else if (data->get_name() < egg_node->get_name()) {
// Here's a joint in the data, umatched by the egg file.
extra_data.push_back(data);
++di;
} else {
// Hey, these two match! Hooray!
found_egg_node_match(data, egg_node, egg_index, model_index);
++ei;
++di;
}
}
while (ei != egg_nodes.end()) {
EggNode *egg_node = (*ei);
// Here's a joint in the egg file, unmatched in the data.
extra_egg_nodes.push_back(egg_node);
++ei;
}
while (di != joint_data->_children.end()) {
EggJointData *data = (*di);
// Here's a joint in the data, umatched by the egg file.
extra_data.push_back(data);
++di;
}
if (!extra_egg_nodes.empty()) {
// If we have some extra egg_nodes, we have to find a place to
// match them. (If we only had extra data, we don't care.)
// First, check to see if any of the names match any past-used
// name.
EggNodeList more_egg_nodes;
for (ei = extra_egg_nodes.begin(); ei != extra_egg_nodes.end(); ++ei) {
EggNode *egg_node = (*ei);
bool matched = false;
for (di = extra_data.begin();
di != extra_data.end() && !matched;
++di) {
EggJointData *data = (*di);
if (data->matches_name(egg_node->get_name())) {
matched = true;
found_egg_node_match(data, egg_node, egg_index, model_index);
extra_data.erase(di);
}
}
if (!matched) {
// This joint name was never seen before.
more_egg_nodes.push_back(egg_node);
}
}
extra_egg_nodes.swap(more_egg_nodes);
}
if (!extra_egg_nodes.empty()) {
// Ok, we've still got to find a home for these remaining
// egg_nodes.
if (extra_egg_nodes.size() == extra_data.size()) {
// Match 'em up one-for-one.
size_t i;
for (i = 0; i < extra_egg_nodes.size(); i++) {
EggNode *egg_node = extra_egg_nodes[i];
EggJointData *data = extra_data[i];
found_egg_node_match(data, egg_node, egg_index, model_index);
}
} else {
// Just tack 'em on the end.
EggNodeList::iterator ei;
for (ei = extra_egg_nodes.begin(); ei != extra_egg_nodes.end(); ++ei) {
EggNode *egg_node = (*ei);
EggJointData *data = make_joint_data();
joint_data->_children.push_back(data);
found_egg_node_match(data, egg_node, egg_index, model_index);
}
}
}
}
// Now sort the generated joint data hierarchy by name, just to be
// sure.
sort(joint_data->_children.begin(), joint_data->_children.end(),
IndirectCompareNames<Namable>());
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::found_egg_node_match
// Access: Private
// Description: Marks a one-to-one association between the indicated
// EggJointData and the indicated EggNode, and then
// recurses below.
////////////////////////////////////////////////////////////////////
void EggCharacterData::
found_egg_node_match(EggJointData *data, EggNode *egg_node,
int egg_index, int model_index) {
data->add_egg_node(egg_index, model_index, egg_node);
if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *group_node = DCAST(EggGroupNode, egg_node);
// Now consider all the children of egg_node that are themselves
// joints or tables.
EggNodeList egg_nodes;
// Two approaches: either we are scanning a model with joints, or
// an animation bundle with tables.
if (egg_node->is_of_type(EggGroup::get_class_type())) {
// A model with joints.
EggGroupNode::iterator gi;
for (gi = group_node->begin(); gi != group_node->end(); ++gi) {
EggNode *child = (*gi);
if (child->is_of_type(EggGroup::get_class_type())) {
EggGroup *group = DCAST(EggGroup, child);
if (group->get_group_type() == EggGroup::GT_joint) {
egg_nodes.push_back(group);
}
}
}
} else {
// An animation bundle with tables.
EggGroupNode::iterator gi;
for (gi = group_node->begin(); gi != group_node->end(); ++gi) {
EggNode *child = (*gi);
if (child->is_of_type(EggTable::get_class_type())) {
EggTable *table = DCAST(EggTable, child);
if (!(table->get_name() == "xform")) {
egg_nodes.push_back(table);
}
}
}
}
if (!egg_nodes.empty()) {
match_egg_nodes(data, egg_nodes, egg_index, model_index);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void EggCharacterData::
write(ostream &out, int indent_level) const {
Characters::const_iterator ci;
for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
const CharacterInfo &character_info = (*ci);
indent(out, indent_level)
<< "Character " << character_info._name << ":\n";
character_info._root_joint->write(out, indent_level + 2);
}
}

View File

@ -0,0 +1,82 @@
// Filename: eggCharacterData.h
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#ifndef EGGCHARACTERDATA_H
#define EGGCHARACTERDATA_H
#include <pandatoolbase.h>
#include <eggData.h>
#include <eggNode.h>
#include <pointerTo.h>
class EggJointData;
class EggTable;
////////////////////////////////////////////////////////////////////
// Class : EggCharacterData
// Description : Represents a set of characters, as read and collected
// from several models and animation files. Each
// character is the root of a hierarchy of EggJointData
// nodes.
////////////////////////////////////////////////////////////////////
class EggCharacterData {
public:
EggCharacterData();
virtual ~EggCharacterData();
bool add_egg(EggData *egg);
INLINE int get_num_eggs() const;
INLINE EggData *get_egg(int i) const;
virtual void write(ostream &out, int indent_level = 0) const;
protected:
virtual EggJointData *make_joint_data();
EggJointData *get_root_joint(const string &character_name);
class EggInfo {
public:
PT(EggData) _egg;
typedef vector<PT(EggNode)> Models;
Models _models;
};
typedef vector<EggInfo> Eggs;
Eggs _eggs;
class CharacterInfo {
public:
string _name;
EggJointData *_root_joint;
};
typedef vector<CharacterInfo> Characters;
Characters _characters;
private:
bool scan_hierarchy(EggNode *egg_node);
void scan_for_top_joints(EggNode *egg_node, EggNode *model_root,
const string &character_name);
void scan_for_top_tables(EggTable *bundle, EggNode *model_root,
const string &character_name);
typedef vector<EggNode *> EggNodeList;
typedef map<EggNode *, EggNodeList> TopEggNodes;
typedef map<string, TopEggNodes> TopEggNodesByName;
TopEggNodesByName _top_egg_nodes;
void match_egg_nodes(EggJointData *joint_data, EggNodeList &egg_nodes,
int egg_index, int model_index);
void found_egg_node_match(EggJointData *data, EggNode *egg_node,
int egg_index, int model_index);
};
#include "eggCharacterData.I"
#endif

View File

@ -0,0 +1,75 @@
// Filename: eggCharacterFilter.cxx
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#include "eggCharacterFilter.h"
#include "eggCharacterData.h"
////////////////////////////////////////////////////////////////////
// Function: EggCharacterFilter::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterFilter::
EggCharacterFilter() : EggMultiFilter(false) {
_character_data = (EggCharacterData *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterFilter::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterFilter::
~EggCharacterFilter() {
if (_character_data != (EggCharacterData *)NULL) {
delete _character_data;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterFilter::post_command_line
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool EggCharacterFilter::
post_command_line() {
if (_character_data == (EggCharacterData *)NULL) {
_character_data = make_character_data();
}
if (!EggMultiFilter::post_command_line()) {
return false;
}
Eggs::iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
EggData *data = (*ei);
if (!_character_data->add_egg(data)) {
nout << data->get_egg_filename().get_basename()
<< " does not contain a character model or animation channel.\n";
return false;
}
}
_character_data->write(cerr);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterFilter::make_character_data
// Access: Protected, Virtual
// Description: Allocates and returns a new EggCharacterData structure.
// This is primarily intended as a hook so derived
// classes can customize the type of EggCharacterData nodes
// used to represent the character information.
////////////////////////////////////////////////////////////////////
EggCharacterData *EggCharacterFilter::
make_character_data() {
return new EggCharacterData;
}

View File

@ -0,0 +1,41 @@
// Filename: eggCharacterFilter.h
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#ifndef EGGCHARACTERFILTER_H
#define EGGCHARACTERFILTER_H
#include <pandatoolbase.h>
#include "eggMultiFilter.h"
class EggCharacterData;
////////////////////////////////////////////////////////////////////
// Class : EggCharacterFilter
// Description : This is the base class for a family of programs that
// operate on a number of character models and their
// associated animation files together. It reads in a
// number of egg files, any combination of model files
// or character files which must all represent the same
// character skeleton, and maintains a single hierarchy
// of joints and sliders that may be operated on before
// writing the files back out.
////////////////////////////////////////////////////////////////////
class EggCharacterFilter : public EggMultiFilter {
public:
EggCharacterFilter();
virtual ~EggCharacterFilter();
protected:
virtual bool post_command_line();
virtual EggCharacterData *make_character_data();
EggCharacterData *_character_data;
};
#endif

View File

@ -0,0 +1,26 @@
// Filename: eggJointData.I
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_num_children
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE int EggJointData::
get_num_children() const {
return _children.size();
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_child
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE EggJointData *EggJointData::
get_child(int n) const {
nassertr(n >= 0 && n < (int)_children.size(), (EggJointData *)NULL);
return _children[n];
}

View File

@ -0,0 +1,93 @@
// Filename: eggJointData.cxx
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#include "eggJointData.h"
#include <indent.h>
////////////////////////////////////////////////////////////////////
// Function: EggJointData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggJointData::
EggJointData() {
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EggJointData::
~EggJointData() {
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::matches_name
// Access: Public
// Description: Returns true if the indicated name matches any name
// that was ever matched with this particular joint,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool EggJointData::
matches_name(const string &name) const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::add_egg_node
// Access: Public
// Description: Adds the indicated model joint or anim table to the
// data.
////////////////////////////////////////////////////////////////////
void EggJointData::
add_egg_node(int egg_index, int model_index, EggNode *egg_node) {
if (!has_name() && egg_node->has_name()) {
set_name(egg_node->get_name());
}
while ((int)_joints.size() <= egg_index) {
_joints.push_back(JointNodes());
}
JointNodes &nodes = _joints[egg_index];
while ((int)nodes.size() <= model_index) {
nodes.push_back((EggNode *)NULL);
}
nodes[model_index] = egg_node;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void EggJointData::
write(ostream &out, int indent_level) const {
indent(out, indent_level)
<< "Joint " << get_name()
<< " (models:";
for (size_t egg_index = 0; egg_index < _joints.size(); egg_index++) {
const JointNodes &nodes = _joints[egg_index];
if (nodes.size() == 1) {
out << " " << egg_index;
} else if (nodes.size() > 1) {
for (size_t model_index = 0; model_index < nodes.size(); model_index++) {
if (nodes[model_index] != (EggNode *)NULL) {
out << " " << egg_index << "/" << model_index;
}
}
}
}
out << ") {\n";
Children::const_iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) {
(*ci)->write(out, indent_level + 2);
}
indent(out, indent_level) << "}\n";
}

View File

@ -0,0 +1,63 @@
// Filename: eggJointData.h
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#ifndef EGGJOINTDATA_H
#define EGGJOINTDATA_H
#include <pandatoolbase.h>
#include <eggNode.h>
#include <pointerTo.h>
#include <namable.h>
class EggGroup;
////////////////////////////////////////////////////////////////////
// Class : EggJointData
// Description : This is one node of a hierarchy of EggJointData
// nodes, each of which represents a single joint of the
// character hierarchy across all loaded files: the
// various models, the LOD's of each model, and the
// various animation channel files.
////////////////////////////////////////////////////////////////////
class EggJointData : public Namable {
public:
EggJointData();
virtual ~EggJointData();
INLINE int get_num_children() const;
INLINE EggJointData *get_child(int n) const;
bool matches_name(const string &name) const;
void add_egg_node(int egg_index, int model_index, EggNode *egg_node);
virtual void write(ostream &out, int indent_level = 0) const;
protected:
typedef vector<PT(EggNode)> JointNodes;
typedef vector<JointNodes> Joints;
Joints _tables;
// _tables[i][anim] is the table for the animth bundle of the ith
// file.
Joints _joints;
// _joints[i][model] is the joint for the modelth lod of the ith
// file.
LMatrix4d _rest_transform;
LMatrix4d _net_rest_transform;
typedef vector<EggJointData *> Children;
Children _children;
friend class EggCharacterData;
};
#include "eggJointData.I"
#endif

View File

@ -97,9 +97,9 @@ append_command_comment(EggData &data) {
// information stored with each egg structure, by
// deriving from EggData.
////////////////////////////////////////////////////////////////////
EggData *EggMultiBase::
PT(EggData) EggMultiBase::
read_egg(const Filename &filename) {
EggData *data = new EggData;
PT(EggData) data = new EggData;
// First, we always try to resolve a filename from the current
// directory. This means a local filename will always be found
@ -110,13 +110,11 @@ read_egg(const Filename &filename) {
if (!data->read(local_filename)) {
// Failure reading.
delete data;
return (EggData *)NULL;
}
if (_force_complete) {
if (!data->resolve_externals()) {
delete data;
return (EggData *)NULL;
}
}

View File

@ -10,9 +10,10 @@
#include <programBase.h>
#include <coordinateSystem.h>
#include <eggData.h>
#include <pointerTo.h>
class Filename;
class EggData;
////////////////////////////////////////////////////////////////////
// Class : EggMultiBase
@ -30,13 +31,13 @@ public:
protected:
void append_command_comment(EggData &_data);
virtual EggData *read_egg(const Filename &filename);
virtual PT(EggData) read_egg(const Filename &filename);
protected:
bool _got_coordinate_system;
CoordinateSystem _coordinate_system;
typedef vector<EggData *> Eggs;
typedef vector<PT(EggData)> Eggs;
Eggs _eggs;
bool _force_complete;

View File

@ -93,7 +93,7 @@ handle_args(ProgramBase::Args &args) {
Args::const_iterator ai;
for (ai = args.begin(); ai != args.end(); ++ai) {
EggData *data = read_egg(*ai);
PT(EggData) data = read_egg(*ai);
if (data == (EggData *)NULL) {
// Rather than returning false, we simply exit here, so the
// ProgramBase won't try to tell the user how to run the program

View File

@ -20,3 +20,11 @@
eggTextureCards.cxx eggTextureCards.h
#end bin_target
#begin noinst_bin_target
#define TARGET egg-topstrip
#define SOURCES \
eggTopstrip.cxx eggTopstrip.h
#end noinst_bin_target

View File

@ -0,0 +1,40 @@
// Filename: eggTopstrip.cxx
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#include "eggTopstrip.h"
////////////////////////////////////////////////////////////////////
// Function: EggTopstrip::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggTopstrip::
EggTopstrip() {
set_program_description
("egg-topstrip reads a character model and its associated animation "
"files, and unapplies the animation from one of the top joints. "
"This effectively freezes that particular joint, and makes the rest "
"of the character relative to that joint.\n\n"
"This is a particularly useful thing to do to generate character "
"models that can stack one on top of the other in a sensible way.");
}
////////////////////////////////////////////////////////////////////
// Function: EggTopstrip::run
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EggTopstrip::
run() {
}
int main(int argc, char *argv[]) {
EggTopstrip prog;
prog.parse_command_line(argc, argv);
prog.run();
return 0;
}

View File

@ -0,0 +1,29 @@
// Filename: eggTopstrip.h
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
#ifndef EGGTOPSTRIP_H
#define EGGTOPSTRIP_H
#include <pandatoolbase.h>
#include <eggCharacterFilter.h>
////////////////////////////////////////////////////////////////////
// Class : EggTopstrip
// Description : Reads a character model and/or animations and strips
// out the animation from one of the top joints from the
// entire character. Particularly useful for generating
// stackable character models from separately-extracted
// characters.
////////////////////////////////////////////////////////////////////
class EggTopstrip : public EggCharacterFilter {
public:
EggTopstrip();
void run();
};
#endif