*** empty log message ***

This commit is contained in:
David Rose 2000-11-08 22:38:50 +00:00
parent 67faa460e7
commit e2e9663c4c
25 changed files with 1980 additions and 669 deletions

View File

@ -322,6 +322,21 @@ sorts_less_than(const EggTexture &other, int eq) const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::has_alpha_channel
// Access: Public
// Description: Given the number of color components (channels) in
// the image file as actually read from the disk, return
// true if this texture seems to have an alpha channel
// or not. This depends on the EggTexture's format as
// well as the number of channels.
////////////////////////////////////////////////////////////////////
bool EggTexture::
has_alpha_channel(int num_components) const {
return (num_components == 2 || num_components == 4 ||
(num_components == 1 && _format == F_alpha));
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::string_format
// Access: Public

View File

@ -44,6 +44,8 @@ public:
bool is_equivalent_to(const EggTexture &other, int eq) const;
bool sorts_less_than(const EggTexture &other, int eq) const;
bool has_alpha_channel(int num_components) const;
enum Format {
F_unspecified,
F_rgba, F_rgba12, F_rgba8, F_rgba4, F_rgba5,

View File

@ -871,8 +871,7 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
Texture *tex = def._texture->get_texture();
nassertv(tex != (Texture *)NULL);
int num_components = tex->_pbuffer->get_num_components();
if (num_components == 2 || num_components == 4 ||
(num_components == 1 && egg_tex->get_format() == EggTexture::F_alpha)) {
if (egg_tex->has_alpha_channel(num_components)) {
implicit_alpha = true;
}
}

View File

@ -13,6 +13,25 @@ INLINE GlobPattern::
GlobPattern(const string &pattern) : _pattern(pattern) {
}
////////////////////////////////////////////////////////////////////
// Function: GlobPattern::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE GlobPattern::
GlobPattern(const GlobPattern &copy) : _pattern(copy._pattern) {
}
////////////////////////////////////////////////////////////////////
// Function: GlobPattern::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void GlobPattern::
operator = (const GlobPattern &copy) {
_pattern = copy._pattern;
}
////////////////////////////////////////////////////////////////////
// Function: GlobPattern::set_pattern
// Access: Public
@ -47,3 +66,12 @@ matches(const string &candidate) const {
candidate.begin(), candidate.end());
}
////////////////////////////////////////////////////////////////////
// Function: GlobPattern::output
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void GlobPattern::
output(ostream &out) const {
out << _pattern;
}

View File

@ -26,11 +26,15 @@
class EXPCL_PANDA GlobPattern {
public:
INLINE GlobPattern(const string &pattern = string());
INLINE GlobPattern(const GlobPattern &copy);
INLINE void operator = (const GlobPattern &copy);
INLINE void set_pattern(const string &pattern);
INLINE const string &get_pattern() const;
INLINE bool matches(const string &candidate) const;
INLINE void output(ostream &out) const;
private:
bool matches_substr(string::const_iterator pi,
@ -45,6 +49,12 @@ private:
string _pattern;
};
INLINE ostream &operator << (ostream &out, const GlobPattern &glob) {
glob.output(out);
return out;
}
#include "globPattern.I"
#endif

View File

@ -1,4 +1,4 @@
#begin bin_target
#begin noinst_bin_target
#define TARGET egg-palettize
#define LOCAL_LIBS \
eggbase progbase
@ -10,11 +10,14 @@
#define SOURCES \
attribFile.cxx attribFile.h config_egg_palettize.cxx \
eggPalettize.cxx eggPalettize.h \
imageFile.cxx imageFile.h palette.cxx palette.h sourceEgg.cxx \
sourceEgg.h string_utils.cxx string_utils.h pTexture.cxx pTexture.h \
userAttribLine.cxx userAttribLine.h
imageFile.cxx imageFile.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
#end noinst_bin_target

View File

@ -8,6 +8,8 @@
#include "eggPalettize.h"
#include "string_utils.h"
#include "pTexture.h"
#include "texturePacking.h"
#include "paletteGroup.h"
#include "palette.h"
#include "sourceEgg.h"
@ -30,7 +32,6 @@ AttribFile(const Filename &filename) {
_optimal = false;
_txa_needs_rewrite = false;
_palette_prefix = _name + "-palette.";
_pal_xsize = 512;
_pal_ysize = 512;
_default_margin = 2;
@ -87,7 +88,7 @@ release_lock() {
}
bool AttribFile::
read() {
read(bool force_redo_all) {
bool okflag = true;
okflag = read_txa(_txa_fstrm);
@ -102,7 +103,7 @@ read() {
return false;
}
okflag = read_pi(infile);
okflag = read_pi(infile, force_redo_all);
}
}
@ -156,8 +157,80 @@ update_params(EggPalettize *prog) {
}
}
////////////////////////////////////////////////////////////////////
// Function: AttribFile::get_group
// Access: Public
// Description: Returns a pointer to the PaletteGroup object with the
// given name. If there is not yet any such
// PaletteGroup, creates one.
////////////////////////////////////////////////////////////////////
PaletteGroup *AttribFile::
get_group(const string &group_name) {
Groups::iterator gi;
gi = _groups.find(group_name);
if (gi != _groups.end()) {
return (*gi).second;
}
PaletteGroup *new_group = new PaletteGroup(group_name);
_groups[group_name] = new_group;
return new_group;
}
////////////////////////////////////////////////////////////////////
// Function: AttribFile::get_default_group
// Access: Public
// Description: Returns the PaletteGroup that should be associated
// with any textures or egg files not explicitly placed
// in a different group.
////////////////////////////////////////////////////////////////////
PaletteGroup *AttribFile::
get_default_group() {
return get_group(_name);
}
////////////////////////////////////////////////////////////////////
// Function: AttribFile::get_egg_group_requests
// Access: Public
// Description: Checks all of the known egg filenames against the egg
// files named in the .txa file, looking to see which
// egg files as assigned to which groups.
////////////////////////////////////////////////////////////////////
void AttribFile::
get_req_sizes() {
get_egg_group_requests() {
Eggs::iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = (*ei).second;
UserLines::const_iterator ui;
bool matched = false;
for (ui = _user_lines.begin();
ui != _user_lines.end() && !matched;
++ui) {
matched = (*ui)->get_group_request(egg);
}
if (matched) {
egg->set_matched_anything(true);
}
}
// Now go back and make sure that all textures are assigned to
// *something*.
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = (*ei).second;
egg->all_textures_assigned();
}
}
////////////////////////////////////////////////////////////////////
// Function: AttribFile::get_size_requests
// Access: Public
// Description: Determines the size/scaling requested for each
// texture by scanning the .txa file.
////////////////////////////////////////////////////////////////////
void AttribFile::
get_size_requests() {
PTextures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *tex = (*ti).second;
@ -170,7 +243,7 @@ get_req_sizes() {
for (ui = _user_lines.begin();
ui != _user_lines.end() && !matched;
++ui) {
matched = (*ui)->match_texture(tex, margin);
matched = (*ui)->get_size_request(tex, margin);
}
if (matched) {
@ -185,11 +258,11 @@ get_req_sizes() {
void AttribFile::
update_texture_flags() {
// First, clear all the flags.
PTextures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *tex = (*ti).second;
tex->set_unused(true);
tex->set_uses_alpha(false);
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
TexturePacking *packing = (*pi);
packing->set_unused(true);
packing->set_uses_alpha(false);
}
// Then, for each egg file, mark all the textures it's known to be
@ -203,12 +276,12 @@ update_texture_flags() {
// Now go back through and omit any unused textures. This is also a
// fine time to mark the textures' original packing state, so we can
// check later to see if they've been repacked elsewhere.
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *tex = (*ti).second;
tex->record_orig_state();
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
TexturePacking *packing = (*pi);
packing->record_orig_state();
if (tex->unused()) {
tex->set_omit(PTexture::OR_unused);
if (packing->unused()) {
packing->set_omit(OR_unused);
}
}
}
@ -218,31 +291,25 @@ update_texture_flags() {
// algorithm can manage.
void AttribFile::
repack_all_textures() {
// First, delete all the existing palettes.
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
// Remove the old palette file?
if (_aggressively_clean_mapdir &&
!(*pi)->get_filename().empty()) {
if (access((*pi)->get_filename().c_str(), F_OK) == 0) {
nout << "Deleting " << (*pi)->get_filename() << "\n";
unlink((*pi)->get_filename().c_str());
}
// First, empty all the existing palette groups.
Groups::iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
PaletteGroup *group = (*gi).second;
if (_aggressively_clean_mapdir) {
group->remove_palette_files();
}
delete (*pi);
group->clear_palettes();
}
_palettes.clear();
// Reorder the textures in descending order by height and width for
// optimal packing.
vector<PTexture *> textures;
vector<TexturePacking *> textures;
get_eligible_textures(textures);
// Now pack all the textures. This will create new palettes.
vector<PTexture *>::iterator ti;
vector<TexturePacking *>::iterator ti;
for (ti = textures.begin(); ti != textures.end(); ++ti) {
pack_texture(*ti);
(*ti)->pack();
}
_optimal = true;
@ -254,22 +321,20 @@ repack_all_textures() {
// palette.
void AttribFile::
repack_some_textures() {
bool empty_before = _palettes.empty();
bool empty_before = _groups.empty();
bool any_added = false;
// Reorder the textures in descending order by height and width for
// optimal packing.
vector<PTexture *> textures;
vector<TexturePacking *> textures;
get_eligible_textures(textures);
// Now pack whatever textures are currently unpacked.
vector<PTexture *>::iterator ti;
vector<TexturePacking *>::iterator ti;
for (ti = textures.begin(); ti != textures.end(); ++ti) {
PTexture *tex = (*ti);
if (!tex->is_packed()) {
if (pack_texture(tex)) {
any_added = true;
}
TexturePacking *packing = (*ti);
if (packing->pack()) {
any_added = true;
}
}
@ -278,17 +343,17 @@ repack_some_textures() {
void AttribFile::
optimal_resize() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
(*pi)->optimal_resize();
Groups::iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
(*gi).second->optimal_resize();
}
}
void AttribFile::
finalize_palettes() {
Palettes::iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
(*pi)->finalize_palette();
Groups::iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
(*gi).second->finalize_palettes();
}
}
@ -310,112 +375,41 @@ remove_unused_lines() {
_user_lines.erase(write, _user_lines.end());
}
// Checks that each texture that wants packing has been packed, that
// no textures that don't need packing have been packed, and that all
// textures are packed at their correct sizes. Returns true if no
// changes need to be made, false otherwise.
////////////////////////////////////////////////////////////////////
// Function: AttribFile::prepare_repack
// Access: Public
// Description: Checks if any 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 force_optimal is true, returns true if anything
// has changed at all that would result in a suboptimal
// palette.
////////////////////////////////////////////////////////////////////
bool AttribFile::
check_packing(bool force_optimal) {
bool all_ok = true;
prepare_repack(bool force_optimal) {
bool needs_repack = false;
PTextures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
if (texture->get_omit() == PTexture::OR_none) {
// Here's a texture that thinks it wants to be packed. Does it?
int xsize, 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";
texture->set_omit(PTexture::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.
texture->set_omit(PTexture::OR_size);
} else {
// Ok, this texture really does want to be packed. Is it?
int px, py, m;
if (texture->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_texture(texture);
_optimal = false;
all_ok = false;
}
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.
all_ok = false;
}
}
}
if (texture->get_omit() != PTexture::OR_none) {
// Here's a texture that doesn't want to be packed. Is it?
if (unpack_texture(texture)) {
// It was! Not any more.
_optimal = false;
all_ok = false;
}
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
TexturePacking *packing = (*pi);
if (packing->prepare_repack(_optimal)) {
needs_repack = true;
}
}
if (force_optimal && !_optimal) {
// If the user wants to insist on an optimal packing, we'll have
// to give it to him.
all_ok = false;
needs_repack = true;
}
return all_ok;
return needs_repack;
}
bool AttribFile::
pack_texture(PTexture *texture) {
// 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(texture)) {
return true;
}
}
// It didn't place anywhere; create a new palette for it.
Palette *palette =
new Palette(_palettes.size() + 1, _pal_xsize, _pal_ysize, 0, this);
if (!palette->pack_texture(texture)) {
// Hmm, it didn't fit on an empty palette. Must be too big.
texture->set_omit(PTexture::OR_size);
delete palette;
return false;
}
_palettes.push_back(palette);
return true;
}
bool AttribFile::
unpack_texture(PTexture *texture) {
if (texture->is_packed()) {
bool unpacked = texture->get_palette()->unpack_texture(texture);
assert(unpacked);
return true;
}
// It wasn't packed.
return false;
}
// Updates the timestamp on each egg file that will need to be
// rebuilt, so that a future make process will pick it up. This is
// only necessary to update egg files that may not have been included
@ -453,22 +447,23 @@ get_texture(const string &name) {
}
void AttribFile::
get_eligible_textures(vector<PTexture *> &textures) {
get_eligible_textures(vector<TexturePacking *> &textures) {
// First, copy the texture pointers into this map structure to sort
// them in descending order by size. This is a 2-d map such that
// each map[ysize][xsize] is a set of texture pointers.
typedef map<int, map<int, set<PTexture *> > > TexBySize;
typedef map<int, map<int, set<TexturePacking *> > > TexBySize;
TexBySize tex_by_size;
int num_textures = 0;
PTextures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
TexturePacking *packing = (*pi);
PTexture *texture = packing->get_texture();
if (texture->get_omit() == PTexture::OR_none) {
if (packing->get_omit() == OR_none) {
int xsize, ysize;
if (texture->get_req(xsize, ysize)) {
tex_by_size[-ysize][-xsize].insert(texture);
tex_by_size[-ysize][-xsize].insert(packing);
num_textures++;
}
}
@ -481,9 +476,9 @@ get_eligible_textures(vector<PTexture *> &textures) {
TexBySize::const_iterator t1;
for (t1 = tex_by_size.begin(); t1 != tex_by_size.end(); ++t1) {
map<int, set<PTexture *> >::const_iterator t2;
map<int, set<TexturePacking *> >::const_iterator t2;
for (t2 = (*t1).second.begin(); t2 != (*t1).second.end(); ++t2) {
set<PTexture *>::const_iterator t3;
set<TexturePacking *>::const_iterator t3;
for (t3 = (*t2).second.begin(); t3 != (*t2).second.end(); ++t3) {
textures.push_back(*t3);
}
@ -499,7 +494,7 @@ get_egg(Filename name) {
return (*ei).second;
}
SourceEgg *egg = new SourceEgg();
SourceEgg *egg = new SourceEgg(this);
egg->resolve_egg_filename(name);
egg->set_egg_filename(name);
_eggs[name] = egg;
@ -510,18 +505,10 @@ bool AttribFile::
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;
}
Groups::iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
PaletteGroup *group = (*gi).second;
okflag = group->generate_palette_images() && okflag;
}
return okflag;
@ -531,23 +518,24 @@ bool AttribFile::
transfer_unplaced_images(bool force_redo_all) {
bool okflag = true;
PTextures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
TexturePacking *packing = (*pi);
PTexture *texture = packing->get_texture();
if (texture->get_omit() != PTexture::OR_none &&
texture->get_omit() != PTexture::OR_unused) {
if (packing->get_omit() != OR_none &&
packing->get_omit() != OR_unused) {
// Here's a texture that needs to be moved to our mapdir. But
// maybe it's already there and hasn't changed recently.
if (force_redo_all || texture->needs_refresh()) {
if (force_redo_all || packing->needs_refresh()) {
// Nope, needs to be updated.
okflag = texture->transfer() && okflag;
}
} else {
if (_aggressively_clean_mapdir) {
if (access(texture->get_filename().c_str(), F_OK) == 0) {
if (_aggressively_clean_mapdir && texture->is_unused()) {
if (texture->get_filename().exists()) {
nout << "Deleting " << texture->get_filename() << "\n";
unlink(texture->get_filename().c_str());
texture->get_filename().unlink();
}
}
}
@ -560,6 +548,7 @@ transfer_unplaced_images(bool force_redo_all) {
void AttribFile::
check_dup_textures(map<string, PTexture *> &textures,
map<string, int> &dup_textures) const {
/*
PTextures::const_iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
@ -623,14 +612,16 @@ check_dup_textures(map<string, PTexture *> &textures,
}
}
}
*/
}
void AttribFile::
collect_statistics(int &num_textures, int &num_placed, int &num_palettes,
int &orig_size, int &resized_size,
int &palette_size, int &unplaced_size) const {
/*
num_textures = _textures.size();
num_palettes = _palettes.size();
num_palettes = 0; //_palettes.size();
num_placed = 0;
orig_size = 0;
resized_size = 0;
@ -641,10 +632,10 @@ collect_statistics(int &num_textures, int &num_placed, int &num_palettes,
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
int xsize, ysize;
int xsize, ysize, zsize;
int rxsize, rysize;
int rsize = 0;
if (texture->get_size(xsize, ysize) &&
if (texture->get_size(xsize, ysize, zsize) &&
texture->get_last_req(rxsize, rysize)) {
orig_size += xsize * ysize;
resized_size += rxsize * rysize;
@ -662,11 +653,12 @@ collect_statistics(int &num_textures, int &num_placed, int &num_palettes,
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
Palette *palette = (*pi);
if (palette->get_num_textures() > 1) {
int xsize, ysize;
palette->get_size(xsize, ysize);
int xsize, ysize, zsize;
palette->get_size(xsize, ysize, zsize);
palette_size += xsize * ysize;
}
}
}
*/
}
@ -695,7 +687,7 @@ read_txa(istream &infile) {
}
bool AttribFile::
read_pi(istream &infile) {
read_pi(istream &infile, bool force_redo_all) {
string line;
getline(infile, line);
@ -734,7 +726,7 @@ read_pi(istream &infile) {
okflag = parse_pathname(words, infile, line, line_num);
} else if (words[0] == "egg") {
okflag = parse_egg(words, infile, line, line_num);
okflag = parse_egg(words, infile, line, line_num, force_redo_all);
} else if (words[0] == "palette") {
okflag = parse_palette(words, infile, line, line_num);
@ -776,6 +768,8 @@ write_txa(ostream &out) const {
bool AttribFile::
write_pi(ostream &out) const {
bool any_surprises = false;
out <<
"# This file was generated by egg-palettize. Edit it at your own peril.\n";
@ -802,19 +796,21 @@ write_pi(ostream &out) const {
Eggs::const_iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = (*ei).second;
out << "\n";
(*ei).second->write_pi(out);
egg->write_pi(out);
any_surprises = any_surprises || !egg->matched_anything();
}
Palettes::const_iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
out << "\n";
(*pi)->write(out);
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
(*gi).second->write(out);
}
out << "\n";
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
(*ti).second->write_unplaced(out);
Packing::const_iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
(*pi)->write_unplaced(out);
}
// Sort textures in descending order by scale percent.
@ -826,8 +822,6 @@ write_pi(ostream &out) const {
texture));
}
bool any_surprises = false;
out << "\ntextures\n";
SortPTextures::const_iterator sti;
for (sti = sort_textures.begin(); sti != sort_textures.end(); ++sti) {
@ -837,8 +831,8 @@ write_pi(ostream &out) const {
}
if (any_surprises) {
// Some textures didn't match any commands; they're "surprise"
// textures.
// Some textures or egg files didn't match any commands; they're
// "surprises".
out << "\nsurprises\n";
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
PTexture *texture = (*ti).second;
@ -846,6 +840,13 @@ write_pi(ostream &out) const {
out << " " << texture->get_name() << "\n";
}
}
Eggs::const_iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = (*ei).second;
if (!egg->matched_anything()) {
out << " " << egg->get_egg_filename().get_basename() << "\n";
}
}
}
if (!out) {
@ -873,14 +874,6 @@ parse_params(const vector<string> &words, istream &infile,
if (param == "map_directory") {
_map_dirname = value;
// These are all deprecated.
} else if (param == "converted_directory") {
} else if (param == "convert_extension") {
} else if (param == "convert_command") {
} else if (param == "palette_prefix") {
} else if (param == "map_prefix") {
} else if (param == "pal_xsize") {
_pal_xsize = atoi(value.c_str());
} else if (param == "pal_ysize") {
@ -944,8 +937,9 @@ parse_texture(const vector<string> &words, istream &infile,
while (kw < (int)twords.size()) {
if (kw + 3 <= (int)twords.size() && twords[kw] == "orig") {
texture->set_size(atoi(twords[kw + 1].c_str()),
atoi(twords[kw + 2].c_str()));
kw += 3;
atoi(twords[kw + 2].c_str()),
atoi(twords[kw + 3].c_str()));
kw += 4;
} else if (kw + 3 <= (int)twords.size() && twords[kw] == "new") {
texture->set_last_req(atoi(twords[kw + 1].c_str()),
@ -1014,7 +1008,7 @@ parse_pathname(const vector<string> &words, istream &infile,
bool AttribFile::
parse_egg(const vector<string> &words, istream &infile,
string &line, int &line_num) {
string &line, int &line_num, bool force_redo_all) {
if (words.size() != 2) {
nout << "Egg filename expected.\n";
return false;
@ -1035,10 +1029,20 @@ parse_egg(const vector<string> &words, istream &infile,
string name = twords[0];
bool repeats = false;
bool alpha = false;
PaletteGroup *group = (PaletteGroup *)NULL;
int kw = 1;
while (kw < (int)twords.size()) {
if (twords[kw] == "repeats") {
if (twords[kw] == "in") {
kw++;
if (kw >= (int)twords.size()) {
nout << "Expected group name\n";
return false;
}
group = get_group(twords[kw]);
kw++;
} else if (twords[kw] == "repeats") {
repeats = true;
kw++;
@ -1052,7 +1056,14 @@ parse_egg(const vector<string> &words, istream &infile,
}
}
egg->add_texture(get_texture(name), repeats, alpha);
PTexture *texture = get_texture(name);
TexturePacking *packing = (TexturePacking *)NULL;
if (!force_redo_all) {
packing = texture->add_to_group(group);
}
egg->add_texture(texture, packing, repeats, alpha);
getline(infile, line);
line = trim_right(line);
@ -1063,26 +1074,32 @@ parse_egg(const vector<string> &words, istream &infile,
}
bool AttribFile::
parse_palette(const vector<string> &words, istream &infile,
string &line, int &line_num) {
if (words.size() != 6) {
nout << "Palette filename, size, and number of components expected.\n";
if (words.size() != 8) {
nout << "Palette filename, group, size, and number of components expected.\n";
return false;
}
string filename = words[1];
if (!(words[2] == "size")) {
if (!(words[2] == "in")) {
nout << "Expected keyword 'in'\n";
return false;
}
PaletteGroup *group = get_group(words[3]);
if (!(words[4] == "size")) {
nout << "Expected keyword 'size'\n";
return false;
}
int xsize = atoi(words[3].c_str());
int ysize = atoi(words[4].c_str());
int components = atoi(words[5].c_str());
int xsize = atoi(words[5].c_str());
int ysize = atoi(words[6].c_str());
int components = atoi(words[7].c_str());
Palette *palette = new Palette(filename, xsize, ysize, components, this);
_palettes.push_back(palette);
Palette *palette =
new Palette(filename, group, xsize, ysize, components, this);
group->add_palette(palette);
getline(infile, line);
line = trim_right(line);
@ -1095,6 +1112,7 @@ parse_palette(const vector<string> &words, istream &infile,
}
PTexture *texture = get_texture(twords[0]);
TexturePacking *packing = texture->add_to_group(group);
if (!(twords[1] == "at")) {
nout << "Expected keyword 'at'\n";
@ -1115,8 +1133,8 @@ parse_palette(const vector<string> &words, istream &infile,
return false;
}
int margin = atoi(twords[8].c_str());
palette->place_texture_at(texture, left, top, xsize, ysize, margin);
palette->place_texture_at(packing, left, top, xsize, ysize, margin);
getline(infile, line);
line = trim_right(line);
@ -1131,34 +1149,40 @@ parse_palette(const vector<string> &words, istream &infile,
bool AttribFile::
parse_unplaced(const vector<string> &words, istream &infile,
string &line, int &line_num) {
if (words.size() != 4) {
if (words.size() != 6) {
nout << "Unplaced texture description expected.\n";
return false;
}
PTexture *texture = get_texture(words[1]);
if (!(words[2] == "because")) {
if (!(words[2] == "in")) {
nout << "Expected keyword 'in'\n";
return false;
}
PaletteGroup *group = get_group(words[3]);
TexturePacking *packing = texture->add_to_group(group);
if (!(words[4] == "because")) {
nout << "Expected keyword 'because'\n";
return false;
}
if (words[3] == "size") {
texture->set_omit(PTexture::OR_size);
} else if (words[3] == "repeats") {
texture->set_omit(PTexture::OR_repeats);
} else if (words[3] == "omitted") {
texture->set_omit(PTexture::OR_omitted);
} else if (words[3] == "unused") {
texture->set_omit(PTexture::OR_unused);
} else if (words[3] == "unknown") {
texture->set_omit(PTexture::OR_unknown);
} else if (words[3] == "solitary") {
texture->set_omit(PTexture::OR_solitary);
} else if (words[3] == "cmdline") {
texture->set_omit(PTexture::OR_cmdline);
if (words[5] == "size") {
packing->set_omit(OR_size);
} else if (words[5] == "repeats") {
packing->set_omit(OR_repeats);
} else if (words[5] == "omitted") {
packing->set_omit(OR_omitted);
} else if (words[5] == "unused") {
packing->set_omit(OR_unused);
} else if (words[5] == "unknown") {
packing->set_omit(OR_unknown);
} else if (words[5] == "solitary") {
packing->set_omit(OR_solitary);
} else {
nout << "Unknown keyword " << words[3] << "\n";
nout << "Unknown keyword " << words[5] << "\n";
return false;
}

View File

@ -15,6 +15,8 @@
class UserAttribLine;
class PTexture;
class TexturePacking;
class PaletteGroup;
class Palette;
class SourceEgg;
class EggPalettize;
@ -32,12 +34,16 @@ public:
bool grab_lock();
bool release_lock();
bool read();
bool read(bool force_redo_all);
bool write();
void update_params(EggPalettize *prog);
void get_req_sizes();
PaletteGroup *get_group(const string &group_name);
PaletteGroup *get_default_group();
void get_egg_group_requests();
void get_size_requests();
void update_texture_flags();
void repack_all_textures();
@ -45,15 +51,13 @@ public:
void optimal_resize();
void finalize_palettes();
void remove_unused_lines();
bool check_packing(bool force_optimal);
bool pack_texture(PTexture *texture);
bool unpack_texture(PTexture *texture);
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<PTexture *> &textures);
void get_eligible_textures(vector<TexturePacking *> &textures);
SourceEgg *get_egg(Filename name);
bool generate_palette_images();
@ -74,16 +78,17 @@ private:
typedef map<string, SourceEgg *> Eggs;
Eggs _eggs;
typedef vector<Palette *> Palettes;
Palettes _palettes;
typedef map<string, PaletteGroup *> Groups;
Groups _groups;
typedef map<string, PTexture *> PTextures;
PTextures _textures;
string get_pi_filename(const string &txa_filename) const;
typedef vector<TexturePacking *> Packing;
Packing _packing;
bool read_txa(istream &outfile);
bool read_pi(istream &outfile);
bool read_pi(istream &outfile, bool force_redo_all);
bool write_txa(ostream &outfile) const;
bool write_pi(ostream &outfile) const;
@ -96,7 +101,7 @@ private:
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);
string &line, int &line_num, bool force_redo_all);
bool parse_palette(const vector<string> &words, istream &infile,
string &line, int &line_num);
bool parse_unplaced(const vector<string> &words, istream &infile,
@ -111,7 +116,6 @@ private:
Filename _txa_filename;
Filename _pi_filename;
public:
// These parameter values come from the command line, or from the
// .pi file if omitted from the command line. These are the
@ -119,7 +123,6 @@ public:
// palettes, and thus should be stored in the .pi file for future
// reference.
Filename _map_dirname;
string _palette_prefix;
int _pal_xsize, _pal_ysize;
int _default_margin;
bool _force_power_2;
@ -127,6 +130,8 @@ public:
int _txa_fd;
fstream _txa_fstrm;
friend class PTexture;
};
#endif

View File

@ -8,6 +8,7 @@
#include "pTexture.h"
#include "string_utils.h"
#include "sourceEgg.h"
#include "textureOmitReason.h"
#include <pnmImage.h>
#include <stdio.h>
@ -40,6 +41,11 @@ EggPalettize() : EggMultiFilter(true) {
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 "
@ -124,10 +130,6 @@ EggPalettize() : EggMultiFilter(true) {
"which is an even power of 2. They will be scaled down to "
"achieve this.",
&EggPalettize::dispatch_none, &_got_force_power_2);
add_option
("x", "", 0,
"Don't palettize anything, but do resize textures.",
&EggPalettize::dispatch_none, &_dont_palettize);
add_option
("k", "", 0,
"Kill lines from the attributes file that aren't used on any "
@ -227,14 +229,21 @@ 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 *egg = _attrib_files[0]->get_egg(filename);
if (egg->read(filename)) {
return egg;
SourceEgg *data = _attrib_files[0]->get_egg(filename);
if (!data->read(filename)) {
// Failure reading.
delete data;
return (EggData *)NULL;
}
// Failure reading.
delete egg;
return (EggData *)NULL;
if (_force_complete) {
if (!data->resolve_externals()) {
delete data;
return (EggData *)NULL;
}
}
return data;
}
////////////////////////////////////////////////////////////////////
@ -267,20 +276,48 @@ describe_input_file() {
"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"
" :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"
" :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";
}
@ -314,6 +351,7 @@ format_space(int size_pixels, bool verbose) {
////////////////////////////////////////////////////////////////////
void EggPalettize::
report_statistics() {
/*
// Look for textures in common.
map<string, PTexture *> textures;
map<string, int> dup_textures;
@ -371,17 +409,17 @@ report_statistics() {
int net_orig_size = 0;
int net_resized_size = 0;
int net_unplaced_size = 0;
typedef map<PTexture::OmitReason, pair<int, int> > UnplacedReasons;
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;
int xsize, ysize, zsize;
int rxsize, rysize;
int rsize = 0;
if (texture->get_size(xsize, ysize) &&
if (texture->get_size(xsize, ysize, zsize) &&
texture->get_last_req(rxsize, rysize)) {
net_orig_size += xsize * ysize;
net_resized_size += rxsize * rysize;
@ -427,7 +465,7 @@ report_statistics() {
for (uri = unplaced_reasons.begin();
uri != unplaced_reasons.end();
++uri) {
PTexture::OmitReason reason = (*uri).first;
TextureOmitReason reason = (*uri).first;
int count = (*uri).second.first;
int size = (*uri).second.second;
cout << count << " textures (" << format_space(size)
@ -471,6 +509,7 @@ report_statistics() {
}
cout << "\n";
*/
}
////////////////////////////////////////////////////////////////////
@ -503,12 +542,12 @@ run() {
if (_statistics_only) {
nout << "Reading " << af.get_name() << "\n";
okflag = af.read();
okflag = af.read(false);
} else {
nout << "Processing " << af.get_name() << "\n";
if (!af.read()) {
if (!af.read(_force_redo_all)) {
// Failing to read the attribute file is a fatal error.
exit(1);
}
@ -519,15 +558,17 @@ run() {
Eggs::iterator ei;
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
SourceEgg *egg = DCAST(SourceEgg, *ei);
egg->get_textures(af, this);
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_req_sizes();
af.get_size_requests();
af.update_texture_flags();
if (!af.check_packing(_force_optimal) || _force_redo_all) {
// We need some more palettization work here.
if (af.prepare_repack(_force_optimal) || _force_redo_all) {
if (_force_redo_all || _force_optimal) {
af.repack_all_textures();
} else {

View File

@ -36,7 +36,7 @@ public:
AttribFiles _attrib_files;
// The following parameter values specifically relate to textures
// and palettes. These values are store in the .pi file for future
// and palettes. These values are stored in the .pi file for future
// reference.
Filename _map_dirname;
bool _got_map_dirname;
@ -62,7 +62,6 @@ public:
bool _optimal_resize;
bool _touch_eggs;
bool _eggs_include_images;
bool _dont_palettize;
bool _remove_unused_lines;
bool _describe_input_file;

View File

@ -4,32 +4,46 @@
////////////////////////////////////////////////////////////////////
#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 *af, const Filename &name) :
PTexture(AttribFile *attrib_file, const Filename &name) :
_name(name),
_attrib_file(af)
_attrib_file(attrib_file)
{
_got_filename = false;
_file_exists = false;
_texture_changed = false;
_unused = true;
_matched_anything = false;
_got_size = false;
_got_req = false;
_got_last_req = false;
_margin = _attrib_file->_default_margin;
_omit = false;
_read_header = false;
_omit = OR_none;
_uses_alpha = false;
_is_packed = false;
_palette = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PTexture::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PTexture::
~PTexture() {
Packing::iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
delete (*pi).second;
}
}
Filename PTexture::
@ -68,15 +82,16 @@ add_filename(const Filename &filename) {
read_header();
}
int oxsize, oysize;
int oxsize, oysize, ozsize;
bool got_other_size =
read_image_header(filename, oxsize, oysize);
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;
@ -106,7 +121,7 @@ get_basename() const {
}
bool PTexture::
get_size(int &xsize, int &ysize) {
get_size(int &xsize, int &ysize, int &zsize) {
if (!_got_size) {
read_header();
}
@ -117,16 +132,18 @@ get_size(int &xsize, int &ysize) {
xsize = _xsize;
ysize = _ysize;
zsize = _zsize;
return true;
}
void PTexture::
set_size(int xsize, int ysize) {
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;
}
}
@ -134,7 +151,8 @@ set_size(int xsize, int ysize) {
bool PTexture::
get_req(int &xsize, int &ysize) {
if (!_got_req) {
return get_size(xsize, ysize);
int zsize;
return get_size(xsize, ysize, zsize);
}
xsize = _req_xsize;
ysize = _req_ysize;
@ -144,7 +162,8 @@ get_req(int &xsize, int &ysize) {
bool PTexture::
get_last_req(int &xsize, int &ysize) {
if (!_got_last_req) {
return get_size(xsize, ysize);
int zsize;
return get_size(xsize, ysize, zsize);
}
xsize = _last_req_xsize;
ysize = _last_req_ysize;
@ -187,12 +206,6 @@ void PTexture::
clear_req() {
_got_req = false;
_margin = _attrib_file->_default_margin;
if (_omit != OR_cmdline) {
// If we previously omitted this texture from the command line,
// preserve that property.
_omit = OR_none;
}
}
double PTexture::
@ -219,179 +232,127 @@ set_margin(int margin) {
_margin = margin;
}
PTexture::OmitReason PTexture::
get_omit() const {
return _omit;
}
////////////////////////////////////////////////////////////////////
// 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::
set_omit(OmitReason omit) {
_omit = omit;
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);
}
}
bool PTexture::
needs_refresh() {
if (!_texture_changed) {
// We consider the texture to be out-of-date if it's moved around
// in the palette.
_texture_changed = packing_changed();
////////////////////////////////////////////////////////////////////
// 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;
}
if (!_texture_changed && _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.
_texture_changed = true;
}
} else {
// Compare to the resized file.
target_filename = get_filename();
}
if (!_texture_changed) {
_texture_changed =
(target_filename.compare_timestamps(_filename, true, false) < 0);
}
}
return _texture_changed;
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;
}
bool PTexture::
unused() const {
return _unused;
}
void PTexture::
set_unused(bool unused) {
_unused = unused;
}
////////////////////////////////////////////////////////////////////
// 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::
uses_alpha() const {
return _uses_alpha;
}
void PTexture::
set_uses_alpha(bool uses_alpha) {
_uses_alpha = uses_alpha;
}
void PTexture::
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;
}
void PTexture::
mark_unpacked() {
_is_packed = false;
}
bool PTexture::
is_packed() const {
return _is_packed;
}
// 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 PTexture::
is_really_packed() const {
return _is_packed && _omit != OR_solitary;
}
Palette *PTexture::
get_palette() const {
return _is_packed ? _palette : (Palette *)NULL;
}
bool PTexture::
get_packed_location(int &left, int &top) const {
left = _pleft;
top = _ptop;
return _is_packed;
}
bool PTexture::
get_packed_size(int &xsize, int &ysize, int &margin) const {
xsize = _pxsize;
ysize = _pysize;
margin = _pmargin;
return _is_packed;
}
void PTexture::
record_orig_state() {
// 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.
_orig_is_packed = _is_packed;
if (_is_packed) {
_orig_palette_name = _palette->get_filename();
_opleft = _pleft;
_optop = _ptop;
_opxsize = _pxsize;
_opysize = _pysize;
_opmargin = _pmargin;
is_unused() const {
Packing::const_iterator pi;
for (pi = _packing.begin(); pi != _packing.end(); ++pi) {
if (!(*pi).second->unused()) {
return false;
}
}
return true;
}
bool PTexture::
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;
}
void PTexture::
write_size(ostream &out) {
if (_omit != OR_unused) {
if (!is_unused()) {
if (!_got_size) {
read_header();
}
out << " " << _name;
if (_got_size) {
out << " orig " << _xsize << " " << _ysize;
out << " orig " << _xsize << " " << _ysize << " " << _zsize;
}
if (_got_req) {
out << " new " << _req_xsize << " " << _req_ysize;
@ -402,7 +363,7 @@ write_size(ostream &out) {
void PTexture::
write_pathname(ostream &out) const {
if (_got_filename && _omit != OR_unused) {
if (_got_filename && !is_unused()) {
if (!_file_exists) {
nout << "Warning: texture " << _filename << " does not exist.\n";
}
@ -423,40 +384,6 @@ write_pathname(ostream &out) const {
}
}
void PTexture::
write_unplaced(ostream &out) const {
if (_omit != OR_none && _omit != OR_unused) {
out << "unplaced " << 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;
case OR_cmdline:
out << "cmdline";
break;
default:
nout << "Invalid type: " << (int)_omit << "\n";
abort();
}
out << "\n";
}
}
bool PTexture::
transfer() {
bool okflag = true;
@ -557,14 +484,15 @@ read_header() {
if (_got_filename && _file_exists) {
if (!_read_header) {
_read_header = true;
_got_size = read_image_header(_filename, _xsize, _ysize);
_got_size = read_image_header(_filename, _xsize, _ysize, _zsize);
}
_read_header = true;
}
}
bool PTexture::
read_image_header(const Filename &filename, int &xsize, int &ysize) {
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";
@ -573,6 +501,7 @@ read_image_header(const Filename &filename, int &xsize, int &ysize) {
xsize = header.get_x_size();
ysize = header.get_y_size();
zsize = header.get_num_channels();
return true;
}

View File

@ -11,24 +11,26 @@
#include "imageFile.h"
#include <set>
#include <map>
class Palette;
class PNMImage;
class AttribFile;
class PaletteGroup;
class TexturePacking;
class TextureEggRef;
////////////////////////////////////////////////////////////////////
// Class : PTexture
// Description :
// 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 ImageFile {
public:
enum OmitReason {
OR_none,
OR_size, OR_repeats, OR_omitted, OR_unused, OR_unknown,
OR_cmdline, OR_solitary
};
PTexture(AttribFile *af, const Filename &name);
PTexture(AttribFile *attrib_file, const Filename &name);
~PTexture();
Filename get_name() const;
@ -37,8 +39,8 @@ public:
virtual Filename get_filename() const;
virtual Filename get_basename() const;
bool get_size(int &xsize, int &ysize);
void set_size(int xsize, int ysize);
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);
@ -51,44 +53,32 @@ public:
int get_margin() const;
void set_margin(int margin);
OmitReason get_omit() const;
void set_omit(OmitReason omit);
void user_omit();
TexturePacking *add_to_group(PaletteGroup *group);
TexturePacking *check_group(PaletteGroup *group) const;
bool needs_refresh();
void set_changed(bool changed);
bool unused() const;
void set_unused(bool unused);
bool matched_anything() const;
void set_matched_anything(bool matched_anything);
bool uses_alpha() const;
void set_uses_alpha(bool uses_alpha);
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;
bool is_unused() const;
void write_size(ostream &out);
void write_pathname(ostream &out) const;
void write_unplaced(ostream &out) const;
bool transfer();
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);
bool read_image_header(const Filename &filename,
int &xsize, int &ysize, int &zsize);
static int to_power_2(int value);
Filename _name;
@ -100,31 +90,27 @@ private:
Filename _filename;
bool _file_exists;
bool _texture_changed;
bool _unused;
bool _matched_anything;
bool _uses_alpha;
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;
OmitReason _omit;
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 _omit;
bool _read_header;
AttribFile *_attrib_file;
typedef map<PaletteGroup *, TexturePacking *> Packing;
Packing _packing;
friend class TexturePacking;
};

View File

@ -4,7 +4,9 @@
////////////////////////////////////////////////////////////////////
#include "palette.h"
#include "paletteGroup.h"
#include "pTexture.h"
#include "texturePacking.h"
#include "attribFile.h"
#include "string_utils.h"
@ -135,13 +137,14 @@ add_margins(PNMImage *source) const {
Palette::
Palette(const Filename &filename, int xsize, int ysize, int components,
AttribFile *af) :
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(af)
_attrib_file(attrib_file)
{
_index = -1;
_palette_changed = false;
@ -149,12 +152,14 @@ Palette(const Filename &filename, int xsize, int ysize, int components,
}
Palette::
Palette(int index, int xsize, int ysize, int components, AttribFile *af) :
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(af)
_attrib_file(attrib_file)
{
_palette_changed = false;
_new_palette = true;
@ -165,7 +170,7 @@ Palette::
// Unmark any textures we've had packed.
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
(*ti)._texture->mark_unpacked();
(*ti)._packing->mark_unpacked();
}
}
@ -199,7 +204,7 @@ 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)._texture->uses_alpha()) {
if ((*ti)._packing->uses_alpha()) {
return true;
}
}
@ -215,10 +220,11 @@ get_size(int &xsize, int &ysize) const {
void Palette::
place_texture_at(PTexture *texture, int left, int top,
place_texture_at(TexturePacking *packing, int left, int top,
int xsize, int ysize, int margin) {
nassertv(_group != (PaletteGroup *)NULL);
TexturePlacement tp;
tp._texture = texture;
tp._packing = packing;
tp._left = left;
tp._top = top;
tp._xsize = xsize;
@ -226,11 +232,12 @@ place_texture_at(PTexture *texture, int left, int top,
tp._margin = margin;
_texplace.push_back(tp);
texture->mark_pack_location(this, left, top, xsize, ysize, margin);
packing->mark_pack_location(this, left, top, xsize, ysize, margin);
}
bool Palette::
pack_texture(PTexture *texture) {
pack_texture(TexturePacking *packing) {
PTexture *texture = packing->get_texture();
int xsize, ysize;
if (!texture->get_req(xsize, ysize)) {
return false;
@ -238,7 +245,7 @@ pack_texture(PTexture *texture) {
int left, top;
if (find_home(left, top, xsize, ysize)) {
place_texture_at(texture, left, top, xsize, ysize, texture->get_margin());
place_texture_at(packing, left, top, xsize, ysize, texture->get_margin());
_palette_changed = true;
return true;
}
@ -246,12 +253,12 @@ pack_texture(PTexture *texture) {
}
bool Palette::
unpack_texture(PTexture *texture) {
unpack_texture(TexturePacking *packing) {
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
if ((*ti)._texture == texture) {
if ((*ti)._packing == packing) {
_texplace.erase(ti);
texture->mark_unpacked();
packing->mark_unpacked();
return true;
}
}
@ -308,7 +315,7 @@ optimal_resize() {
// these textures will need to be rebuilt.
TexPlace::iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
(*ti)._texture->set_changed(true);
(*ti)._packing->set_changed(true);
}
// And we'll have to mark the palette as a new image.
@ -323,7 +330,7 @@ finalize_palette() {
// Generate a filename based on the index number.
char index_str[128];
sprintf(index_str, "%03d", _index);
_basename = _attrib_file->_palette_prefix + index_str + ".rgb";
_basename = _group->get_name() + "-palette." + index_str + ".rgb";
_filename = _basename;
_filename.set_dirname(_attrib_file->_map_dirname);
} else {
@ -334,7 +341,7 @@ finalize_palette() {
if (_texplace.size() == 1) {
// If we packed exactly one texture, never mind.
PTexture *texture = (*_texplace.begin())._texture;
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
@ -342,18 +349,18 @@ finalize_palette() {
// 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.
texture->set_omit(PTexture::OR_solitary);
packing->set_omit(OR_solitary);
}
}
void Palette::
write(ostream &out) const {
out << "palette " << _filename
out << "palette " << _filename << " in " << _group->get_name()
<< " size " << _xsize << " " << _ysize << " " << _components
<< "\n";
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
out << " " << (*ti)._texture->get_name()
out << " " << (*ti)._packing->get_texture()->get_name()
<< " at " << (*ti)._left << " " << (*ti)._top
<< " size " << (*ti)._xsize << " " << (*ti)._ysize
<< " margin " << (*ti)._margin
@ -371,7 +378,8 @@ generate_image() {
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
PTexture *texture = (*ti)._texture;
TexturePacking *packing = (*ti)._packing;
PTexture *texture = packing->get_texture();
nout << " " << texture->get_name() << "\n";
okflag = copy_texture_image(palette, *ti) && okflag;
}
@ -415,8 +423,9 @@ refresh_image() {
TexPlace::const_iterator ti;
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
PTexture *texture = (*ti)._texture;
if (texture->needs_refresh()) {
TexturePacking *packing = (*ti)._packing;
PTexture *texture = packing->get_texture();
if (packing->needs_refresh()) {
if (!any_changed) {
nout << "Refreshing " << _filename << "\n";
any_changed = true;
@ -442,7 +451,7 @@ refresh_image() {
Palette *Palette::
try_resize(int new_xsize, int new_ysize) const {
Palette *np =
new Palette(_index, new_xsize, new_ysize,
new Palette(_group, _index, new_xsize, new_ysize,
_components, _attrib_file);
bool okflag = true;
@ -450,7 +459,7 @@ try_resize(int new_xsize, int new_ysize) const {
for (ti = _texplace.begin();
ti != _texplace.end() && okflag;
++ti) {
okflag = np->pack_texture((*ti)._texture);
okflag = np->pack_texture((*ti)._packing);
}
if (okflag) {
@ -518,9 +527,10 @@ find_home(int &left, int &top, int xsize, int ysize) const {
bool Palette::
copy_texture_image(PNMImage &palette, const TexturePlacement &tp) {
bool okflag = true;
PNMImage *image = tp._texture->read_image();
PTexture *texture = tp._packing->get_texture();
PNMImage *image = texture->read_image();
if (image == NULL) {
nout << " *** Unable to read " << tp._texture->get_name() << "\n";
nout << " *** Unable to read " << texture->get_name() << "\n";
okflag = false;
// Create a solid red texture for images we can't read.

View File

@ -14,25 +14,33 @@
#include <vector>
class PTexture;
class PaletteGroup;
class TexturePacking;
class PNMImage;
class AttribFile;
////////////////////////////////////////////////////////////////////
// Class : Palette
// Description :
// 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 ImageFile {
public:
Palette(const Filename &filename, int xsize, int ysize, int components,
AttribFile *af);
Palette(int index, int xsize, int ysize, int components,
AttribFile *af);
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();
virtual Filename get_filename() const;
virtual Filename get_basename() const;
PaletteGroup *get_group() const;
bool changed() const;
bool new_palette() const;
int get_num_textures() const;
@ -41,11 +49,11 @@ public:
void get_size(int &xsize, int &ysize) const;
void place_texture_at(PTexture *texture, int left, int top,
void place_texture_at(TexturePacking *packing, int left, int top,
int xsize, int ysize, int margin);
bool pack_texture(PTexture *texture);
bool unpack_texture(PTexture *texture);
bool pack_texture(TexturePacking *packing);
bool unpack_texture(TexturePacking *packing);
void optimal_resize();
@ -63,7 +71,7 @@ private:
PNMImage *resize_image(PNMImage *source) const;
PNMImage *add_margins(PNMImage *source) const;
PTexture *_texture;
TexturePacking *_packing;
int _left, _top;
int _xsize, _ysize, _margin;
};
@ -79,6 +87,7 @@ private:
Filename _filename;
Filename _basename;
PaletteGroup *_group;
int _index;
int _xsize, _ysize, _components;
bool _palette_changed;

View File

@ -0,0 +1,259 @@
// Filename: paletteGroup.cxx
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#include "paletteGroup.h"
#include "texturePacking.h"
#include "palette.h"
#include "attribFile.h"
////////////////////////////////////////////////////////////////////
// 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::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
// Access: Public
// Description: Writes out a .pi file description of the palette
// group and all of its nested Palette images.
////////////////////////////////////////////////////////////////////
void PaletteGroup::
write(ostream &out) const {
Palettes::const_iterator pi;
for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
out << "\n";
(*pi)->write(out);
}
}

View File

@ -0,0 +1,79 @@
// 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);
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(ostream &out) const;
private:
typedef vector<PaletteGroup *> Parents;
Parents _parents;
typedef vector<Palette *> Palettes;
Palettes _palettes;
};
#endif

View File

@ -5,6 +5,9 @@
#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"
@ -14,30 +17,52 @@
#include <eggNurbsSurface.h>
#include <eggPrimitive.h>
#include <eggTextureCollection.h>
#include <eggAlphaMode.h>
#include <algorithm>
TypeHandle SourceEgg::_type_handle;
SourceEgg::TextureRef::
TextureRef(PTexture *texture, bool repeats, bool alpha) :
_texture(texture),
_repeats(repeats),
_alpha(alpha)
{
_eggtex = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: SourceEgg::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
SourceEgg::
SourceEgg() {
SourceEgg(AttribFile *attrib_file) : _attrib_file(attrib_file) {
_matched_anything = false;
}
SourceEgg::TextureRef &SourceEgg::
add_texture(PTexture *texture, bool repeats, bool alpha) {
_texrefs.push_back(TextureRef(texture, repeats, alpha));
return _texrefs.back();
////////////////////////////////////////////////////////////////////
// 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(AttribFile &af, EggPalettize *prog) {
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;
@ -48,20 +73,9 @@ get_textures(AttribFile &af, EggPalettize *prog) {
EggTexture *eggtex = (*ti);
string name = eggtex->get_basename();
PTexture *texture = af.get_texture(name);
PTexture *texture = _attrib_file->get_texture(name);
texture->add_filename(*eggtex);
if (prog->_dont_palettize) {
// If the user specified -x, it means to omit all textures
// processed in this run, forever.
texture->set_omit(PTexture::OR_cmdline);
} else {
// Or until we next see it without -x.
if (texture->get_omit() == PTexture::OR_cmdline) {
texture->set_omit(PTexture::OR_none);
}
}
bool repeats =
eggtex->get_wrap_mode() == EggTexture::WM_repeat ||
eggtex->get_wrap_u() == EggTexture::WM_repeat ||
@ -72,7 +86,21 @@ get_textures(AttribFile &af, EggPalettize *prog) {
eggtex->get_wrap_u() == EggTexture::WM_unspecified &&
eggtex->get_wrap_v() == EggTexture::WM_unspecified;
bool alpha = true; //eggtex->uses_alpha();
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;
@ -153,48 +181,94 @@ get_textures(AttribFile &af, EggPalettize *prog) {
}
}
TextureRef &texref = add_texture(texture, repeats, alpha);
texref._eggtex = eggtex;
TextureEggRef *texref = add_texture(texture, (TexturePacking *)NULL,
repeats, alpha);
texref->_eggtex = eggtex;
}
}
// Updates each PTexture with the flags stored in the various egg
// files. Also marks textures as used.
////////////////////////////////////////////////////////////////////
// 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) {
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() {
TexRefs::iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
TextureEggRef *texref = (*ti);
if (texref->_packing == (TexturePacking *)NULL) {
texref->_packing =
texref->_texture->add_to_group(_attrib_file->get_default_group());
}
}
}
////////////////////////////////////////////////////////////////////
// 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) {
PTexture *texture = (*ti)._texture;
texture->set_unused(false);
if ((*ti)._alpha) {
texture->set_uses_alpha(true);
TexturePacking *packing = (*ti)->_packing;
packing->set_unused(false);
if ((*ti)->_alpha) {
packing->set_uses_alpha(true);
}
if ((*ti)._repeats) {
texture->set_omit(PTexture::OR_repeats);
if ((*ti)->_repeats) {
packing->set_omit(OR_repeats);
}
}
}
// Updates the egg file to point to the new palettes.
////////////////////////////////////////////////////////////////////
// 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) {
PTexture *texture = (*ti)._texture;
EggTexture *eggtex = (*ti)._eggtex;
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) {
eggtex->set_alpha = eggtex->UsesAlpha() ?
EggTexture::AM_on : EggTexture::AM_off;
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 (!texture->is_packed() ||
texture->get_omit() != PTexture::OR_none) {
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_fullpath(texture->get_filename());
@ -202,7 +276,7 @@ update_trefs() {
} else {
// This texture was palettized, so redirect the tref to point
// within the palette.
Palette *palette = texture->get_palette();
Palette *palette = packing->get_palette();
eggtex->set_fullpath(palette->get_filename());
@ -217,8 +291,8 @@ update_trefs() {
// Build a matrix that will scale the UV's to their new place
// on the palette.
int left, top, xsize, ysize, margin;
texture->get_packed_location(left, top);
texture->get_packed_size(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;
@ -254,9 +328,14 @@ update_trefs() {
}
}
// 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.
////////////////////////////////////////////////////////////////////
// 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 {
@ -265,8 +344,8 @@ needs_rebuild(bool force_redo_all,
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
bool dirty =
eggs_include_images ?
(*ti)._texture->needs_refresh() :
(*ti)._texture->packing_changed();
(*ti)->_packing->needs_refresh() :
(*ti)->_packing->packing_changed();
if (force_redo_all || dirty) {
return true;
}
@ -276,22 +355,57 @@ needs_rebuild(bool force_redo_all,
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 {
out << "egg " << get_egg_filename() << "\n";
TexRefs::const_iterator ti;
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
out << " " << (*ti)._texture->get_name();
if ((*ti)._repeats) {
out << " " << (*ti)->_packing->get_texture()->get_name()
<< " in " << (*ti)->_packing->get_group()->get_name();
if ((*ti)->_repeats) {
out << " repeats";
}
if ((*ti)._alpha) {
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) {

View File

@ -8,24 +8,28 @@
#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:
class TextureRef;
SourceEgg(AttribFile *attrib_file);
SourceEgg();
TextureRef &add_texture(PTexture *texture, bool repeats, bool alpha);
void get_textures(AttribFile &af, EggPalettize *prog);
TextureEggRef *add_texture(PTexture *texture, TexturePacking *packing,
bool repeats, bool alpha);
void get_textures(EggPalettize *prog);
void require_groups(PaletteGroup *preferred, const PaletteGroups &groups);
void all_textures_assigned();
void mark_texture_flags();
void update_trefs();
@ -33,26 +37,22 @@ public:
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;
class TextureRef {
public:
TextureRef(PTexture *texture, bool repeats, bool alpha);
PTexture *_texture;
bool _repeats;
bool _alpha;
EggTexture *_eggtex;
};
typedef set<TextureEggRef *> TexRefs;
TexRefs _texrefs;
private:
void get_uv_range(EggGroupNode *group, EggTexture *tref,
bool &any_uvs, TexCoordd &min_uv, TexCoordd &max_uv);
typedef vector<TextureRef> TexRefs;
TexRefs _texrefs;
AttribFile *_attrib_file;
bool _matched_anything;
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,69 @@
// 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 the leads to the least redundant
// packing.
_packing = _texture->add_to_group(preferred);
}

View File

@ -0,0 +1,43 @@
// 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 associated 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

@ -0,0 +1,26 @@
// 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

@ -0,0 +1,463 @@
// Filename: texturePacking.cxx
// Created by: drose (06Nov00)
//
////////////////////////////////////////////////////////////////////
#include "texturePacking.h"
#include "paletteGroup.h"
#include "attribFile.h"
#include "palette.h"
#include "pTexture.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 = _texture->get_filename();
}
if (!any_change) {
any_change =
(target_filename.compare_timestamps(_texture->_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";
}
}

View File

@ -0,0 +1,82 @@
// 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;
private:
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

@ -6,16 +6,14 @@
#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>
#include <fnmatch.h>
UserAttribLine::TextureName::
TextureName(const string &pattern) : _pattern(pattern) {
}
UserAttribLine::
UserAttribLine(const string &cline, AttribFile *af) : _attrib_file(af) {
@ -91,6 +89,8 @@ was_used() const {
void UserAttribLine::
write(ostream &out) const {
int i;
switch (_line_type) {
case LT_invalid:
out << "*** invalid line ***\n";
@ -107,8 +107,16 @@ write(ostream &out) const {
out << ":palette " << _xsize << " " << _ysize;
break;
case LT_group_relate:
out << ":group " << _names[0] << " with";
for (i = 1; i < (int)_names.size(); i++) {
out << " " << _names[i];
}
out << "\n";
break;
case LT_size:
list_textures(out) << " : " << _xsize << " " << _ysize;
list_patterns(out) << " : " << _xsize << " " << _ysize;
if (_msize > 0) {
out << " " << _msize;
}
@ -118,7 +126,7 @@ write(ostream &out) const {
break;
case LT_scale:
list_textures(out) << " : " << _scale_pct << "%";
list_patterns(out) << " : " << _scale_pct << "%";
if (_msize > 0) {
out << " " << _msize;
}
@ -128,12 +136,17 @@ write(ostream &out) const {
break;
case LT_name:
list_textures(out) << " :";
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();
@ -143,17 +156,36 @@ write(ostream &out) const {
}
bool UserAttribLine::
match_texture(PTexture *texture, int &margin) {
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;
TextureNames::const_iterator tni;
for (tni = _texture_names.begin();
tni != _texture_names.end() && !matched_any;
++tni) {
if (fnmatch((*tni)._pattern.c_str(), texture->get_name().c_str(), 0) == 0) {
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) {
@ -164,6 +196,7 @@ match_texture(PTexture *texture, int &margin) {
case LT_invalid:
case LT_comment:
case LT_palette:
case LT_group_relate:
return false;
case LT_margin:
@ -174,7 +207,7 @@ match_texture(PTexture *texture, int &margin) {
texture->reset_req(_xsize, _ysize);
texture->set_margin(_msize < 0 ? margin : _msize);
if (_omit) {
texture->set_omit(PTexture::OR_omitted);
texture->user_omit();
}
return true;
@ -182,13 +215,13 @@ match_texture(PTexture *texture, int &margin) {
texture->scale_req(_scale_pct);
texture->set_margin(_msize < 0 ? margin : _msize);
if (_omit) {
texture->set_omit(PTexture::OR_omitted);
texture->user_omit();
}
return true;
case LT_name:
if (_omit) {
texture->set_omit(PTexture::OR_omitted);
texture->user_omit();
}
return true;
@ -201,12 +234,73 @@ match_texture(PTexture *texture, int &margin) {
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_textures(ostream &out) const {
if (!_texture_names.empty()) {
out << _texture_names[0]._pattern;
for (int i = 1; i < (int)_texture_names.size(); i++) {
out << " " << _texture_names[i]._pattern;
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;
@ -229,7 +323,7 @@ keyword_line(const string &line) {
} else if (words[0] == ":palette") {
_line_type = LT_palette;
if (words.size() != 3) {
nout << "Expected xsize ysize of palette.\n";
nout << "Expected :palette xsize ysize.\n";
return false;
}
_xsize = atoi(words[1].c_str());
@ -237,6 +331,18 @@ keyword_line(const string &line) {
_attrib_file->_pal_xsize = _xsize;
_attrib_file->_pal_ysize = _ysize;
} else if (words[0] == ":group") {
_line_type = LT_group_relate;
if (words.size() < 4 || !(words[2] == "with")) {
nout << "Expected :group groupname with groupname [groupname ...].\n";
return false;
}
PaletteGroup *group = _attrib_file->get_group(words[1]);
for (int i = 3; i < (int)words.size(); i++) {
group->add_parent(_attrib_file->get_group(words[i]));
}
} else {
nout << "Unknown keyword: " << words[0] << "\n";
return false;
@ -262,7 +368,7 @@ texture_line(const string &line) {
vector<string>::const_iterator ni;
for (ni = names.begin(); ni != names.end(); ++ni) {
_texture_names.push_back(TextureName(*ni));
_patterns.push_back(GlobPattern(*ni));
}
if (!params.empty() && params[params.size() - 1] == "omit") {
@ -277,6 +383,14 @@ texture_line(const string &line) {
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;
@ -335,7 +449,7 @@ old_style_line(const string &line) {
_is_old_style = true;
_line_type = LT_size;
_texture_names.push_back(TextureName(words[0]));
_patterns.push_back(GlobPattern(words[0]));
_xsize = atoi(words[1].c_str());
_ysize = atoi(words[2].c_str());
if (words.size() > 3) {

View File

@ -8,16 +8,18 @@
#include <pandatoolbase.h>
#include <globPattern.h>
#include <vector>
class AttribFile;
class PTexture;
class SourceEgg;
////////////////////////////////////////////////////////////////////
// Class : UserAttribLine
// Description : A single entry in the user part (the beginning) of
// the attrib file, this defines how the user would like
// some particular texture to be scaled.
// Description : A single entry in the .txa file, this defines how the
// user would like some particular texture to be scaled.
////////////////////////////////////////////////////////////////////
//
@ -27,10 +29,13 @@ class PTexture;
// # Comment
// :margin msize
// :palette xsize ysize
// :group groupname 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 {
@ -44,28 +49,25 @@ public:
void write(ostream &out) const;
bool match_texture(PTexture *texture, int &margin);
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_size, LT_scale, LT_name
};
class TextureName {
public:
TextureName(const string &pattern);
TextureName(const TextureName &copy) :
_pattern(copy._pattern) { }
string _pattern;
LT_margin, LT_palette, LT_group_relate,
LT_size, LT_scale, LT_name,
LT_group_assign
};
typedef vector<TextureName> TextureNames;
TextureNames _texture_names;
typedef vector<GlobPattern> Patterns;
Patterns _patterns;
typedef vector<string> Names;
Names _names;
ostream &list_textures(ostream &out) const;
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);