mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 08:44:19 -04:00
*** empty log message ***
This commit is contained in:
parent
a338413da4
commit
43f23a2a55
@ -81,6 +81,9 @@
|
|||||||
// Do we have <unistd.h>?
|
// Do we have <unistd.h>?
|
||||||
#define HAVE_UNISTD_H 1
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
// Do we have <utime.h>?
|
||||||
|
#define HAVE_UTIME_H 1
|
||||||
|
|
||||||
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
||||||
// interface)?
|
// interface)?
|
||||||
#define HAVE_SYS_SOUNDCARD_H
|
#define HAVE_SYS_SOUNDCARD_H
|
||||||
|
@ -81,6 +81,9 @@
|
|||||||
// Do we have <unistd.h>?
|
// Do we have <unistd.h>?
|
||||||
#define HAVE_UNISTD_H 1
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
// Do we have <utime.h>?
|
||||||
|
#define HAVE_UTIME_H 1
|
||||||
|
|
||||||
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
||||||
// interface)?
|
// interface)?
|
||||||
#define HAVE_SYS_SOUNDCARD_H 1
|
#define HAVE_SYS_SOUNDCARD_H 1
|
||||||
|
@ -81,6 +81,9 @@
|
|||||||
// Do we have <unistd.h>?
|
// Do we have <unistd.h>?
|
||||||
#define HAVE_UNISTD_H
|
#define HAVE_UNISTD_H
|
||||||
|
|
||||||
|
// Do we have <utime.h>?
|
||||||
|
#define HAVE_UTIME_H
|
||||||
|
|
||||||
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
// Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
||||||
// interface)?
|
// interface)?
|
||||||
#define HAVE_SYS_SOUNDCARD_H
|
#define HAVE_SYS_SOUNDCARD_H
|
||||||
|
@ -130,6 +130,9 @@ $[cdefine HAVE_SYS_TYPES]
|
|||||||
/* Define if you have the <unistd.h> header file. */
|
/* Define if you have the <unistd.h> header file. */
|
||||||
$[cdefine HAVE_UNISTD_H]
|
$[cdefine HAVE_UNISTD_H]
|
||||||
|
|
||||||
|
/* Define if you have the <utime.h> header file. */
|
||||||
|
$[cdefine HAVE_UTIME_H]
|
||||||
|
|
||||||
/* Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
/* Do we have <sys/soundcard.h> (and presumably a Linux-style audio
|
||||||
interface)? */
|
interface)? */
|
||||||
$[cdefine HAVE_SYS_SOUNDCARD_H]
|
$[cdefine HAVE_SYS_SOUNDCARD_H]
|
||||||
|
@ -31,6 +31,10 @@ Warning: Lib(s) $[nonexisting], referenced in $[DIRNAME]/$[TARGET], not found.
|
|||||||
#set DEPENDABLE_HEADERS $[DEPENDABLE_HEADERS] $[filter %.h %.I,$[SOURCES]]
|
#set DEPENDABLE_HEADERS $[DEPENDABLE_HEADERS] $[filter %.h %.I,$[SOURCES]]
|
||||||
#end metalib_target static_lib_target ss_lib_target lib_target noinst_lib_target bin_target noinst_bin_target
|
#end metalib_target static_lib_target ss_lib_target lib_target noinst_lib_target bin_target noinst_bin_target
|
||||||
|
|
||||||
|
#forscopes test_bin_target
|
||||||
|
#set DEPENDABLE_HEADERS $[DEPENDABLE_HEADERS] $[filter %.h %.I,$[SOURCES]]
|
||||||
|
#end test_bin_target
|
||||||
|
|
||||||
// Allow the user to define additional EXTRA_DEPENDS targets in each
|
// Allow the user to define additional EXTRA_DEPENDS targets in each
|
||||||
// Sources.pp.
|
// Sources.pp.
|
||||||
#define DEPEND_DIRS \
|
#define DEPEND_DIRS \
|
||||||
|
@ -18,6 +18,14 @@
|
|||||||
#include <stdio.h> // For rename()
|
#include <stdio.h> // For rename()
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_UTIME_H
|
||||||
|
#include <utime.h>
|
||||||
|
|
||||||
|
// We assume we have these too.
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
/* begin Win32-specific code */
|
/* begin Win32-specific code */
|
||||||
@ -787,6 +795,45 @@ open_read_write(fstream &stream) const {
|
|||||||
return (!stream.fail());
|
return (!stream.fail());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: Filename::touch
|
||||||
|
// Access: Public
|
||||||
|
// Description: Updates the modification time of the file to the
|
||||||
|
// current time. If the file does not already exist, it
|
||||||
|
// will be created. Returns true if successful, false
|
||||||
|
// if there is an error.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool Filename::
|
||||||
|
touch() const {
|
||||||
|
#ifdef HAVE_UTIME_H
|
||||||
|
// Most Unix systems can do this explicitly.
|
||||||
|
string filename = to_os_specific();
|
||||||
|
int result = utime(filename.c_str(), NULL);
|
||||||
|
if (result < 0) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
// So the file doesn't already exist; create it.
|
||||||
|
int fd = creat(filename.c_str(), 0666);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror(filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
perror(filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// Other systems may not have an explicit control over the
|
||||||
|
// modification time. For these systems, we'll just temporary open
|
||||||
|
// the file in append mode, then close it again (it gets closed when
|
||||||
|
// the ofstream goes out of scope).
|
||||||
|
ofstream file;
|
||||||
|
return open_append(file);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: Filename::unlink
|
// Function: Filename::unlink
|
||||||
// Access: Public
|
// Access: Public
|
||||||
|
@ -130,6 +130,8 @@ public:
|
|||||||
bool open_append(ofstream &stream) const;
|
bool open_append(ofstream &stream) const;
|
||||||
bool open_read_write(fstream &stream) const;
|
bool open_read_write(fstream &stream) const;
|
||||||
|
|
||||||
|
bool touch() const;
|
||||||
|
|
||||||
bool unlink() const;
|
bool unlink() const;
|
||||||
bool rename_to(const Filename &other) const;
|
bool rename_to(const Filename &other) const;
|
||||||
|
|
||||||
|
20
pandatool/src/egg-palettize/Sources.pp
Normal file
20
pandatool/src/egg-palettize/Sources.pp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#begin bin_target
|
||||||
|
#define TARGET egg-palettize
|
||||||
|
#define LOCAL_LIBS \
|
||||||
|
eggbase progbase
|
||||||
|
#define OTHER_LIBS \
|
||||||
|
egg:c linmath:c putil:c express:c pnmimagetypes:c \
|
||||||
|
pandaegg:m panda:m pandaexpress:m \
|
||||||
|
dtoolutil:c dconfig:c dtool:m pystub
|
||||||
|
|
||||||
|
#define SOURCES \
|
||||||
|
attribFile.cxx attribFile.h config_egg_palettize.cxx \
|
||||||
|
eggPalettize.cxx eggPalettize.h \
|
||||||
|
imageFile.cxx imageFile.h palette.cxx palette.h sourceEgg.cxx \
|
||||||
|
sourceEgg.h string_utils.cxx string_utils.h texture.cxx texture.h \
|
||||||
|
userAttribLine.cxx userAttribLine.h
|
||||||
|
|
||||||
|
#define INSTALL_HEADERS \
|
||||||
|
|
||||||
|
#end bin_target
|
||||||
|
|
1191
pandatool/src/egg-palettize/attribFile.cxx
Normal file
1191
pandatool/src/egg-palettize/attribFile.cxx
Normal file
File diff suppressed because it is too large
Load Diff
132
pandatool/src/egg-palettize/attribFile.h
Normal file
132
pandatool/src/egg-palettize/attribFile.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Filename: attribFile.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef ATTRIBFILE_H
|
||||||
|
#define ATTRIBFILE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <filename.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class UserAttribLine;
|
||||||
|
class Texture;
|
||||||
|
class Palette;
|
||||||
|
class SourceEgg;
|
||||||
|
class EggPalettize;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : AttribFile
|
||||||
|
// Description :
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class AttribFile {
|
||||||
|
public:
|
||||||
|
AttribFile(const Filename &filename);
|
||||||
|
|
||||||
|
string get_name() const;
|
||||||
|
|
||||||
|
bool grab_lock();
|
||||||
|
bool release_lock();
|
||||||
|
|
||||||
|
bool read();
|
||||||
|
bool write();
|
||||||
|
|
||||||
|
void update_params(EggPalettize *prog);
|
||||||
|
|
||||||
|
void get_req_sizes();
|
||||||
|
void update_texture_flags();
|
||||||
|
|
||||||
|
void repack_all_textures();
|
||||||
|
void repack_some_textures();
|
||||||
|
void optimal_resize();
|
||||||
|
void finalize_palettes();
|
||||||
|
void remove_unused_lines();
|
||||||
|
bool check_packing(bool force_optimal);
|
||||||
|
bool pack_texture(Texture *texture);
|
||||||
|
bool unpack_texture(Texture *texture);
|
||||||
|
|
||||||
|
void touch_dirty_egg_files(bool force_redo_all,
|
||||||
|
bool eggs_include_images);
|
||||||
|
|
||||||
|
Texture *get_texture(const string &name);
|
||||||
|
void get_eligible_textures(vector<Texture *> &textures);
|
||||||
|
SourceEgg *get_egg(Filename name);
|
||||||
|
|
||||||
|
bool generate_palette_images();
|
||||||
|
bool transfer_unplaced_images(bool force_redo_all);
|
||||||
|
|
||||||
|
void check_dup_textures(map<string, Texture *> &textures,
|
||||||
|
map<string, int> &dup_textures) const;
|
||||||
|
|
||||||
|
void collect_statistics(int &num_textures, int &num_placed,
|
||||||
|
int &num_palettes,
|
||||||
|
int &orig_size, int &resized_size,
|
||||||
|
int &palette_size, int &unplaced_size) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef vector<UserAttribLine *> UserLines;
|
||||||
|
UserLines _user_lines;
|
||||||
|
|
||||||
|
typedef map<string, SourceEgg *> Eggs;
|
||||||
|
Eggs _eggs;
|
||||||
|
|
||||||
|
typedef vector<Palette *> Palettes;
|
||||||
|
Palettes _palettes;
|
||||||
|
|
||||||
|
typedef map<string, Texture *> Textures;
|
||||||
|
Textures _textures;
|
||||||
|
|
||||||
|
string get_pi_filename(const string &txa_filename) const;
|
||||||
|
|
||||||
|
bool read_txa(istream &outfile);
|
||||||
|
bool read_pi(istream &outfile);
|
||||||
|
bool write_txa(ostream &outfile) const;
|
||||||
|
bool write_pi(ostream &outfile) const;
|
||||||
|
|
||||||
|
bool parse_params(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_packing(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_texture(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_pathname(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_egg(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_palette(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_unplaced(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
bool parse_surprises(const vector<string> &words, istream &infile,
|
||||||
|
string &line, int &line_num);
|
||||||
|
|
||||||
|
bool _optimal;
|
||||||
|
bool _txa_needs_rewrite;
|
||||||
|
|
||||||
|
string _name;
|
||||||
|
Filename _txa_filename;
|
||||||
|
Filename _pi_filename;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
// These parameter values come from the command line, or from the
|
||||||
|
// .pi file if omitted from the command line. These are the
|
||||||
|
// parameter values that specifically refer to textures and
|
||||||
|
// palettes, and thus should be stored in the .pi file for future
|
||||||
|
// reference.
|
||||||
|
Filename _map_dirname;
|
||||||
|
string _palette_prefix;
|
||||||
|
int _pal_xsize, _pal_ysize;
|
||||||
|
int _default_margin;
|
||||||
|
bool _force_power_2;
|
||||||
|
bool _aggressively_clean_mapdir;
|
||||||
|
|
||||||
|
int _txa_fd;
|
||||||
|
fstream _txa_fstrm;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
14
pandatool/src/egg-palettize/config_egg_palettize.cxx
Normal file
14
pandatool/src/egg-palettize/config_egg_palettize.cxx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Filename: config_egg_palettize.cxx
|
||||||
|
// Created by: drose (02Nov00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <dconfig.h>
|
||||||
|
|
||||||
|
#include "sourceEgg.h"
|
||||||
|
|
||||||
|
Configure(config_egg_palettize);
|
||||||
|
|
||||||
|
ConfigureFn(config_egg_palettize) {
|
||||||
|
SourceEgg::init_type();
|
||||||
|
}
|
592
pandatool/src/egg-palettize/eggPalettize.cxx
Normal file
592
pandatool/src/egg-palettize/eggPalettize.cxx
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
// Filename: eggPalettize.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "eggPalettize.h"
|
||||||
|
#include "attribFile.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "sourceEgg.h"
|
||||||
|
|
||||||
|
#include <pnmImage.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::Constructor
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
EggPalettize::
|
||||||
|
EggPalettize() : EggMultiFilter(true) {
|
||||||
|
set_program_description
|
||||||
|
("egg-palettize attempts to pack several texture maps from various models "
|
||||||
|
"together into one or more palette images, for improved rendering performance "
|
||||||
|
"and ease of texture management. It can also resize textures on the fly, "
|
||||||
|
"whether or not they are actually placed on a palette.\n\n"
|
||||||
|
|
||||||
|
"egg-palettize reads and writes an AttributesFile, which contains instructions "
|
||||||
|
"from the user about resizing particular textures, as well as the complete "
|
||||||
|
"information necessary to reconstruct the palettization from past runs, "
|
||||||
|
"including references to other egg files that may share this palette. This "
|
||||||
|
"is designed to allow multiple egg files to use the same palette, without "
|
||||||
|
"having to process them all at once.\n\n"
|
||||||
|
|
||||||
|
"Note that it is not even necessary to specify any egg files at all on the "
|
||||||
|
"command line; egg-palettize can be run on an existing AttributesFiles by "
|
||||||
|
"itself to freshen up a palette when necessary.");
|
||||||
|
|
||||||
|
|
||||||
|
clear_runlines();
|
||||||
|
add_runline("[opts] attribfile.txa file.egg [file.egg ...]");
|
||||||
|
|
||||||
|
add_option
|
||||||
|
("s", "", 0,
|
||||||
|
"Do not process anything, but report statistics on all palette "
|
||||||
|
"information files read.",
|
||||||
|
&EggPalettize::dispatch_none, &_statistics_only);
|
||||||
|
redescribe_option
|
||||||
|
("d",
|
||||||
|
"The directory in which to write the palettized egg files. This is "
|
||||||
|
"only necessary if more than one egg file is processed at the same "
|
||||||
|
"time; if it is included, each egg file will be processed and written "
|
||||||
|
"into the indicated directory.");
|
||||||
|
add_option
|
||||||
|
("dm", "dirname", 0,
|
||||||
|
"The directory in which to place all maps: generated palettes, "
|
||||||
|
"as well as images which were not placed on palettes "
|
||||||
|
"(but may have been resized). It is often best if this is a "
|
||||||
|
"fully-qualified directory name rather than a relative directory name, "
|
||||||
|
"particularly if -d is used to write the egg files to a directory "
|
||||||
|
"different than the current directory, as the same name is written "
|
||||||
|
"into the egg files.",
|
||||||
|
&EggPalettize::dispatch_filename, &_got_map_dirname, &_map_dirname);
|
||||||
|
add_option
|
||||||
|
("f", "", 0,
|
||||||
|
"Force an optimal packing. By default, textures are added to "
|
||||||
|
"existing palettes without disturbing them, which can lead to "
|
||||||
|
"suboptimal packing. Including this switch forces the palettes "
|
||||||
|
"to be rebuilt if necessary to optimize the packing, but this "
|
||||||
|
"may invalidate other egg files which share this palette.",
|
||||||
|
&EggPalettize::dispatch_none, &_force_optimal);
|
||||||
|
add_option
|
||||||
|
("F", "", 0,
|
||||||
|
"Force a redo of everything. This is useful in case something "
|
||||||
|
"has gotten out of sync and the old palettes are just bad.",
|
||||||
|
&EggPalettize::dispatch_none, &_force_redo_all);
|
||||||
|
add_option
|
||||||
|
("R", "", 0,
|
||||||
|
"Resize mostly-empty palettes to their minimal size.",
|
||||||
|
&EggPalettize::dispatch_none, &_optimal_resize);
|
||||||
|
add_option
|
||||||
|
("t", "", 0,
|
||||||
|
"Touch any additional egg files that share this palette and will "
|
||||||
|
"need to be refreshed, but were not included on the command "
|
||||||
|
"line. Presumably a future make pass will cause them to be run "
|
||||||
|
"through egg-palettize again.",
|
||||||
|
&EggPalettize::dispatch_none, &_touch_eggs);
|
||||||
|
add_option
|
||||||
|
("T", "", 0,
|
||||||
|
"When touching egg files, consider an egg file to be invalidated "
|
||||||
|
"if textures have changed in any way, rather than simply moving "
|
||||||
|
"around within their palettes. You should use this switch "
|
||||||
|
"if the texture images themselves are to be stored within bam files "
|
||||||
|
"generated from the eggs, or some such nonsense.",
|
||||||
|
&EggPalettize::dispatch_none, &_eggs_include_images);
|
||||||
|
add_option
|
||||||
|
("C", "", 0,
|
||||||
|
"Aggressively keep the map directory clean by deleting unused "
|
||||||
|
"textures from previous passes.",
|
||||||
|
&EggPalettize::dispatch_none, &_got_aggressively_clean_mapdir);
|
||||||
|
add_option
|
||||||
|
("r", "", 0,
|
||||||
|
"Respect any repeat/clamp flags given in the egg files. The "
|
||||||
|
"default is to override a repeat flag if a texture's UV's don't "
|
||||||
|
"exceed the range [0..1].",
|
||||||
|
&EggPalettize::dispatch_none, &_dont_override_repeats);
|
||||||
|
add_option
|
||||||
|
("z", "fuzz", 0,
|
||||||
|
"The fuzz factor when applying the above repeat test. UV's are "
|
||||||
|
"considered to be in the range [0..1] if they are actually in "
|
||||||
|
"the range [0-fuzz .. 1+fuzz]. The default is 0.01.",
|
||||||
|
&EggPalettize::dispatch_double, NULL, &_fuzz_factor);
|
||||||
|
add_option
|
||||||
|
("m", "margin", 0,
|
||||||
|
"Specify the default margin size.",
|
||||||
|
&EggPalettize::dispatch_int, &_got_default_margin, &_default_margin);
|
||||||
|
add_option
|
||||||
|
("P", "x,y", 0,
|
||||||
|
"Specify the default palette size.",
|
||||||
|
&EggPalettize::dispatch_int_pair, &_got_palette_size, _pal_size);
|
||||||
|
add_option
|
||||||
|
("2", "", 0,
|
||||||
|
"Force textures that have been left out of the palette to a size "
|
||||||
|
"which is an 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 "
|
||||||
|
"texture.",
|
||||||
|
&EggPalettize::dispatch_none, &_remove_unused_lines);
|
||||||
|
add_option
|
||||||
|
("H", "", 0,
|
||||||
|
"Describe the syntax of the attributes file.",
|
||||||
|
&EggPalettize::dispatch_none, &_describe_input_file);
|
||||||
|
|
||||||
|
_fuzz_factor = 0.01;
|
||||||
|
_aggressively_clean_mapdir = false;
|
||||||
|
_force_power_2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::handle_args
|
||||||
|
// Access: Protected, Virtual
|
||||||
|
// Description: Does something with the additional arguments on the
|
||||||
|
// command line (after all the -options have been
|
||||||
|
// parsed). Returns true if the arguments are good,
|
||||||
|
// false otherwise.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool EggPalettize::
|
||||||
|
handle_args(ProgramBase::Args &args) {
|
||||||
|
if (_describe_input_file) {
|
||||||
|
describe_input_file();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Args egg_names;
|
||||||
|
Args txa_names;
|
||||||
|
|
||||||
|
// First, collect all the filenames.
|
||||||
|
Args::iterator ai;
|
||||||
|
for (ai = args.begin(); ai != args.end(); ++ai) {
|
||||||
|
Filename filename = (*ai);
|
||||||
|
string ext = filename.get_extension();
|
||||||
|
|
||||||
|
if (ext == "egg") {
|
||||||
|
egg_names.push_back(filename);
|
||||||
|
} else if (ext == "txa" || ext == "pi") {
|
||||||
|
txa_names.push_back(filename);
|
||||||
|
} else {
|
||||||
|
nout << "Don't know what kind of file " << filename << " is.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now sanity check them. Either we have zero egg files, and one or
|
||||||
|
// more txa files, or we have some egg files and exactly one txa
|
||||||
|
// file.
|
||||||
|
if (egg_names.empty()) {
|
||||||
|
if (txa_names.empty()) {
|
||||||
|
nout << "No files specified.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ai = txa_names.begin(); ai != txa_names.end(); ++ai) {
|
||||||
|
AttribFile *af = new AttribFile(*ai);
|
||||||
|
_attrib_files.push_back(af);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (txa_names.empty()) {
|
||||||
|
nout << "Must include an attribs.txa file.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (txa_names.size() != 1) {
|
||||||
|
nout << "Can only read one attribs.txa file when processing egg files.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttribFile *af = new AttribFile(txa_names[0]);
|
||||||
|
_attrib_files.push_back(af);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EggMultiFilter::handle_args(egg_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::read_egg
|
||||||
|
// Access: Protected, Virtual
|
||||||
|
// Description: Allocates and returns a new EggData structure that
|
||||||
|
// represents the indicated egg file. If the egg file
|
||||||
|
// cannot be read for some reason, returns NULL.
|
||||||
|
//
|
||||||
|
// This can be overridden by derived classes to control
|
||||||
|
// how the egg files are read, or to extend the
|
||||||
|
// information stored with each egg structure, by
|
||||||
|
// deriving from EggData.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
EggData *EggPalettize::
|
||||||
|
read_egg(const Filename &filename) {
|
||||||
|
// We should only call this function if we have exactly one .txa file.
|
||||||
|
nassertr(_attrib_files.size() == 1, (EggData *)NULL);
|
||||||
|
|
||||||
|
SourceEgg *egg = _attrib_files[0]->get_egg(filename);
|
||||||
|
if (egg->read(filename)) {
|
||||||
|
return egg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failure reading.
|
||||||
|
delete egg;
|
||||||
|
return (EggData *)NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::describe_input_file
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void EggPalettize::
|
||||||
|
describe_input_file() {
|
||||||
|
nout <<
|
||||||
|
"An attributes file consists mostly of lines describing desired sizes of "
|
||||||
|
"texture maps. The format resembles, but is not identical to, that of "
|
||||||
|
"the qtess input file. Examples:\n\n"
|
||||||
|
|
||||||
|
" texturename.rgb : 64 64\n"
|
||||||
|
" texture-a.rgb texture-b.rgb : 32 16 2\n"
|
||||||
|
" *.rgb : 50%\n"
|
||||||
|
" eyelids.rgb : 16 16 omit\n\n"
|
||||||
|
|
||||||
|
"In general, each line consists of one or more filenames (and can "
|
||||||
|
"contain shell globbing characters like '*' or '?'), and a colon "
|
||||||
|
"followed by a size request. For each texture appearing in an egg "
|
||||||
|
"file, the input list is scanned from the beginning and the first "
|
||||||
|
"line that matches the filename defines the size of the texture.\n\n"
|
||||||
|
|
||||||
|
"A size request may be either a pair of numbers, giving a specific x y "
|
||||||
|
"size of the texture, or it may be a scale factor in the form of a "
|
||||||
|
"percentage. It may also include an additional number, giving a margin "
|
||||||
|
"for this particular texture (otherwise the default margin is "
|
||||||
|
"applied). Finally, the keyword 'omit' may be included along with the "
|
||||||
|
"size to specify that the texture should not be placed in a palette.\n\n"
|
||||||
|
|
||||||
|
"There are some other special lines that may appear in this second, "
|
||||||
|
"along with the resize commands. They begin with a colon to "
|
||||||
|
"distinguish them from the resize commands. They are:\n\n"
|
||||||
|
|
||||||
|
":palette xsize ysize\n\n"
|
||||||
|
|
||||||
|
"This specifies the size of the palette file(s) to be created. It "
|
||||||
|
"overrides the -s command-line option.\n\n"
|
||||||
|
|
||||||
|
":margin msize\n\n"
|
||||||
|
|
||||||
|
"This specifies the size of the default margin for all subsequent "
|
||||||
|
"resize commands. This may appear several times in a given file.\n\n"
|
||||||
|
|
||||||
|
"Comments may appear freely throughout the file, and are set off by a "
|
||||||
|
"hash mark (#).\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::format_space
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
string EggPalettize::
|
||||||
|
format_space(int size_pixels, bool verbose) {
|
||||||
|
int size_bytes = size_pixels * 4;
|
||||||
|
int size_k = (size_bytes + 512) / 1024;
|
||||||
|
int mm_size_k = (size_bytes * 4 / 3 + 512) / 1024;
|
||||||
|
char str[128];
|
||||||
|
if (verbose) {
|
||||||
|
sprintf(str, "%dk, w/mm %dk", size_k, mm_size_k);
|
||||||
|
} else {
|
||||||
|
sprintf(str, "%dk, %dk", size_k, mm_size_k);
|
||||||
|
}
|
||||||
|
assert(strlen(str) < 128);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::report_statistics
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void EggPalettize::
|
||||||
|
report_statistics() {
|
||||||
|
// Look for textures in common.
|
||||||
|
map<string, Texture *> textures;
|
||||||
|
map<string, int> dup_textures;
|
||||||
|
|
||||||
|
AttribFiles::iterator afi;
|
||||||
|
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
|
||||||
|
AttribFile &af = *(*afi);
|
||||||
|
af.check_dup_textures(textures, dup_textures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dup_textures.empty()) {
|
||||||
|
cout << "\nEach texture appears in only one palette group.\n";
|
||||||
|
} else {
|
||||||
|
cout << "\nThe following textures appear in more than one palette group:\n";
|
||||||
|
int net_wasted_size = 0;
|
||||||
|
map<string, int>::const_iterator di;
|
||||||
|
for (di = dup_textures.begin(); di != dup_textures.end(); ++di) {
|
||||||
|
cout << " " << (*di).first << " ("
|
||||||
|
<< format_space((*di).second) << ")\n";
|
||||||
|
net_wasted_size += (*di).second;
|
||||||
|
}
|
||||||
|
cout << "Total wasted memory from common textures is "
|
||||||
|
<< format_space(net_wasted_size, true) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int net_palette_size = 0;
|
||||||
|
int net_num_palettes = 0;
|
||||||
|
|
||||||
|
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
|
||||||
|
AttribFile &af = *(*afi);
|
||||||
|
int num_textures, num_placed, num_palettes;
|
||||||
|
int orig_size, resized_size, palette_size, unplaced_size;
|
||||||
|
af.collect_statistics(num_textures, num_placed, num_palettes,
|
||||||
|
orig_size, resized_size,
|
||||||
|
palette_size, unplaced_size);
|
||||||
|
cout << "\nPalette group " << af.get_name() << ":\n"
|
||||||
|
<< " " << num_textures << " textures, "
|
||||||
|
<< num_placed << " of which are packed onto "
|
||||||
|
<< num_palettes << " palettes (" << num_textures - num_placed
|
||||||
|
<< " unplaced)"
|
||||||
|
<< "\n Original texture memory: "
|
||||||
|
<< format_space(orig_size, true)
|
||||||
|
<< "\n After resizing: "
|
||||||
|
<< format_space(resized_size, true)
|
||||||
|
<< "\n After palettizing: "
|
||||||
|
<< format_space(palette_size + unplaced_size, true)
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
net_palette_size += palette_size;
|
||||||
|
net_num_palettes += num_palettes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int net_num_textures = textures.size();
|
||||||
|
int net_num_placed = 0;
|
||||||
|
int net_orig_size = 0;
|
||||||
|
int net_resized_size = 0;
|
||||||
|
int net_unplaced_size = 0;
|
||||||
|
typedef map<Texture::OmitReason, pair<int, int> > UnplacedReasons;
|
||||||
|
UnplacedReasons unplaced_reasons;
|
||||||
|
|
||||||
|
map<string, Texture *>::iterator ti;
|
||||||
|
for (ti = textures.begin(); ti != textures.end(); ++ti) {
|
||||||
|
Texture *texture = (*ti).second;
|
||||||
|
|
||||||
|
int xsize, ysize;
|
||||||
|
int rxsize, rysize;
|
||||||
|
int rsize = 0;
|
||||||
|
if (texture->get_size(xsize, ysize) &&
|
||||||
|
texture->get_last_req(rxsize, rysize)) {
|
||||||
|
net_orig_size += xsize * ysize;
|
||||||
|
net_resized_size += rxsize * rysize;
|
||||||
|
rsize = rxsize * rysize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (texture->is_really_packed()) {
|
||||||
|
net_num_placed++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
net_unplaced_size += rsize;
|
||||||
|
|
||||||
|
// Here's an unplaced texture; add its size to the unplaced
|
||||||
|
// reasons table.
|
||||||
|
UnplacedReasons::iterator uri =
|
||||||
|
unplaced_reasons.find(texture->get_omit());
|
||||||
|
if (uri == unplaced_reasons.end()) {
|
||||||
|
unplaced_reasons.insert
|
||||||
|
(UnplacedReasons::value_type(texture->get_omit(),
|
||||||
|
pair<int, int>(1, rsize)));
|
||||||
|
} else {
|
||||||
|
(*uri).second.first++;
|
||||||
|
(*uri).second.second += rsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "\nOverall:\n"
|
||||||
|
<< " " << net_num_textures << " textures, "
|
||||||
|
<< net_num_placed << " of which are packed onto "
|
||||||
|
<< net_num_palettes << " palettes ("
|
||||||
|
<< net_num_textures - net_num_placed << " unplaced)"
|
||||||
|
<< "\n Original texture memory: "
|
||||||
|
<< format_space(net_orig_size, true)
|
||||||
|
<< "\n After resizing: "
|
||||||
|
<< format_space(net_resized_size, true)
|
||||||
|
<< "\n After palettizing: "
|
||||||
|
<< format_space(net_palette_size + net_unplaced_size, true)
|
||||||
|
<< "\n\n";
|
||||||
|
|
||||||
|
UnplacedReasons::iterator uri;
|
||||||
|
for (uri = unplaced_reasons.begin();
|
||||||
|
uri != unplaced_reasons.end();
|
||||||
|
++uri) {
|
||||||
|
Texture::OmitReason reason = (*uri).first;
|
||||||
|
int count = (*uri).second.first;
|
||||||
|
int size = (*uri).second.second;
|
||||||
|
cout << count << " textures (" << format_space(size)
|
||||||
|
<< ") unplaced because ";
|
||||||
|
switch (reason) {
|
||||||
|
case Texture::OR_none:
|
||||||
|
cout << "of no reason--textures should have been placed\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_size:
|
||||||
|
cout << "size was too large for palette\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_repeats:
|
||||||
|
cout << "repeating\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_omitted:
|
||||||
|
cout << "explicitly omitted\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_unused:
|
||||||
|
cout << "unused by any egg file\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_unknown:
|
||||||
|
cout << "texture file is missing\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_cmdline:
|
||||||
|
cout << "-x was given on command line\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::OR_solitary:
|
||||||
|
cout << "texture was alone on a palette\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cout << "unknown reason\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggPalettize::run
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void EggPalettize::
|
||||||
|
run() {
|
||||||
|
_force_power_2 = _got_force_power_2;
|
||||||
|
_aggressively_clean_mapdir = _got_aggressively_clean_mapdir;
|
||||||
|
|
||||||
|
bool okflag = true;
|
||||||
|
|
||||||
|
// We'll repeat the processing steps for each attrib file. If we
|
||||||
|
// have multiple attrib files, then we have no egg files, so all of
|
||||||
|
// the egg loops below fall out. On the other hand, if we do have
|
||||||
|
// egg files, then we have only one attrib file, so this outer loop
|
||||||
|
// falls out.
|
||||||
|
|
||||||
|
AttribFiles::iterator afi;
|
||||||
|
for (afi = _attrib_files.begin(); afi != _attrib_files.end(); ++afi) {
|
||||||
|
AttribFile &af = *(*afi);
|
||||||
|
|
||||||
|
if (!af.grab_lock()) {
|
||||||
|
// Failing to grab the write lock on the attribute file is a
|
||||||
|
// fatal error.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_statistics_only) {
|
||||||
|
nout << "Reading " << af.get_name() << "\n";
|
||||||
|
okflag = af.read();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nout << "Processing " << af.get_name() << "\n";
|
||||||
|
|
||||||
|
if (!af.read()) {
|
||||||
|
// Failing to read the attribute file is a fatal error.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
af.update_params(this);
|
||||||
|
|
||||||
|
// Get all the texture references out of the egg files.
|
||||||
|
Eggs::iterator ei;
|
||||||
|
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
|
||||||
|
SourceEgg *egg = DCAST(SourceEgg, *ei);
|
||||||
|
egg->get_textures(af, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the user's requested size changes onto the textures.
|
||||||
|
af.get_req_sizes();
|
||||||
|
af.update_texture_flags();
|
||||||
|
|
||||||
|
if (!af.check_packing(_force_optimal) || _force_redo_all) {
|
||||||
|
// We need some more palettization work here.
|
||||||
|
if (_force_redo_all || _force_optimal) {
|
||||||
|
af.repack_all_textures();
|
||||||
|
} else {
|
||||||
|
af.repack_some_textures();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nout << "No changes to palette arrangement are required.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
af.finalize_palettes();
|
||||||
|
|
||||||
|
if (_optimal_resize) {
|
||||||
|
af.optimal_resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_remove_unused_lines) {
|
||||||
|
af.remove_unused_lines();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!af.write()) {
|
||||||
|
// Failing to rewrite the attribute file is a fatal error.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And rebuild whatever images are necessary.
|
||||||
|
okflag = af.generate_palette_images() && okflag;
|
||||||
|
okflag = af.transfer_unplaced_images(_force_redo_all) && okflag;
|
||||||
|
|
||||||
|
// Now apply the palettization effects to the egg files.
|
||||||
|
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
|
||||||
|
SourceEgg *egg = DCAST(SourceEgg, *ei);
|
||||||
|
egg->update_trefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the egg files.
|
||||||
|
write_eggs();
|
||||||
|
|
||||||
|
if (_touch_eggs) {
|
||||||
|
af.touch_dirty_egg_files(_force_redo_all, _eggs_include_images);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
okflag = af.release_lock() && okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_statistics_only) {
|
||||||
|
report_statistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!okflag) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[]) {
|
||||||
|
EggPalettize prog;
|
||||||
|
prog.parse_command_line(argc, argv);
|
||||||
|
prog.run();
|
||||||
|
return 0;
|
||||||
|
}
|
71
pandatool/src/egg-palettize/eggPalettize.h
Normal file
71
pandatool/src/egg-palettize/eggPalettize.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Filename: eggPalettize.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef EGGPALETTIZE_H
|
||||||
|
#define EGGPALETTIZE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include "attribFile.h"
|
||||||
|
#include <eggMultiFilter.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : EggPalettize
|
||||||
|
// Description : This is the heart of the program.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class EggPalettize : public EggMultiFilter {
|
||||||
|
public:
|
||||||
|
EggPalettize();
|
||||||
|
|
||||||
|
virtual bool handle_args(Args &args);
|
||||||
|
virtual EggData *read_egg(const Filename &filename);
|
||||||
|
|
||||||
|
void describe_input_file();
|
||||||
|
|
||||||
|
string format_space(int size_pixels, bool verbose = false);
|
||||||
|
void report_statistics();
|
||||||
|
void run();
|
||||||
|
|
||||||
|
typedef vector<AttribFile *> AttribFiles;
|
||||||
|
AttribFiles _attrib_files;
|
||||||
|
|
||||||
|
// The following parameter values specifically relate to textures
|
||||||
|
// and palettes. These values are store in the .pi file for future
|
||||||
|
// reference.
|
||||||
|
Filename _map_dirname;
|
||||||
|
bool _got_map_dirname;
|
||||||
|
int _pal_size[2];
|
||||||
|
bool _got_palette_size;
|
||||||
|
int _default_margin;
|
||||||
|
bool _got_default_margin;
|
||||||
|
bool _force_power_2;
|
||||||
|
bool _got_force_power_2;
|
||||||
|
bool _aggressively_clean_mapdir;
|
||||||
|
bool _got_aggressively_clean_mapdir;
|
||||||
|
|
||||||
|
// The following values relate specifically to egg files. They're
|
||||||
|
// not saved for future sessions.
|
||||||
|
double _fuzz_factor;
|
||||||
|
bool _dont_override_repeats;
|
||||||
|
|
||||||
|
// The following values control behavior specific to this session.
|
||||||
|
// They're not saved either.
|
||||||
|
bool _statistics_only;
|
||||||
|
bool _force_optimal;
|
||||||
|
bool _force_redo_all;
|
||||||
|
bool _optimal_resize;
|
||||||
|
bool _touch_eggs;
|
||||||
|
bool _eggs_include_images;
|
||||||
|
bool _dont_palettize;
|
||||||
|
bool _remove_unused_lines;
|
||||||
|
|
||||||
|
bool _describe_input_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
14
pandatool/src/egg-palettize/imageFile.cxx
Normal file
14
pandatool/src/egg-palettize/imageFile.cxx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Filename: imageFile.cxx
|
||||||
|
// Created by: drose (07Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "imageFile.h"
|
||||||
|
|
||||||
|
ImageFile::
|
||||||
|
ImageFile() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageFile::
|
||||||
|
~ImageFile() {
|
||||||
|
}
|
28
pandatool/src/egg-palettize/imageFile.h
Normal file
28
pandatool/src/egg-palettize/imageFile.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Filename: imageFile.h
|
||||||
|
// Created by: drose (07Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef IMAGEFILE_H
|
||||||
|
#define IMAGEFILE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <filename.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : ImageFile
|
||||||
|
// Description : This is the base class for both Palette and Texture.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class ImageFile {
|
||||||
|
public:
|
||||||
|
ImageFile();
|
||||||
|
virtual ~ImageFile();
|
||||||
|
|
||||||
|
virtual Filename get_filename() const=0;
|
||||||
|
virtual Filename get_basename() const=0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
544
pandatool/src/egg-palettize/palette.cxx
Normal file
544
pandatool/src/egg-palettize/palette.cxx
Normal file
@ -0,0 +1,544 @@
|
|||||||
|
// Filename: palette.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "palette.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "attribFile.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
#include <notify.h>
|
||||||
|
#include <pnmImage.h>
|
||||||
|
|
||||||
|
bool Palette::TexturePlacement::
|
||||||
|
intersects(int hleft, int htop, int xsize, int ysize) const {
|
||||||
|
int hright = hleft + xsize;
|
||||||
|
int hbot = htop + ysize;
|
||||||
|
|
||||||
|
int mright = _left + _xsize;
|
||||||
|
int mbot = _top + _ysize;
|
||||||
|
|
||||||
|
return
|
||||||
|
!(hleft >= mright || hright <= _left ||
|
||||||
|
htop >= mbot || hbot <= _top);
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *Palette::TexturePlacement::
|
||||||
|
resize_image(PNMImage *source) const {
|
||||||
|
// Compute need_*size to account for the (interior) margins.
|
||||||
|
// However, if the size of the texture if very small, we want to
|
||||||
|
// make exterior margins, so we won't reduce the size past a certain
|
||||||
|
// point.
|
||||||
|
int need_xsize = min(_xsize, max(_xsize - 2 * _margin, 8));
|
||||||
|
int need_ysize = min(_ysize, max(_ysize - 2 * _margin, 8));
|
||||||
|
|
||||||
|
if (source->get_x_size() == need_xsize && source->get_y_size() == need_ysize) {
|
||||||
|
// The image is already the right size.
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *new_image =
|
||||||
|
new PNMImage(need_xsize, need_ysize, source->get_color_type());
|
||||||
|
new_image->gaussian_filter_from(0.5, *source);
|
||||||
|
delete source;
|
||||||
|
return new_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *Palette::TexturePlacement::
|
||||||
|
add_margins(PNMImage *source) const {
|
||||||
|
if (_margin == 0) {
|
||||||
|
// No margins to add.
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
int orig_xsize = source->get_x_size();
|
||||||
|
int orig_ysize = source->get_y_size();
|
||||||
|
|
||||||
|
int need_xsize = orig_xsize + _margin * 2;
|
||||||
|
int need_ysize = orig_ysize + _margin * 2;
|
||||||
|
|
||||||
|
PNMImage *new_image =
|
||||||
|
new PNMImage(need_xsize, need_ysize, source->get_color_type());
|
||||||
|
new_image->copy_sub_image(*source, _margin, _margin);
|
||||||
|
|
||||||
|
// Now extend the margin out to the sides.
|
||||||
|
for (int i = 0; i < _margin; i++) {
|
||||||
|
for (int x = 0; x < orig_xsize; x++) {
|
||||||
|
// top edge
|
||||||
|
new_image->set_xel(x + _margin, i, source->get_xel(x, 0));
|
||||||
|
// bottom edge
|
||||||
|
new_image->set_xel(x + _margin, need_ysize - 1 - i,
|
||||||
|
source->get_xel(x, orig_ysize - 1));
|
||||||
|
}
|
||||||
|
for (int y = 0; y < orig_ysize; y++) {
|
||||||
|
// left edge
|
||||||
|
new_image->set_xel(i, y + _margin, source->get_xel(0, y));
|
||||||
|
// right edge
|
||||||
|
new_image->set_xel(need_xsize - 1 - i, y + _margin,
|
||||||
|
source->get_xel(orig_xsize - 1, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < _margin; j++) {
|
||||||
|
// top-left corner
|
||||||
|
new_image->set_xel(i, j, source->get_xel(0, 0));
|
||||||
|
// top-right corner
|
||||||
|
new_image->set_xel(need_xsize - 1 - i, j,
|
||||||
|
source->get_xel(orig_xsize - 1, 0));
|
||||||
|
// bottom-left corner
|
||||||
|
new_image->set_xel(i, need_ysize - 1 - j,
|
||||||
|
source->get_xel(0, orig_ysize - 1));
|
||||||
|
// bottom-right corner
|
||||||
|
new_image->set_xel(need_xsize - 1 - i, need_ysize - 1 - j,
|
||||||
|
source->get_xel(orig_xsize - 1, orig_ysize - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source->has_alpha()) {
|
||||||
|
// Now do the same thing in the alpha channel.
|
||||||
|
for (int i = 0; i < _margin; i++) {
|
||||||
|
for (int x = 0; x < orig_xsize; x++) {
|
||||||
|
// top edge
|
||||||
|
new_image->set_alpha(x + _margin, i, source->get_alpha(x, 0));
|
||||||
|
// bottom edge
|
||||||
|
new_image->set_alpha(x + _margin, need_ysize - 1 - i,
|
||||||
|
source->get_alpha(x, orig_ysize - 1));
|
||||||
|
}
|
||||||
|
for (int y = 0; y < orig_ysize; y++) {
|
||||||
|
// left edge
|
||||||
|
new_image->set_alpha(i, y + _margin, source->get_alpha(0, y));
|
||||||
|
// right edge
|
||||||
|
new_image->set_alpha(need_xsize - 1 - i, y + _margin,
|
||||||
|
source->get_alpha(orig_xsize - 1, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < _margin; j++) {
|
||||||
|
// top-left corner
|
||||||
|
new_image->set_alpha(i, j, source->get_alpha(0, 0));
|
||||||
|
// top-right corner
|
||||||
|
new_image->set_alpha(need_xsize - 1 - i, j,
|
||||||
|
source->get_alpha(orig_xsize - 1, 0));
|
||||||
|
// bottom-left corner
|
||||||
|
new_image->set_alpha(i, need_ysize - 1 - j,
|
||||||
|
source->get_alpha(0, orig_ysize - 1));
|
||||||
|
// bottom-right corner
|
||||||
|
new_image->set_alpha(need_xsize - 1 - i, need_ysize - 1 - j,
|
||||||
|
source->get_alpha(orig_xsize - 1, orig_ysize - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete source;
|
||||||
|
return new_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Palette::
|
||||||
|
Palette(const Filename &filename, int xsize, int ysize, int components,
|
||||||
|
AttribFile *af) :
|
||||||
|
_filename(filename),
|
||||||
|
_xsize(xsize),
|
||||||
|
_ysize(ysize),
|
||||||
|
_components(components),
|
||||||
|
_attrib_file(af)
|
||||||
|
{
|
||||||
|
_index = -1;
|
||||||
|
_palette_changed = false;
|
||||||
|
_new_palette = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Palette::
|
||||||
|
Palette(int index, int xsize, int ysize, int components, AttribFile *af) :
|
||||||
|
_index(index),
|
||||||
|
_xsize(xsize),
|
||||||
|
_ysize(ysize),
|
||||||
|
_components(components),
|
||||||
|
_attrib_file(af)
|
||||||
|
{
|
||||||
|
_palette_changed = false;
|
||||||
|
_new_palette = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Palette::
|
||||||
|
~Palette() {
|
||||||
|
// Unmark any textures we've had packed.
|
||||||
|
TexPlace::iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
(*ti)._texture->mark_unpacked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename Palette::
|
||||||
|
get_filename() const {
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename Palette::
|
||||||
|
get_basename() const {
|
||||||
|
return _basename;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
changed() const {
|
||||||
|
return _palette_changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
new_palette() const {
|
||||||
|
return _new_palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Palette::
|
||||||
|
get_num_textures() const {
|
||||||
|
return _texplace.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
check_uses_alpha() const {
|
||||||
|
// Returns true if any texture in the palette uses alpha.
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
if ((*ti)._texture->uses_alpha()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Palette::
|
||||||
|
get_size(int &xsize, int &ysize) const {
|
||||||
|
xsize = _xsize;
|
||||||
|
ysize = _ysize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Palette::
|
||||||
|
place_texture_at(Texture *texture, int left, int top,
|
||||||
|
int xsize, int ysize, int margin) {
|
||||||
|
TexturePlacement tp;
|
||||||
|
tp._texture = texture;
|
||||||
|
tp._left = left;
|
||||||
|
tp._top = top;
|
||||||
|
tp._xsize = xsize;
|
||||||
|
tp._ysize = ysize;
|
||||||
|
tp._margin = margin;
|
||||||
|
_texplace.push_back(tp);
|
||||||
|
|
||||||
|
texture->mark_pack_location(this, left, top, xsize, ysize, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
pack_texture(Texture *texture) {
|
||||||
|
int xsize, ysize;
|
||||||
|
if (!texture->get_req(xsize, ysize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int left, top;
|
||||||
|
if (find_home(left, top, xsize, ysize)) {
|
||||||
|
place_texture_at(texture, left, top, xsize, ysize, texture->get_margin());
|
||||||
|
_palette_changed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
unpack_texture(Texture *texture) {
|
||||||
|
TexPlace::iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
if ((*ti)._texture == texture) {
|
||||||
|
_texplace.erase(ti);
|
||||||
|
texture->mark_unpacked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to shrink the palette down to the smallest possible (power
|
||||||
|
// of 2) size that includes all its textures.
|
||||||
|
void Palette::
|
||||||
|
optimal_resize() {
|
||||||
|
if (_texplace.size() < 2) {
|
||||||
|
// If we don't have at least two textures, there's no point in
|
||||||
|
// shrinking the palette because we won't be using it anyway.
|
||||||
|
// Might as well keep it full-sized so we can better pack future
|
||||||
|
// textures.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_y = get_max_y();
|
||||||
|
assert(max_y > 0);
|
||||||
|
bool resized = false;
|
||||||
|
|
||||||
|
while (max_y <= _ysize / 2) {
|
||||||
|
// If at least half the palette is empty, we can try resizing.
|
||||||
|
if (max_y <= _ysize / 4) {
|
||||||
|
// Wow, three-quarters of the palette is empty.
|
||||||
|
Palette *np = try_resize(_xsize / 2, _ysize / 2);
|
||||||
|
if (np != NULL) {
|
||||||
|
// Excellent! We just reduced the palette size by half in
|
||||||
|
// both dimensions.
|
||||||
|
_texplace = np->_texplace;
|
||||||
|
_xsize = _xsize / 2;
|
||||||
|
_ysize = _ysize / 2;
|
||||||
|
_palette_changed = true;
|
||||||
|
delete np;
|
||||||
|
} else {
|
||||||
|
// Well, we couldn't reduce in both dimensions, but we can
|
||||||
|
// always reduce twice in the y dimension.
|
||||||
|
_ysize = _ysize / 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ysize = _ysize / 2;
|
||||||
|
}
|
||||||
|
resized = true;
|
||||||
|
max_y = get_max_y();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(get_max_y() <= _ysize);
|
||||||
|
|
||||||
|
if (resized) {
|
||||||
|
nout << "Resizing " << get_basename() << " to " << _xsize << " "
|
||||||
|
<< _ysize << "\n";
|
||||||
|
// If we've resized the palette, all the egg files that reference
|
||||||
|
// these textures will need to be rebuilt.
|
||||||
|
TexPlace::iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
(*ti)._texture->set_changed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we'll have to mark the palette as a new image.
|
||||||
|
_new_palette = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Palette::
|
||||||
|
finalize_palette() {
|
||||||
|
if (_filename.empty()) {
|
||||||
|
// Generate a filename based on the index number.
|
||||||
|
char index_str[128];
|
||||||
|
sprintf(index_str, "%03d", _index);
|
||||||
|
_basename = _attrib_file->_palette_prefix + index_str + ".rgb";
|
||||||
|
_filename = _basename;
|
||||||
|
_filename.set_dirname(_attrib_file->_map_dirname);
|
||||||
|
} else {
|
||||||
|
_basename = _filename.get_basename();
|
||||||
|
}
|
||||||
|
|
||||||
|
_components = check_uses_alpha() ? 4 : 3;
|
||||||
|
|
||||||
|
if (_texplace.size() == 1) {
|
||||||
|
// If we packed exactly one texture, never mind.
|
||||||
|
Texture *texture = (*_texplace.begin())._texture;
|
||||||
|
|
||||||
|
// This is a little odd: we mark the texture as being omitted, but
|
||||||
|
// we don't actually unpack it. That way it will still be
|
||||||
|
// recorded as belonging to this palette (for future
|
||||||
|
// palettizations), but it will also be copied to the map
|
||||||
|
// directory, and any egg files that reference it will use the
|
||||||
|
// texture and not the palette.
|
||||||
|
texture->set_omit(Texture::OR_solitary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Palette::
|
||||||
|
write(ostream &out) const {
|
||||||
|
out << "palette " << _filename
|
||||||
|
<< " size " << _xsize << " " << _ysize << " " << _components
|
||||||
|
<< "\n";
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
out << " " << (*ti)._texture->get_name()
|
||||||
|
<< " at " << (*ti)._left << " " << (*ti)._top
|
||||||
|
<< " size " << (*ti)._xsize << " " << (*ti)._ysize
|
||||||
|
<< " margin " << (*ti)._margin
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
generate_image() {
|
||||||
|
bool okflag = true;
|
||||||
|
|
||||||
|
PNMImage palette(_xsize, _ysize, _components);
|
||||||
|
|
||||||
|
nout << "Generating " << _filename << "\n";
|
||||||
|
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
Texture *texture = (*ti)._texture;
|
||||||
|
nout << " " << texture->get_name() << "\n";
|
||||||
|
okflag = copy_texture_image(palette, *ti) && okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
nout << "Writing " << _filename << "\n";
|
||||||
|
if (!palette.write(_filename)) {
|
||||||
|
nout << "Error in writing.\n";
|
||||||
|
okflag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
refresh_image() {
|
||||||
|
if (!_filename.exists()) {
|
||||||
|
nout << "Palette image " << _filename << " does not exist, rebuilding.\n";
|
||||||
|
return generate_image();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool okflag = true;
|
||||||
|
|
||||||
|
PNMImage palette;
|
||||||
|
if (!palette.read(_filename)) {
|
||||||
|
nout << "Unable to read old palette image " << _filename
|
||||||
|
<< ", rebuilding.\n";
|
||||||
|
return generate_image();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool any_changed = false;
|
||||||
|
|
||||||
|
if (_components == 4 && !palette.has_alpha()) {
|
||||||
|
palette.add_alpha();
|
||||||
|
palette.alpha_fill(1.0);
|
||||||
|
any_changed = true;
|
||||||
|
|
||||||
|
} else if (_components == 3 && palette.has_alpha()) {
|
||||||
|
palette.remove_alpha();
|
||||||
|
any_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
Texture *texture = (*ti)._texture;
|
||||||
|
if (texture->needs_refresh()) {
|
||||||
|
if (!any_changed) {
|
||||||
|
nout << "Refreshing " << _filename << "\n";
|
||||||
|
any_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nout << " " << texture->get_name() << "\n";
|
||||||
|
okflag = copy_texture_image(palette, *ti) && okflag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any_changed) {
|
||||||
|
nout << "Writing " << _filename << "\n";
|
||||||
|
if (!palette.write(_filename)) {
|
||||||
|
nout << "Error in writing.\n";
|
||||||
|
okflag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Palette *Palette::
|
||||||
|
try_resize(int new_xsize, int new_ysize) const {
|
||||||
|
Palette *np =
|
||||||
|
new Palette(_index, new_xsize, new_ysize,
|
||||||
|
_components, _attrib_file);
|
||||||
|
|
||||||
|
bool okflag = true;
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin();
|
||||||
|
ti != _texplace.end() && okflag;
|
||||||
|
++ti) {
|
||||||
|
okflag = np->pack_texture((*ti)._texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (okflag) {
|
||||||
|
return np;
|
||||||
|
} else {
|
||||||
|
delete np;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Palette::
|
||||||
|
get_max_y() const {
|
||||||
|
int max_y = 0;
|
||||||
|
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin(); ti != _texplace.end(); ++ti) {
|
||||||
|
max_y = max(max_y, (*ti)._top + (*ti)._ysize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
find_home(int &left, int &top, int xsize, int ysize) const {
|
||||||
|
top = 0;
|
||||||
|
while (top + ysize <= _ysize) {
|
||||||
|
int next_y = _ysize;
|
||||||
|
// Scan along the row at 'top'.
|
||||||
|
left = 0;
|
||||||
|
while (left + xsize <= _xsize) {
|
||||||
|
int next_x = left;
|
||||||
|
// Consider the spot at left, top.
|
||||||
|
|
||||||
|
// Can we place it here?
|
||||||
|
bool place_ok = true;
|
||||||
|
TexPlace::const_iterator ti;
|
||||||
|
for (ti = _texplace.begin();
|
||||||
|
ti != _texplace.end() && place_ok;
|
||||||
|
++ti) {
|
||||||
|
if ((*ti).intersects(left, top, xsize, ysize)) {
|
||||||
|
// Nope.
|
||||||
|
place_ok = false;
|
||||||
|
next_x = (*ti)._left + (*ti)._xsize;
|
||||||
|
next_y = min(next_y, (*ti)._top + (*ti)._ysize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (place_ok) {
|
||||||
|
// Hooray!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next_x > left);
|
||||||
|
left = next_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next_y > top);
|
||||||
|
top = next_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nope, wouldn't fit anywhere.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Palette::
|
||||||
|
copy_texture_image(PNMImage &palette, const TexturePlacement &tp) {
|
||||||
|
bool okflag = true;
|
||||||
|
PNMImage *image = tp._texture->read_image();
|
||||||
|
if (image == NULL) {
|
||||||
|
nout << " *** Unable to read " << tp._texture->get_name() << "\n";
|
||||||
|
okflag = false;
|
||||||
|
|
||||||
|
// Create a solid red texture for images we can't read.
|
||||||
|
image = new PNMImage(tp._xsize, tp._ysize);
|
||||||
|
image->fill(1.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
image = tp.add_margins(tp.resize_image(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_components == 4 && !image->has_alpha()) {
|
||||||
|
// We need to add an alpha channel for the image if the
|
||||||
|
// palette has an alpha channel.
|
||||||
|
image->add_alpha();
|
||||||
|
image->alpha_fill(1.0);
|
||||||
|
}
|
||||||
|
palette.copy_sub_image(*image, tp._left, tp._top);
|
||||||
|
delete image;
|
||||||
|
|
||||||
|
return okflag;
|
||||||
|
}
|
90
pandatool/src/egg-palettize/palette.h
Normal file
90
pandatool/src/egg-palettize/palette.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Filename: palette.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PALETTE_H
|
||||||
|
#define PALETTE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include "imageFile.h"
|
||||||
|
|
||||||
|
#include <filename.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Texture;
|
||||||
|
class PNMImage;
|
||||||
|
class AttribFile;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : Palette
|
||||||
|
// Description :
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
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();
|
||||||
|
|
||||||
|
virtual Filename get_filename() const;
|
||||||
|
virtual Filename get_basename() const;
|
||||||
|
|
||||||
|
bool changed() const;
|
||||||
|
bool new_palette() const;
|
||||||
|
int get_num_textures() const;
|
||||||
|
|
||||||
|
bool check_uses_alpha() const;
|
||||||
|
|
||||||
|
void get_size(int &xsize, int &ysize) const;
|
||||||
|
|
||||||
|
void place_texture_at(Texture *texture, int left, int top,
|
||||||
|
int xsize, int ysize, int margin);
|
||||||
|
|
||||||
|
bool pack_texture(Texture *texture);
|
||||||
|
bool unpack_texture(Texture *texture);
|
||||||
|
|
||||||
|
void optimal_resize();
|
||||||
|
|
||||||
|
void finalize_palette();
|
||||||
|
|
||||||
|
void write(ostream &out) const;
|
||||||
|
|
||||||
|
bool generate_image();
|
||||||
|
bool refresh_image();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class TexturePlacement {
|
||||||
|
public:
|
||||||
|
bool intersects(int left, int top, int xsize, int ysize) const;
|
||||||
|
PNMImage *resize_image(PNMImage *source) const;
|
||||||
|
PNMImage *add_margins(PNMImage *source) const;
|
||||||
|
|
||||||
|
Texture *_texture;
|
||||||
|
int _left, _top;
|
||||||
|
int _xsize, _ysize, _margin;
|
||||||
|
};
|
||||||
|
|
||||||
|
Palette *try_resize(int new_xsize, int new_ysize) const;
|
||||||
|
int get_max_y() const;
|
||||||
|
bool find_home(int &left, int &top, int xsize, int ysize) const;
|
||||||
|
bool copy_texture_image(PNMImage &palette, const TexturePlacement &tp);
|
||||||
|
|
||||||
|
|
||||||
|
typedef vector<TexturePlacement> TexPlace;
|
||||||
|
TexPlace _texplace;
|
||||||
|
|
||||||
|
Filename _filename;
|
||||||
|
Filename _basename;
|
||||||
|
int _index;
|
||||||
|
int _xsize, _ysize, _components;
|
||||||
|
bool _palette_changed;
|
||||||
|
bool _new_palette;
|
||||||
|
|
||||||
|
AttribFile *_attrib_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
347
pandatool/src/egg-palettize/sourceEgg.cxx
Normal file
347
pandatool/src/egg-palettize/sourceEgg.cxx
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
// Filename: sourceEgg.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "sourceEgg.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "eggPalettize.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "palette.h"
|
||||||
|
|
||||||
|
#include <eggAttributes.h>
|
||||||
|
#include <eggVertex.h>
|
||||||
|
#include <eggNurbsSurface.h>
|
||||||
|
#include <eggPrimitive.h>
|
||||||
|
#include <eggTextureCollection.h>
|
||||||
|
|
||||||
|
TypeHandle SourceEgg::_type_handle;
|
||||||
|
|
||||||
|
SourceEgg::TextureRef::
|
||||||
|
TextureRef(Texture *texture, bool repeats, bool alpha) :
|
||||||
|
_texture(texture),
|
||||||
|
_repeats(repeats),
|
||||||
|
_alpha(alpha)
|
||||||
|
{
|
||||||
|
_eggtex = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceEgg::
|
||||||
|
SourceEgg() {
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceEgg::TextureRef &SourceEgg::
|
||||||
|
add_texture(Texture *texture, bool repeats, bool alpha) {
|
||||||
|
_texrefs.push_back(TextureRef(texture, repeats, alpha));
|
||||||
|
return _texrefs.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SourceEgg::
|
||||||
|
get_textures(AttribFile &af, EggPalettize *prog) {
|
||||||
|
_texrefs.clear();
|
||||||
|
|
||||||
|
EggTextureCollection tc;
|
||||||
|
tc.find_used_textures(this);
|
||||||
|
|
||||||
|
EggTextureCollection::iterator ti;
|
||||||
|
for (ti = tc.begin(); ti != tc.end(); ++ti) {
|
||||||
|
EggTexture *eggtex = (*ti);
|
||||||
|
string name = eggtex->get_basename();
|
||||||
|
|
||||||
|
Texture *texture = af.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(Texture::OR_cmdline);
|
||||||
|
} else {
|
||||||
|
// Or until we next see it without -x.
|
||||||
|
if (texture->get_omit() == Texture::OR_cmdline) {
|
||||||
|
texture->set_omit(Texture::OR_none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool repeats =
|
||||||
|
eggtex->get_wrap_mode() == EggTexture::WM_repeat ||
|
||||||
|
eggtex->get_wrap_u() == EggTexture::WM_repeat ||
|
||||||
|
eggtex->get_wrap_v() == EggTexture::WM_repeat;
|
||||||
|
|
||||||
|
bool repeat_unspecified =
|
||||||
|
eggtex->get_wrap_mode() == EggTexture::WM_unspecified &&
|
||||||
|
eggtex->get_wrap_u() == EggTexture::WM_unspecified &&
|
||||||
|
eggtex->get_wrap_v() == EggTexture::WM_unspecified;
|
||||||
|
|
||||||
|
bool alpha = true; //eggtex->uses_alpha();
|
||||||
|
|
||||||
|
// Check the range of UV's actually used within the egg file.
|
||||||
|
bool any_uvs = false;
|
||||||
|
TexCoordd min_uv, max_uv;
|
||||||
|
get_uv_range(this, eggtex, any_uvs, min_uv, max_uv);
|
||||||
|
|
||||||
|
// Now we need to apply the texture matrix, if there is one, to
|
||||||
|
// our bounding box UV's.
|
||||||
|
|
||||||
|
if (eggtex->has_transform()) {
|
||||||
|
// Transforming a bounding box by a matrix requires transforming
|
||||||
|
// all four corners.
|
||||||
|
|
||||||
|
TexCoordd a(min_uv[0], min_uv[1]);
|
||||||
|
TexCoordd b(min_uv[0], max_uv[1]);
|
||||||
|
TexCoordd c(max_uv[0], max_uv[1]);
|
||||||
|
TexCoordd d(max_uv[0], min_uv[1]);
|
||||||
|
|
||||||
|
LMatrix3d transform = eggtex->get_transform();
|
||||||
|
|
||||||
|
a = a * transform;
|
||||||
|
b = b * transform;
|
||||||
|
c = c * transform;
|
||||||
|
d = d * transform;
|
||||||
|
|
||||||
|
// Now boil down these four corners into a new bounding box.
|
||||||
|
|
||||||
|
min_uv.set(min(min(a[0], b[0]), min(c[0], d[0])),
|
||||||
|
min(min(a[1], b[1]), min(c[1], d[1])));
|
||||||
|
max_uv.set(max(max(a[0], b[0]), max(c[0], d[0])),
|
||||||
|
max(max(a[1], b[1]), max(c[1], d[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool truly_repeats =
|
||||||
|
(max_uv[0] > 1.0 + prog->_fuzz_factor ||
|
||||||
|
min_uv[0] < 0.0 - prog->_fuzz_factor ||
|
||||||
|
max_uv[1] > 1.0 + prog->_fuzz_factor ||
|
||||||
|
min_uv[1] < 0.0 - prog->_fuzz_factor);
|
||||||
|
|
||||||
|
if (repeat_unspecified) {
|
||||||
|
// If the egg file didn't give us any advice regarding
|
||||||
|
// repeating, we can go entirely based on what we saw in the
|
||||||
|
// UV's.
|
||||||
|
repeats = truly_repeats;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (repeats && !truly_repeats) {
|
||||||
|
// The egg file specified that the texture repeats, but we
|
||||||
|
// discovered that it doesn't really. Quietly mark it so.
|
||||||
|
if (!prog->_dont_override_repeats) {
|
||||||
|
repeats = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!repeats && truly_repeats) {
|
||||||
|
// The egg file specified that the texture doesn't repeat, but
|
||||||
|
// its UV's were outside the normal range! That's almost
|
||||||
|
// certainly a modeling error.
|
||||||
|
|
||||||
|
// We won't trouble the user with this kind of spammy message,
|
||||||
|
// though.
|
||||||
|
/*
|
||||||
|
nout << "Warning: texture " << texture->get_name() << " (tref "
|
||||||
|
<< eggtex->name << ") marked clamped, but UV's range from ("
|
||||||
|
<< min_uv << ") to (" << max_uv << ")\n";
|
||||||
|
*/
|
||||||
|
|
||||||
|
} else if (repeats && truly_repeats) {
|
||||||
|
// Well, it really does repeat. Or does it?
|
||||||
|
if (fabs(max_uv[0] - min_uv[0]) <= 1.0 + prog->_fuzz_factor &&
|
||||||
|
fabs(max_uv[1] - min_uv[1]) <= 1.0 + prog->_fuzz_factor) {
|
||||||
|
// It really shouldn't! The UV's fit totally within a unit
|
||||||
|
// square, just not the square (0,0) - (1,1). This is a
|
||||||
|
// modeling problem; inform the user.
|
||||||
|
nout << "Warning: texture " << texture->get_name()
|
||||||
|
<< " cannot be clamped because UV's range from ("
|
||||||
|
<< min_uv << ") to (" << max_uv << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureRef &texref = add_texture(texture, repeats, alpha);
|
||||||
|
texref._eggtex = eggtex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates each Texture 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) {
|
||||||
|
Texture *texture = (*ti)._texture;
|
||||||
|
texture->set_unused(false);
|
||||||
|
if ((*ti)._alpha) {
|
||||||
|
texture->set_uses_alpha(true);
|
||||||
|
}
|
||||||
|
if ((*ti)._repeats) {
|
||||||
|
texture->set_omit(Texture::OR_repeats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
Texture *texture = (*ti)._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 (!texture->is_packed() ||
|
||||||
|
texture->get_omit() != Texture::OR_none) {
|
||||||
|
// This texture wasn't palettized, so just rename the
|
||||||
|
// reference to the new one.
|
||||||
|
eggtex->set_fullpath(texture->get_filename());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// This texture was palettized, so redirect the tref to point
|
||||||
|
// within the palette.
|
||||||
|
Palette *palette = texture->get_palette();
|
||||||
|
|
||||||
|
eggtex->set_fullpath(palette->get_filename());
|
||||||
|
|
||||||
|
// Set the texture attributes to be uniform across all palettes.
|
||||||
|
eggtex->set_minfilter(EggTexture::FT_mipmap_trilinear);
|
||||||
|
eggtex->set_magfilter(EggTexture::FT_bilinear);
|
||||||
|
eggtex->set_format(EggTexture::F_rgba8);
|
||||||
|
eggtex->set_wrap_mode(EggTexture::WM_clamp);
|
||||||
|
eggtex->set_wrap_u(EggTexture::WM_clamp);
|
||||||
|
eggtex->set_wrap_v(EggTexture::WM_clamp);
|
||||||
|
|
||||||
|
// Build a matrix that will scale the UV's to their new place
|
||||||
|
// on the palette.
|
||||||
|
int left, top, xsize, ysize, margin;
|
||||||
|
texture->get_packed_location(left, top);
|
||||||
|
texture->get_packed_size(xsize, ysize, margin);
|
||||||
|
|
||||||
|
// Shrink the box to be within the margins.
|
||||||
|
top += margin;
|
||||||
|
left += margin;
|
||||||
|
xsize -= margin*2;
|
||||||
|
ysize -= margin*2;
|
||||||
|
|
||||||
|
// Now determine the relative size and position within the
|
||||||
|
// palette.
|
||||||
|
int bottom = top + ysize;
|
||||||
|
int palx, paly;
|
||||||
|
palette->get_size(palx, paly);
|
||||||
|
LVecBase2d t((double)left / (double)palx,
|
||||||
|
(double)(paly - bottom) / (double)paly);
|
||||||
|
|
||||||
|
LVecBase2d s((double)xsize / (double)palx,
|
||||||
|
(double)ysize / (double)paly);
|
||||||
|
|
||||||
|
LMatrix3d texmat
|
||||||
|
(s[0], 0.0, 0.0,
|
||||||
|
0.0, s[1], 0.0,
|
||||||
|
t[0], t[1], 1.0);
|
||||||
|
|
||||||
|
// Do we already have a texture matrix? If so, compose them.
|
||||||
|
if (eggtex->has_transform()) {
|
||||||
|
eggtex->set_transform(eggtex->get_transform() * texmat);
|
||||||
|
} else {
|
||||||
|
// Otherwise, just store it.
|
||||||
|
eggtex->set_transform(texmat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if any of the textures referenced by the egg file have
|
||||||
|
// been adjusted this pass, implying that the egg file will have to be
|
||||||
|
// re-run through egg-palettize, and/or re-pfb'ed.
|
||||||
|
bool SourceEgg::
|
||||||
|
needs_rebuild(bool force_redo_all,
|
||||||
|
bool eggs_include_images) const {
|
||||||
|
// if (!_wrote_egg) {
|
||||||
|
TexRefs::const_iterator ti;
|
||||||
|
for (ti = _texrefs.begin(); ti != _texrefs.end(); ++ti) {
|
||||||
|
bool dirty =
|
||||||
|
eggs_include_images ?
|
||||||
|
(*ti)._texture->needs_refresh() :
|
||||||
|
(*ti)._texture->packing_changed();
|
||||||
|
if (force_redo_all || dirty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 << " repeats";
|
||||||
|
}
|
||||||
|
if ((*ti)._alpha) {
|
||||||
|
out << " alpha";
|
||||||
|
}
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SourceEgg::
|
||||||
|
get_uv_range(EggGroupNode *group, EggTexture *tref,
|
||||||
|
bool &any_uvs, TexCoordd &min_uv, TexCoordd &max_uv) {
|
||||||
|
EggGroupNode::iterator ci;
|
||||||
|
|
||||||
|
for (ci = group->begin(); ci != group->end(); ci++) {
|
||||||
|
EggNode *child = (*ci);
|
||||||
|
if (child->is_of_type(EggNurbsSurface::get_class_type())) {
|
||||||
|
EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, child);
|
||||||
|
if (nurbs->has_texture() && nurbs->get_texture() == tref) {
|
||||||
|
// Here's a NURBS surface that references the texture. Unlike
|
||||||
|
// other kinds of geometries, NURBS don't store UV's; they're
|
||||||
|
// implicit in the surface. NURBS UV's will always run in the
|
||||||
|
// range (0,0) - (1,1).
|
||||||
|
if (any_uvs) {
|
||||||
|
min_uv.set(min(min_uv[0], 0.0), min(min_uv[1], 0.0));
|
||||||
|
max_uv.set(max(max_uv[0], 1.0), max(max_uv[1], 1.0));
|
||||||
|
} else {
|
||||||
|
min_uv.set(0.0, 0.0);
|
||||||
|
max_uv.set(1.0, 1.0);
|
||||||
|
any_uvs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (child->is_of_type(EggPrimitive::get_class_type())) {
|
||||||
|
EggPrimitive *geom = DCAST(EggPrimitive, child);
|
||||||
|
if (geom->has_texture() && geom->get_texture() == tref) {
|
||||||
|
// Here's a piece of geometry that references this texture.
|
||||||
|
// Walk through its vertices at get its UV's.
|
||||||
|
EggPrimitive::iterator pi;
|
||||||
|
for (pi = geom->begin(); pi != geom->end(); ++pi) {
|
||||||
|
EggVertex *vtx = (*pi);
|
||||||
|
if (vtx->has_uv()) {
|
||||||
|
const TexCoordd &uv = vtx->get_uv();
|
||||||
|
if (any_uvs) {
|
||||||
|
min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
|
||||||
|
max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
|
||||||
|
} else {
|
||||||
|
// The first UV.
|
||||||
|
min_uv = uv;
|
||||||
|
max_uv = uv;
|
||||||
|
any_uvs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (child->is_of_type(EggGroupNode::get_class_type())) {
|
||||||
|
EggGroupNode *cg = DCAST(EggGroupNode, child);
|
||||||
|
get_uv_range(cg, tref, any_uvs, min_uv, max_uv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
pandatool/src/egg-palettize/sourceEgg.h
Normal file
75
pandatool/src/egg-palettize/sourceEgg.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Filename: sourceEgg.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef SOURCEGG_H
|
||||||
|
#define SOURCEGG_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <eggData.h>
|
||||||
|
#include <luse.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Texture;
|
||||||
|
class AttribFile;
|
||||||
|
class EggPalettize;
|
||||||
|
class EggTexture;
|
||||||
|
class EggGroup;
|
||||||
|
|
||||||
|
class SourceEgg : public EggData {
|
||||||
|
public:
|
||||||
|
class TextureRef;
|
||||||
|
|
||||||
|
SourceEgg();
|
||||||
|
|
||||||
|
TextureRef &add_texture(Texture *texture, bool repeats, bool alpha);
|
||||||
|
void get_textures(AttribFile &af, EggPalettize *prog);
|
||||||
|
|
||||||
|
void mark_texture_flags();
|
||||||
|
void update_trefs();
|
||||||
|
|
||||||
|
bool needs_rebuild(bool force_redo_all,
|
||||||
|
bool eggs_include_images) const;
|
||||||
|
|
||||||
|
void write_pi(ostream &out) const;
|
||||||
|
|
||||||
|
|
||||||
|
class TextureRef {
|
||||||
|
public:
|
||||||
|
TextureRef(Texture *texture, bool repeats, bool alpha);
|
||||||
|
|
||||||
|
Texture *_texture;
|
||||||
|
bool _repeats;
|
||||||
|
bool _alpha;
|
||||||
|
|
||||||
|
EggTexture *_eggtex;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void get_uv_range(EggGroupNode *group, EggTexture *tref,
|
||||||
|
bool &any_uvs, TexCoordd &min_uv, TexCoordd &max_uv);
|
||||||
|
|
||||||
|
typedef vector<TextureRef> TexRefs;
|
||||||
|
TexRefs _texrefs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TypeHandle get_class_type() {
|
||||||
|
return _type_handle;
|
||||||
|
}
|
||||||
|
static void init_type() {
|
||||||
|
EggData::init_type();
|
||||||
|
register_type(_type_handle, "SourceEgg",
|
||||||
|
EggData::get_class_type());
|
||||||
|
}
|
||||||
|
virtual TypeHandle get_type() const {
|
||||||
|
return get_class_type();
|
||||||
|
}
|
||||||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeHandle _type_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
80
pandatool/src/egg-palettize/string_utils.cxx
Normal file
80
pandatool/src/egg-palettize/string_utils.cxx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Filename: string_utils.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
string
|
||||||
|
trim_left(const string &str) {
|
||||||
|
size_t begin = 0;
|
||||||
|
while (begin < str.size() && isspace(str[begin])) {
|
||||||
|
begin++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substr(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
trim_right(const string &str) {
|
||||||
|
size_t begin = 0;
|
||||||
|
size_t end = str.size();
|
||||||
|
while (end > begin && isspace(str[end - 1])) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substr(begin, end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string>
|
||||||
|
extract_words(const string &str) {
|
||||||
|
vector<string> result;
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < str.length() && isspace(str[pos])) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
while (pos < str.length()) {
|
||||||
|
size_t word_start = pos;
|
||||||
|
while (pos < str.length() && !isspace(str[pos])) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
result.push_back(str.substr(word_start, pos - word_start));
|
||||||
|
|
||||||
|
while (pos < str.length() && isspace(str[pos])) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the first word of the string into param, and the remainder
|
||||||
|
// of the line into value.
|
||||||
|
void
|
||||||
|
extract_param_value(const string &str, string ¶m, string &value) {
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
// First, skip all whitespace at the beginning.
|
||||||
|
while (i < str.length() && isspace(str[i])) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t start = i;
|
||||||
|
|
||||||
|
// Now skip to the end of the whitespace.
|
||||||
|
while (i < str.length() && !isspace(str[i])) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t end = i;
|
||||||
|
|
||||||
|
param = str.substr(start, end - start);
|
||||||
|
|
||||||
|
// Skip a little bit further to the start of the value.
|
||||||
|
while (i < str.length() && isspace(str[i])) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
value = trim_right(str.substr(i));
|
||||||
|
}
|
20
pandatool/src/egg-palettize/string_utils.h
Normal file
20
pandatool/src/egg-palettize/string_utils.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Filename: string_utils.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef STRING_UTILS_H
|
||||||
|
#define STRING_UTILS_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
string trim_left(const string &str);
|
||||||
|
string trim_right(const string &str);
|
||||||
|
|
||||||
|
vector<string> extract_words(const string &str);
|
||||||
|
void extract_param_value(const string &str, string ¶m, string &value);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
586
pandatool/src/egg-palettize/texture.cxx
Normal file
586
pandatool/src/egg-palettize/texture.cxx
Normal file
@ -0,0 +1,586 @@
|
|||||||
|
// Filename: texture.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "texture.h"
|
||||||
|
#include "palette.h"
|
||||||
|
#include "attribFile.h"
|
||||||
|
|
||||||
|
#include <pnmImage.h>
|
||||||
|
#include <pnmReader.h>
|
||||||
|
|
||||||
|
|
||||||
|
Texture::
|
||||||
|
Texture(AttribFile *af, const Filename &name) :
|
||||||
|
_name(name),
|
||||||
|
_attrib_file(af)
|
||||||
|
{
|
||||||
|
_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;
|
||||||
|
_read_header = false;
|
||||||
|
_omit = OR_none;
|
||||||
|
_uses_alpha = false;
|
||||||
|
_is_packed = false;
|
||||||
|
_palette = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename Texture::
|
||||||
|
get_name() const {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
add_filename(const Filename &filename) {
|
||||||
|
if (!filename.exists()) {
|
||||||
|
// Store the filename, even though it doesn't exist.
|
||||||
|
if (!_got_filename) {
|
||||||
|
_got_filename = true;
|
||||||
|
_filename = filename;
|
||||||
|
_file_exists = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bool inserted = _filenames.insert(filename).second;
|
||||||
|
|
||||||
|
if (!_got_filename || !_file_exists) {
|
||||||
|
// This was the first filename we encountered; no sweat.
|
||||||
|
_got_filename = true;
|
||||||
|
_file_exists = true;
|
||||||
|
_filename = filename;
|
||||||
|
|
||||||
|
check_size();
|
||||||
|
|
||||||
|
} else if (inserted) {
|
||||||
|
// We had been using a different filename previously. Now we've
|
||||||
|
// got a new one. Maybe this one is better?
|
||||||
|
|
||||||
|
// First, read the headers. We'll consider the larger image to
|
||||||
|
// be the better choice.
|
||||||
|
if (!_got_size) {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
|
||||||
|
int oxsize, oysize;
|
||||||
|
bool got_other_size =
|
||||||
|
read_image_header(filename, oxsize, oysize);
|
||||||
|
|
||||||
|
if (got_other_size) {
|
||||||
|
if (!_got_size || oxsize * oysize > _xsize * _ysize) {
|
||||||
|
_filename = filename;
|
||||||
|
_xsize = oxsize;
|
||||||
|
_ysize = oysize;
|
||||||
|
_got_size = true;
|
||||||
|
_texture_changed = true;
|
||||||
|
|
||||||
|
} else if (oxsize * oysize == _xsize * _ysize) {
|
||||||
|
// If the sizes are the same, we'll consider the newer image
|
||||||
|
// to be the better choice.
|
||||||
|
if (filename.compare_timestamps(_filename, false, false) > 0) {
|
||||||
|
_filename = filename;
|
||||||
|
_texture_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename Texture::
|
||||||
|
get_filename() const {
|
||||||
|
Filename filename = _name;
|
||||||
|
filename.set_dirname(_attrib_file->_map_dirname);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename Texture::
|
||||||
|
get_basename() const {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
get_size(int &xsize, int &ysize) {
|
||||||
|
if (!_got_size) {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_got_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xsize = _xsize;
|
||||||
|
ysize = _ysize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_size(int xsize, int ysize) {
|
||||||
|
// If we've already read the file header, don't let anyone tell us
|
||||||
|
// differently.
|
||||||
|
if (!_read_header) {
|
||||||
|
_xsize = xsize;
|
||||||
|
_ysize = ysize;
|
||||||
|
_got_size = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
get_req(int &xsize, int &ysize) {
|
||||||
|
if (!_got_req) {
|
||||||
|
return get_size(xsize, ysize);
|
||||||
|
}
|
||||||
|
xsize = _req_xsize;
|
||||||
|
ysize = _req_ysize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
get_last_req(int &xsize, int &ysize) {
|
||||||
|
if (!_got_last_req) {
|
||||||
|
return get_size(xsize, ysize);
|
||||||
|
}
|
||||||
|
xsize = _last_req_xsize;
|
||||||
|
ysize = _last_req_ysize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_last_req(int xsize, int ysize) {
|
||||||
|
_last_req_xsize = xsize;
|
||||||
|
_last_req_ysize = ysize;
|
||||||
|
_got_last_req = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
reset_req(int xsize, int ysize) {
|
||||||
|
if (_got_last_req &&
|
||||||
|
(_last_req_xsize != xsize || _last_req_ysize != ysize)) {
|
||||||
|
// We've changed the requested size from the last time we ran.
|
||||||
|
// That constitutes a change to the texture.
|
||||||
|
_texture_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_req_xsize = xsize;
|
||||||
|
_req_ysize = ysize;
|
||||||
|
_got_req = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
scale_req(double scale_pct) {
|
||||||
|
if (!_got_size) {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
if (_got_size) {
|
||||||
|
reset_req(_xsize * scale_pct / 100.0,
|
||||||
|
_ysize * scale_pct / 100.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
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 Texture::
|
||||||
|
get_scale_pct() {
|
||||||
|
if (!_got_size) {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
if (_got_size && _got_req) {
|
||||||
|
return
|
||||||
|
100.0 * (((double)_req_xsize / (double)_xsize) +
|
||||||
|
((double)_req_ysize / (double)_ysize)) / 2.0;
|
||||||
|
} else {
|
||||||
|
return 100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::
|
||||||
|
get_margin() const {
|
||||||
|
return _margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_margin(int margin) {
|
||||||
|
_margin = margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::OmitReason Texture::
|
||||||
|
get_omit() const {
|
||||||
|
return _omit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_omit(OmitReason omit) {
|
||||||
|
_omit = omit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_changed(bool changed) {
|
||||||
|
_texture_changed = changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
unused() const {
|
||||||
|
return _unused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_unused(bool unused) {
|
||||||
|
_unused = unused;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
matched_anything() const {
|
||||||
|
return _matched_anything;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_matched_anything(bool matched_anything) {
|
||||||
|
_matched_anything = matched_anything;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
uses_alpha() const {
|
||||||
|
return _uses_alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
set_uses_alpha(bool uses_alpha) {
|
||||||
|
_uses_alpha = uses_alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
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 Texture::
|
||||||
|
mark_unpacked() {
|
||||||
|
_is_packed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
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 Texture::
|
||||||
|
is_really_packed() const {
|
||||||
|
return _is_packed && _omit != OR_solitary;
|
||||||
|
}
|
||||||
|
|
||||||
|
Palette *Texture::
|
||||||
|
get_palette() const {
|
||||||
|
return _is_packed ? _palette : (Palette *)NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
get_packed_location(int &left, int &top) const {
|
||||||
|
left = _pleft;
|
||||||
|
top = _ptop;
|
||||||
|
return _is_packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
get_packed_size(int &xsize, int &ysize, int &margin) const {
|
||||||
|
xsize = _pxsize;
|
||||||
|
ysize = _pysize;
|
||||||
|
margin = _pmargin;
|
||||||
|
return _is_packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
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 Texture::
|
||||||
|
write_size(ostream &out) {
|
||||||
|
if (_omit != OR_unused) {
|
||||||
|
if (!_got_size) {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
out << " " << _name;
|
||||||
|
if (_got_size) {
|
||||||
|
out << " orig " << _xsize << " " << _ysize;
|
||||||
|
}
|
||||||
|
if (_got_req) {
|
||||||
|
out << " new " << _req_xsize << " " << _req_ysize;
|
||||||
|
}
|
||||||
|
out << " " << get_scale_pct() << "%\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
write_pathname(ostream &out) const {
|
||||||
|
if (_got_filename && _omit != OR_unused) {
|
||||||
|
if (!_file_exists) {
|
||||||
|
nout << "Warning: texture " << _filename << " does not exist.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << " " << _name << " " << _filename << "\n";
|
||||||
|
|
||||||
|
// Also write out all the alternate filenames.
|
||||||
|
Filenames::const_iterator fi;
|
||||||
|
for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
|
||||||
|
if ((*fi) != _filename) {
|
||||||
|
// Indent by the same number of spaces to line up the filenames.
|
||||||
|
for (int i = 0; i < (int)_name.length() + 3; i++) {
|
||||||
|
out << ' ';
|
||||||
|
}
|
||||||
|
out << (*fi) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
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 Texture::
|
||||||
|
transfer() {
|
||||||
|
bool okflag = true;
|
||||||
|
|
||||||
|
Filename new_filename = get_filename();
|
||||||
|
if (new_filename == _filename) {
|
||||||
|
nout << "*** Texture " << _name << " is already in the map directory!\n"
|
||||||
|
<< " Cannot modify texture in place!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nx, ny;
|
||||||
|
if (!get_req(nx, ny)) {
|
||||||
|
nout << "Unknown size for image " << _name << "\n";
|
||||||
|
nx = 16;
|
||||||
|
ny = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_attrib_file->_force_power_2) {
|
||||||
|
int newx = to_power_2(nx);
|
||||||
|
int newy = to_power_2(ny);
|
||||||
|
if (newx != nx || newy != ny) {
|
||||||
|
nx = newx;
|
||||||
|
ny = newy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *image = read_image();
|
||||||
|
if (image == NULL) {
|
||||||
|
nout << "*** Unable to read " << _name << "\n";
|
||||||
|
okflag = false;
|
||||||
|
|
||||||
|
// Create a solid red texture for images we can't read.
|
||||||
|
image = new PNMImage(nx, ny);
|
||||||
|
image->fill(1.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Should we scale it?
|
||||||
|
if (nx != image->get_x_size() && ny != image->get_y_size()) {
|
||||||
|
nout << "Resizing " << new_filename << " to "
|
||||||
|
<< nx << " " << ny << "\n";
|
||||||
|
PNMImage *new_image =
|
||||||
|
new PNMImage(nx, ny, image->get_color_type());
|
||||||
|
new_image->gaussian_filter_from(0.5, *image);
|
||||||
|
delete image;
|
||||||
|
image = new_image;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nout << "Copying " << new_filename
|
||||||
|
<< " (size " << nx << " " << ny << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!image->write(new_filename)) {
|
||||||
|
nout << "Error in writing.\n";
|
||||||
|
okflag = false;
|
||||||
|
}
|
||||||
|
delete image;
|
||||||
|
|
||||||
|
return okflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *Texture::
|
||||||
|
read_image() {
|
||||||
|
if (!_got_filename || !_file_exists) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMImage *image = new PNMImage;
|
||||||
|
if (image->read(_filename)) {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hmm, it wasn't able to read the image successfully. Oh well.
|
||||||
|
delete image;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
check_size() {
|
||||||
|
// Make sure the file has the size it claims to have.
|
||||||
|
if (_got_size) {
|
||||||
|
int old_xsize = _xsize;
|
||||||
|
int old_ysize = _ysize;
|
||||||
|
read_header();
|
||||||
|
if (_xsize != old_xsize && _ysize != old_ysize) {
|
||||||
|
nout << "Source texture " << _name << " has changed size, from "
|
||||||
|
<< old_xsize << " " << old_ysize << " to "
|
||||||
|
<< _xsize << " " << _ysize << "\n";
|
||||||
|
_texture_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::
|
||||||
|
read_header() {
|
||||||
|
// Open the file and read its header to determine its size.
|
||||||
|
if (_got_filename && _file_exists) {
|
||||||
|
if (!_read_header) {
|
||||||
|
_read_header = true;
|
||||||
|
_got_size = read_image_header(_filename, _xsize, _ysize);
|
||||||
|
}
|
||||||
|
_read_header = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::
|
||||||
|
read_image_header(const Filename &filename, int &xsize, int &ysize) {
|
||||||
|
PNMImageHeader header;
|
||||||
|
if (!header.read_header(filename)) {
|
||||||
|
nout << "Warning: cannot read texture " << filename << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xsize = header.get_x_size();
|
||||||
|
ysize = header.get_y_size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::
|
||||||
|
to_power_2(int value) {
|
||||||
|
int x = 1;
|
||||||
|
while ((x << 1) <= value) {
|
||||||
|
x = (x << 1);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
131
pandatool/src/egg-palettize/texture.h
Normal file
131
pandatool/src/egg-palettize/texture.h
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Filename: texture.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef TEXTURE_H
|
||||||
|
#define TEXTURE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include "imageFile.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class Palette;
|
||||||
|
class PNMImage;
|
||||||
|
class AttribFile;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : Texture
|
||||||
|
// Description :
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class Texture : public ImageFile {
|
||||||
|
public:
|
||||||
|
enum OmitReason {
|
||||||
|
OR_none,
|
||||||
|
OR_size, OR_repeats, OR_omitted, OR_unused, OR_unknown,
|
||||||
|
OR_cmdline, OR_solitary
|
||||||
|
};
|
||||||
|
|
||||||
|
Texture(AttribFile *af, const Filename &name);
|
||||||
|
|
||||||
|
Filename get_name() const;
|
||||||
|
|
||||||
|
void add_filename(const Filename &filename);
|
||||||
|
|
||||||
|
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_req(int &xsize, int &ysize);
|
||||||
|
bool get_last_req(int &xsize, int &ysize);
|
||||||
|
void set_last_req(int xsize, int ysize);
|
||||||
|
void reset_req(int xsize, int ysize);
|
||||||
|
void scale_req(double scale_pct);
|
||||||
|
void clear_req();
|
||||||
|
double get_scale_pct();
|
||||||
|
|
||||||
|
int get_margin() const;
|
||||||
|
void set_margin(int margin);
|
||||||
|
|
||||||
|
OmitReason get_omit() const;
|
||||||
|
void set_omit(OmitReason omit);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
void write_size(ostream &out);
|
||||||
|
void write_pathname(ostream &out) const;
|
||||||
|
void write_unplaced(ostream &out) const;
|
||||||
|
|
||||||
|
bool transfer();
|
||||||
|
|
||||||
|
PNMImage *read_image();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void check_size();
|
||||||
|
void read_header();
|
||||||
|
bool read_image_header(const Filename &filename, int &xsize, int &ysize);
|
||||||
|
static int to_power_2(int value);
|
||||||
|
|
||||||
|
Filename _name;
|
||||||
|
|
||||||
|
typedef set<Filename> Filenames;
|
||||||
|
Filenames _filenames;
|
||||||
|
|
||||||
|
bool _got_filename;
|
||||||
|
Filename _filename;
|
||||||
|
bool _file_exists;
|
||||||
|
bool _texture_changed;
|
||||||
|
bool _unused;
|
||||||
|
bool _matched_anything;
|
||||||
|
bool _uses_alpha;
|
||||||
|
|
||||||
|
bool _got_size;
|
||||||
|
int _xsize, _ysize;
|
||||||
|
|
||||||
|
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 _read_header;
|
||||||
|
|
||||||
|
AttribFile *_attrib_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
362
pandatool/src/egg-palettize/userAttribLine.cxx
Normal file
362
pandatool/src/egg-palettize/userAttribLine.cxx
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
// Filename: userAttribLine.cxx
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "userAttribLine.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "attribFile.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) {
|
||||||
|
_is_old_style = false;
|
||||||
|
|
||||||
|
// By default, all lines are marked 'was_used'. That means they'll
|
||||||
|
// be preserved across a -k on the command line. Lines that name
|
||||||
|
// textures will have _was_used set false until a texture actually
|
||||||
|
// matches that line.
|
||||||
|
_was_used = true;
|
||||||
|
|
||||||
|
string line = cline;
|
||||||
|
|
||||||
|
// First, strip off the comment.
|
||||||
|
if (!line.empty()) {
|
||||||
|
if (line[0] == '#') {
|
||||||
|
_comment = line;
|
||||||
|
line = "";
|
||||||
|
} else {
|
||||||
|
size_t pos = line.find(" #");
|
||||||
|
if (pos != string::npos) {
|
||||||
|
while (pos > 0 && isspace(line[pos])) {
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
_comment = line.substr(pos + 1);
|
||||||
|
line = line.substr(0, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, analyze the line.
|
||||||
|
_line_type = LT_invalid;
|
||||||
|
_xsize = 0;
|
||||||
|
_ysize = 0;
|
||||||
|
_scale_pct = 0.0;
|
||||||
|
_msize = -1;
|
||||||
|
_omit = false;
|
||||||
|
|
||||||
|
bool is_valid = true;
|
||||||
|
if (line.empty()) {
|
||||||
|
_line_type = LT_comment;
|
||||||
|
|
||||||
|
} else if (line[0] == ':') {
|
||||||
|
is_valid = keyword_line(line);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
is_valid = texture_line(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_valid) {
|
||||||
|
_line_type = LT_invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserAttribLine::
|
||||||
|
~UserAttribLine() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
is_valid() const {
|
||||||
|
return _line_type != LT_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
is_old_style() const {
|
||||||
|
return _is_old_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
was_used() const {
|
||||||
|
return _was_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserAttribLine::
|
||||||
|
write(ostream &out) const {
|
||||||
|
switch (_line_type) {
|
||||||
|
case LT_invalid:
|
||||||
|
out << "*** invalid line ***\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_comment:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_margin:
|
||||||
|
out << ":margin " << _msize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_palette:
|
||||||
|
out << ":palette " << _xsize << " " << _ysize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_size:
|
||||||
|
list_textures(out) << " : " << _xsize << " " << _ysize;
|
||||||
|
if (_msize > 0) {
|
||||||
|
out << " " << _msize;
|
||||||
|
}
|
||||||
|
if (_omit) {
|
||||||
|
out << " omit";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_scale:
|
||||||
|
list_textures(out) << " : " << _scale_pct << "%";
|
||||||
|
if (_msize > 0) {
|
||||||
|
out << " " << _msize;
|
||||||
|
}
|
||||||
|
if (_omit) {
|
||||||
|
out << " omit";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LT_name:
|
||||||
|
list_textures(out) << " :";
|
||||||
|
if (_omit) {
|
||||||
|
out << " omit";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
nout << "Unexpected type: " << (int)_line_type << "\n";
|
||||||
|
abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
out << _comment << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
match_texture(Texture *texture, int &margin) {
|
||||||
|
// See if the texture name matches any of the filename patterns on
|
||||||
|
// this line.
|
||||||
|
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) {
|
||||||
|
matched_any = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched_any) {
|
||||||
|
_was_used = true;
|
||||||
|
|
||||||
|
// It does! So do the right thing with this line.
|
||||||
|
switch (_line_type) {
|
||||||
|
case LT_invalid:
|
||||||
|
case LT_comment:
|
||||||
|
case LT_palette:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case LT_margin:
|
||||||
|
margin = _msize;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case LT_size:
|
||||||
|
texture->reset_req(_xsize, _ysize);
|
||||||
|
texture->set_margin(_msize < 0 ? margin : _msize);
|
||||||
|
if (_omit) {
|
||||||
|
texture->set_omit(Texture::OR_omitted);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LT_scale:
|
||||||
|
texture->scale_req(_scale_pct);
|
||||||
|
texture->set_margin(_msize < 0 ? margin : _msize);
|
||||||
|
if (_omit) {
|
||||||
|
texture->set_omit(Texture::OR_omitted);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LT_name:
|
||||||
|
if (_omit) {
|
||||||
|
texture->set_omit(Texture::OR_omitted);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
nout << "Unexpected type: " << (int)_line_type << "\n";
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
keyword_line(const string &line) {
|
||||||
|
vector<string> words = extract_words(line);
|
||||||
|
assert(!words.empty());
|
||||||
|
|
||||||
|
if (words[0] == ":margin") {
|
||||||
|
_line_type = LT_margin;
|
||||||
|
if (words.size() != 2) {
|
||||||
|
nout << "Expected margin size.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_msize = atoi(words[1].c_str());
|
||||||
|
|
||||||
|
} else if (words[0] == ":palette") {
|
||||||
|
_line_type = LT_palette;
|
||||||
|
if (words.size() != 3) {
|
||||||
|
nout << "Expected xsize ysize of palette.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_xsize = atoi(words[1].c_str());
|
||||||
|
_ysize = atoi(words[2].c_str());
|
||||||
|
_attrib_file->_pal_xsize = _xsize;
|
||||||
|
_attrib_file->_pal_ysize = _ysize;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nout << "Unknown keyword: " << words[0] << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
texture_line(const string &line) {
|
||||||
|
_was_used = false;
|
||||||
|
|
||||||
|
// Scan for a colon followed by a space.
|
||||||
|
|
||||||
|
size_t colon = line.find(": ");
|
||||||
|
if (colon == string::npos) {
|
||||||
|
return old_style_line(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the line into two parts at the colon.
|
||||||
|
vector<string> names = extract_words(line.substr(0, colon));
|
||||||
|
vector<string> params = extract_words(line.substr(colon + 2));
|
||||||
|
|
||||||
|
vector<string>::const_iterator ni;
|
||||||
|
for (ni = names.begin(); ni != names.end(); ++ni) {
|
||||||
|
_texture_names.push_back(TextureName(*ni));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.empty() && params[params.size() - 1] == "omit") {
|
||||||
|
// If the last word is "omit", set the omit flag and remove the
|
||||||
|
// word.
|
||||||
|
_omit = true;
|
||||||
|
params.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.empty()) {
|
||||||
|
_line_type = LT_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it a percentage?
|
||||||
|
if (!params[0].empty() && params[0][params[0].size() - 1] == '%') {
|
||||||
|
_line_type = LT_scale;
|
||||||
|
_scale_pct = atof(params[0].c_str());
|
||||||
|
if (_scale_pct <= 0.0) {
|
||||||
|
nout << "Invalid scale percentage for ";
|
||||||
|
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
|
||||||
|
nout << ": " << _scale_pct << "%\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It must be xsize ysize [margin]
|
||||||
|
if (params.size() == 2) {
|
||||||
|
_line_type = LT_size;
|
||||||
|
_xsize = atoi(params[0].c_str());
|
||||||
|
_ysize = atoi(params[1].c_str());
|
||||||
|
if (_xsize <= 0 || _ysize <= 0) {
|
||||||
|
nout << "Invalid texture size for ";
|
||||||
|
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
|
||||||
|
nout << ": " << _xsize << " " << _ysize << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() == 3) {
|
||||||
|
_line_type = LT_size;
|
||||||
|
_xsize = atoi(params[0].c_str());
|
||||||
|
_ysize = atoi(params[1].c_str());
|
||||||
|
_msize = atoi(params[2].c_str());
|
||||||
|
if (_xsize <= 0 || _ysize <= 0) {
|
||||||
|
nout << "Invalid texture size for ";
|
||||||
|
copy(names.begin(), names.end(), ostream_iterator<string>(nout, " "));
|
||||||
|
nout << ": " << _xsize << " " << _ysize << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nout << "Expected xsize ysize [msize]\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool UserAttribLine::
|
||||||
|
old_style_line(const string &line) {
|
||||||
|
vector<string> words = extract_words(line);
|
||||||
|
assert(!words.empty());
|
||||||
|
|
||||||
|
if (words.size() != 3 && words.size() != 4) {
|
||||||
|
nout << "Colon omitted.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_old_style = true;
|
||||||
|
_line_type = LT_size;
|
||||||
|
_texture_names.push_back(TextureName(words[0]));
|
||||||
|
_xsize = atoi(words[1].c_str());
|
||||||
|
_ysize = atoi(words[2].c_str());
|
||||||
|
if (words.size() > 3) {
|
||||||
|
_msize = atoi(words[3].c_str());
|
||||||
|
|
||||||
|
if (_msize < 0) {
|
||||||
|
_msize = -1;
|
||||||
|
_omit = true;
|
||||||
|
|
||||||
|
} else if (_msize == _attrib_file->_default_margin) {
|
||||||
|
_msize = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_msize = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_xsize <= 0 || _ysize <= 0) {
|
||||||
|
nout << "Invalid texture size for " << words[0] << ": "
|
||||||
|
<< _xsize << " " << _ysize << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
86
pandatool/src/egg-palettize/userAttribLine.h
Normal file
86
pandatool/src/egg-palettize/userAttribLine.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Filename: userAttribLine.h
|
||||||
|
// Created by: drose (02Sep99)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef USERATTRIBLINE_H
|
||||||
|
#define USERATTRIBLINE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class AttribFile;
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// This might be a line of any of the following forms:
|
||||||
|
//
|
||||||
|
// (blank line)
|
||||||
|
// # Comment
|
||||||
|
// :margin msize
|
||||||
|
// :palette xsize ysize
|
||||||
|
// texturename xsize ysize msize
|
||||||
|
// texturename [texturename ...] : xsize ysize [msize] [omit]
|
||||||
|
// texturename [texturename ...] : scale% [msize] [omit]
|
||||||
|
// texturename [texturename ...] : [omit]
|
||||||
|
//
|
||||||
|
|
||||||
|
class UserAttribLine {
|
||||||
|
public:
|
||||||
|
UserAttribLine(const string &line, AttribFile *af);
|
||||||
|
~UserAttribLine();
|
||||||
|
|
||||||
|
bool is_valid() const;
|
||||||
|
bool is_old_style() const;
|
||||||
|
bool was_used() const;
|
||||||
|
|
||||||
|
void write(ostream &out) const;
|
||||||
|
|
||||||
|
bool match_texture(Texture *texture, int &margin);
|
||||||
|
|
||||||
|
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 ©) :
|
||||||
|
_pattern(copy._pattern) { }
|
||||||
|
|
||||||
|
string _pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef vector<TextureName> TextureNames;
|
||||||
|
TextureNames _texture_names;
|
||||||
|
|
||||||
|
ostream &list_textures(ostream &out) const;
|
||||||
|
bool keyword_line(const string &line);
|
||||||
|
bool texture_line(const string &line);
|
||||||
|
bool old_style_line(const string &line);
|
||||||
|
|
||||||
|
string _comment;
|
||||||
|
LineType _line_type;
|
||||||
|
int _xsize, _ysize;
|
||||||
|
double _scale_pct;
|
||||||
|
int _msize;
|
||||||
|
bool _omit;
|
||||||
|
bool _is_old_style;
|
||||||
|
bool _was_used;
|
||||||
|
|
||||||
|
AttribFile *_attrib_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
#define SOURCES \
|
#define SOURCES \
|
||||||
eggBase.cxx eggBase.h eggConverter.cxx eggConverter.h eggFilter.cxx \
|
eggBase.cxx eggBase.h eggConverter.cxx eggConverter.h eggFilter.cxx \
|
||||||
eggFilter.h eggReader.cxx eggReader.h eggToSomething.cxx \
|
eggFilter.h eggMultiBase.cxx eggMultiBase.h \
|
||||||
|
eggMultiFilter.cxx eggMultiFilter.h \
|
||||||
|
eggReader.cxx eggReader.h eggToSomething.cxx \
|
||||||
eggToSomething.h eggWriter.cxx eggWriter.h somethingToEgg.cxx \
|
eggToSomething.h eggWriter.cxx eggWriter.h somethingToEgg.cxx \
|
||||||
somethingToEgg.h
|
somethingToEgg.h
|
||||||
|
|
||||||
#define INSTALL_HEADERS \
|
#define INSTALL_HEADERS \
|
||||||
eggBase.h eggConverter.h eggFilter.h eggReader.h eggToSomething.h \
|
eggBase.h eggConverter.h eggFilter.h eggMultiBase.h eggMultiFilter.h \
|
||||||
eggWriter.h somethingToEgg.h
|
eggReader.h eggToSomething.h eggWriter.h somethingToEgg.h
|
||||||
|
|
||||||
#end ss_lib_target
|
#end ss_lib_target
|
||||||
|
|
||||||
|
@ -89,5 +89,5 @@ append_command_comment(EggData &data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.insert(_data.begin(), new EggComment("", comment));
|
data.insert(data.begin(), new EggComment("", comment));
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
class EggFilter : public EggReader, public EggWriter {
|
class EggFilter : public EggReader, public EggWriter {
|
||||||
public:
|
public:
|
||||||
EggFilter(bool allow_last_param = false, bool allow_stdout = true);
|
EggFilter(bool allow_last_param = false, bool allow_stdout = true);
|
||||||
|
|
||||||
|
protected:
|
||||||
virtual bool handle_args(Args &args);
|
virtual bool handle_args(Args &args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
104
pandatool/src/eggbase/eggMultiBase.cxx
Normal file
104
pandatool/src/eggbase/eggMultiBase.cxx
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Filename: eggMultiBase.cxx
|
||||||
|
// Created by: drose (02Nov00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "eggMultiBase.h"
|
||||||
|
|
||||||
|
#include <eggData.h>
|
||||||
|
#include <eggComment.h>
|
||||||
|
#include <filename.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiBase::Constructor
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
EggMultiBase::
|
||||||
|
EggMultiBase() {
|
||||||
|
add_option
|
||||||
|
("cs", "coordinate-system", 80,
|
||||||
|
"Specify the coordinate system to operate in. This may be one of "
|
||||||
|
"'y-up', 'z-up', 'y-up-left', or 'z-up-left'.",
|
||||||
|
&EggMultiBase::dispatch_coordinate_system,
|
||||||
|
&_got_coordinate_system, &_coordinate_system);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiBase::append_command_comment
|
||||||
|
// Access: Protected
|
||||||
|
// Description: Inserts a comment into the beginning of the indicated
|
||||||
|
// egg file corresponding to the command line that
|
||||||
|
// invoked this program.
|
||||||
|
//
|
||||||
|
// Normally this function is called automatically when
|
||||||
|
// appropriate by EggMultiFilter, and it's not necessary
|
||||||
|
// to call it explicitly.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void EggMultiBase::
|
||||||
|
append_command_comment(EggData &data) {
|
||||||
|
string comment;
|
||||||
|
|
||||||
|
comment = _program_name;
|
||||||
|
Args::const_iterator ai;
|
||||||
|
for (ai = _program_args.begin(); ai != _program_args.end(); ++ai) {
|
||||||
|
const string &arg = (*ai);
|
||||||
|
|
||||||
|
// First, check to see if the string is shell-acceptable.
|
||||||
|
bool legal = true;
|
||||||
|
string::const_iterator si;
|
||||||
|
for (si = arg.begin(); legal && si != arg.end(); ++si) {
|
||||||
|
switch (*si) {
|
||||||
|
case ' ':
|
||||||
|
case '\n':
|
||||||
|
case '\t':
|
||||||
|
case '*':
|
||||||
|
case '?':
|
||||||
|
case '\\':
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
case '|':
|
||||||
|
case '&':
|
||||||
|
case '<':
|
||||||
|
case '>':
|
||||||
|
case '"':
|
||||||
|
case ';':
|
||||||
|
case '$':
|
||||||
|
legal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (legal) {
|
||||||
|
comment += " " + arg;
|
||||||
|
} else {
|
||||||
|
comment += " '" + arg + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.insert(data.begin(), new EggComment("", comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiBase::read_egg
|
||||||
|
// Access: Protected, Virtual
|
||||||
|
// Description: Allocates and returns a new EggData structure that
|
||||||
|
// represents the indicated egg file. If the egg file
|
||||||
|
// cannot be read for some reason, returns NULL.
|
||||||
|
//
|
||||||
|
// This can be overridden by derived classes to control
|
||||||
|
// how the egg files are read, or to extend the
|
||||||
|
// information stored with each egg structure, by
|
||||||
|
// deriving from EggData.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
EggData *EggMultiBase::
|
||||||
|
read_egg(const Filename &filename) {
|
||||||
|
EggData *data = new EggData;
|
||||||
|
if (data->read(filename)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failure reading.
|
||||||
|
delete data;
|
||||||
|
return (EggData *)NULL;
|
||||||
|
}
|
45
pandatool/src/eggbase/eggMultiBase.h
Normal file
45
pandatool/src/eggbase/eggMultiBase.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Filename: eggMultiBase.h
|
||||||
|
// Created by: drose (02Nov00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef EGGMULTIBASE_H
|
||||||
|
#define EGGMULTIBASE_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include <programBase.h>
|
||||||
|
#include <coordinateSystem.h>
|
||||||
|
|
||||||
|
class Filename;
|
||||||
|
class EggData;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : EggMultiBase
|
||||||
|
// Description : This specialization of ProgramBase is intended for
|
||||||
|
// programs that read and/or write multiple egg files.
|
||||||
|
//
|
||||||
|
// See also EggMultiFilter, for a class that also knows
|
||||||
|
// how to read a bunch of egg files in and write them
|
||||||
|
// out again.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class EggMultiBase : public ProgramBase {
|
||||||
|
public:
|
||||||
|
EggMultiBase();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void append_command_comment(EggData &_data);
|
||||||
|
|
||||||
|
virtual EggData *read_egg(const Filename &filename);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _got_coordinate_system;
|
||||||
|
CoordinateSystem _coordinate_system;
|
||||||
|
|
||||||
|
typedef vector<EggData *> Eggs;
|
||||||
|
Eggs _eggs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
160
pandatool/src/eggbase/eggMultiFilter.cxx
Normal file
160
pandatool/src/eggbase/eggMultiFilter.cxx
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Filename: eggMultiFilter.cxx
|
||||||
|
// Created by: drose (02Nov00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "eggMultiFilter.h"
|
||||||
|
|
||||||
|
#include <notify.h>
|
||||||
|
#include <eggData.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiFilter::Constructor
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
EggMultiFilter::
|
||||||
|
EggMultiFilter(bool allow_empty) : _allow_empty(allow_empty) {
|
||||||
|
clear_runlines();
|
||||||
|
add_runline("-o output.egg [opts] input.egg");
|
||||||
|
add_runline("-d dirname [opts] file.egg [file.egg ...]");
|
||||||
|
add_runline("-inplace [opts] file.egg [file.egg ...]");
|
||||||
|
|
||||||
|
add_option
|
||||||
|
("o", "filename", 50,
|
||||||
|
"Specify the filename to which the resulting egg file will be written. "
|
||||||
|
"This is only valid when there is only one input egg file on the command "
|
||||||
|
"line. If you want to process multiple files simultaneously, you must "
|
||||||
|
"use either -d or -inplace.",
|
||||||
|
&EggMultiFilter::dispatch_filename, &_got_output_filename, &_output_filename);
|
||||||
|
|
||||||
|
add_option
|
||||||
|
("d", "dirname", 50,
|
||||||
|
"Specify the name of the directory in which to write the resulting egg "
|
||||||
|
"files. If you are processing only one egg file, this may be omitted "
|
||||||
|
"in lieu of the -o option. If you are processing multiple egg files, "
|
||||||
|
"this may be omitted only if you specify -inplace instead.",
|
||||||
|
&EggMultiFilter::dispatch_filename, &_got_output_dirname, &_output_dirname);
|
||||||
|
|
||||||
|
add_option
|
||||||
|
("inplace", "", 50,
|
||||||
|
"If this option is given, the input files will be rewritten in place with "
|
||||||
|
"the results. This obviates the need to specify -d for an output "
|
||||||
|
"directory; however, it's risky because the original input "
|
||||||
|
"files are lost.",
|
||||||
|
&EggMultiFilter::dispatch_none, &_inplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiFilter::handle_args
|
||||||
|
// Access: Protected, Virtual
|
||||||
|
// Description: Does something with the additional arguments on the
|
||||||
|
// command line (after all the -options have been
|
||||||
|
// parsed). Returns true if the arguments are good,
|
||||||
|
// false otherwise.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool EggMultiFilter::
|
||||||
|
handle_args(ProgramBase::Args &args) {
|
||||||
|
if (args.empty()) {
|
||||||
|
if (!_allow_empty) {
|
||||||
|
nout << "You must specify the egg file(s) to read on the command line.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// These only apply if we have specified any egg files.
|
||||||
|
|
||||||
|
if (_got_output_filename && args.size() == 1) {
|
||||||
|
if (_got_output_dirname) {
|
||||||
|
nout << "Cannot specify both -o and -d.\n";
|
||||||
|
return false;
|
||||||
|
} else if (_inplace) {
|
||||||
|
nout << "Cannot specify both -o and -inplace.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (_got_output_filename) {
|
||||||
|
nout << "Cannot use -o when multiple egg files are specified.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_got_output_dirname && _inplace) {
|
||||||
|
nout << "Cannot specify both -inplace and -d.\n";
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if (!_got_output_dirname && !_inplace) {
|
||||||
|
nout << "You must specify either -inplace or -d.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Args::const_iterator ai;
|
||||||
|
for (ai = args.begin(); ai != args.end(); ++ai) {
|
||||||
|
EggData *data = read_egg(*ai);
|
||||||
|
if (data == (EggData *)NULL) {
|
||||||
|
// Rather than returning false, we simply exit here, so the
|
||||||
|
// ProgramBase won't try to tell the user how to run the program
|
||||||
|
// just because we got a bad egg file.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_eggs.push_back(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiFilter::post_command_line
|
||||||
|
// Access: Protected, Virtual
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool EggMultiFilter::
|
||||||
|
post_command_line() {
|
||||||
|
Eggs::iterator ei;
|
||||||
|
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
|
||||||
|
EggData *data = (*ei);
|
||||||
|
if (_got_coordinate_system) {
|
||||||
|
data->set_coordinate_system(_coordinate_system);
|
||||||
|
}
|
||||||
|
append_command_comment(*data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EggMultiBase::post_command_line();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: EggMultiFilter::write_eggs
|
||||||
|
// Access: Protected
|
||||||
|
// Description: Writes out all of the egg files in the _eggs vector,
|
||||||
|
// to the output directory if one is specified, or over
|
||||||
|
// the input files if -inplace was specified.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void EggMultiFilter::
|
||||||
|
write_eggs() {
|
||||||
|
Eggs::iterator ei;
|
||||||
|
for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
|
||||||
|
EggData *data = (*ei);
|
||||||
|
Filename filename = data->get_egg_filename();
|
||||||
|
|
||||||
|
if (_got_output_filename) {
|
||||||
|
nassertv(!_inplace && !_got_output_dirname && _eggs.size() == 1);
|
||||||
|
filename = _output_filename;
|
||||||
|
|
||||||
|
} else if (_got_output_dirname) {
|
||||||
|
nassertv(!_inplace);
|
||||||
|
filename.set_dirname(_output_dirname);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nassertv(_inplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->write_egg(filename)) {
|
||||||
|
// Error writing an egg file; abort.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
pandatool/src/eggbase/eggMultiFilter.h
Normal file
40
pandatool/src/eggbase/eggMultiFilter.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Filename: eggMultiFilter.h
|
||||||
|
// Created by: drose (02Nov00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef EGGMULTIFILTER_H
|
||||||
|
#define EGGMULTIFILTER_H
|
||||||
|
|
||||||
|
#include <pandatoolbase.h>
|
||||||
|
|
||||||
|
#include "eggMultiBase.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : EggMultiFilter
|
||||||
|
// Description : This is a base class for a program that reads in a
|
||||||
|
// number of egg files, operates on them, and writes
|
||||||
|
// them out again (presumably to a different directory).
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class EggMultiFilter : public EggMultiBase {
|
||||||
|
public:
|
||||||
|
EggMultiFilter(bool allow_empty = false);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool handle_args(Args &args);
|
||||||
|
virtual bool post_command_line();
|
||||||
|
|
||||||
|
void write_eggs();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _allow_empty;
|
||||||
|
bool _got_output_filename;
|
||||||
|
Filename _output_filename;
|
||||||
|
bool _got_output_dirname;
|
||||||
|
Filename _output_dirname;
|
||||||
|
bool _inplace;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
EggTrans::
|
EggTrans::
|
||||||
EggTrans() {
|
EggTrans() {
|
||||||
set_program_description
|
set_program_description
|
||||||
("This program reads an egg file and writes an essentially equivalent "
|
("egg-trans reads an egg file and writes an essentially equivalent "
|
||||||
"egg file to the standard output, or to the file specified with -o. "
|
"egg file to the standard output, or to the file specified with -o. "
|
||||||
"Some simple operations on the egg file are supported.");
|
"Some simple operations on the egg file are supported.");
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ FltHeader() : FltBeadID(this) {
|
|||||||
|
|
||||||
_vertex_lookups_stale = false;
|
_vertex_lookups_stale = false;
|
||||||
_current_vertex_offset = 0;
|
_current_vertex_offset = 0;
|
||||||
|
_got_color_palette = false;
|
||||||
_got_eyepoint_trackplane_palette = false;
|
_got_eyepoint_trackplane_palette = false;
|
||||||
|
|
||||||
_auto_attr_update = AU_if_missing;
|
_auto_attr_update = AU_if_missing;
|
||||||
@ -1264,6 +1265,11 @@ extract_color_palette(FltRecordReader &reader) {
|
|||||||
nassertr(reader.get_opcode() == FO_color_palette, false);
|
nassertr(reader.get_opcode() == FO_color_palette, false);
|
||||||
DatagramIterator &iterator = reader.get_iterator();
|
DatagramIterator &iterator = reader.get_iterator();
|
||||||
|
|
||||||
|
if (_got_color_palette) {
|
||||||
|
nout << "Warning: multiple color palettes found.\n";
|
||||||
|
}
|
||||||
|
_got_color_palette = true;
|
||||||
|
|
||||||
static const int expected_color_entries = 1024;
|
static const int expected_color_entries = 1024;
|
||||||
|
|
||||||
iterator.skip_bytes(128);
|
iterator.skip_bytes(128);
|
||||||
@ -1559,7 +1565,7 @@ write_eyepoint_palette(FltRecordWriter &writer) const {
|
|||||||
return FE_ok;
|
return FE_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.set_opcode(FO_color_palette);
|
writer.set_opcode(FO_eyepoint_palette);
|
||||||
Datagram &datagram = writer.update_datagram();
|
Datagram &datagram = writer.update_datagram();
|
||||||
datagram.pad_bytes(4);
|
datagram.pad_bytes(4);
|
||||||
|
|
||||||
|
@ -236,6 +236,7 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
// Support for the color palette.
|
// Support for the color palette.
|
||||||
|
bool _got_color_palette;
|
||||||
typedef vector<FltPackedColor> Colors;
|
typedef vector<FltPackedColor> Colors;
|
||||||
typedef map<int, string> ColorNames;
|
typedef map<int, string> ColorNames;
|
||||||
Colors _colors;
|
Colors _colors;
|
||||||
|
@ -141,6 +141,11 @@ copy_flt_file(const Filename &source, const Filename &dest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove all the textures from the palette, and then add back only
|
||||||
|
// those we found in use. This way we don't copy a file that
|
||||||
|
// references bogus textures.
|
||||||
|
header->clear_textures();
|
||||||
|
|
||||||
Textures::const_iterator ti;
|
Textures::const_iterator ti;
|
||||||
for (ti = textures.begin(); ti != textures.end(); ++ti) {
|
for (ti = textures.begin(); ti != textures.end(); ++ti) {
|
||||||
FltTexture *tex = (*ti);
|
FltTexture *tex = (*ti);
|
||||||
@ -164,6 +169,7 @@ copy_flt_file(const Filename &source, const Filename &dest,
|
|||||||
// filename, relative to the flt file.
|
// filename, relative to the flt file.
|
||||||
tex->_filename = dir->get_rel_to(texture_dir) + "/" +
|
tex->_filename = dir->get_rel_to(texture_dir) + "/" +
|
||||||
texture_filename.get_basename();
|
texture_filename.get_basename();
|
||||||
|
header->add_texture(tex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "cvsCopy.h"
|
#include "cvsCopy.h"
|
||||||
|
|
||||||
#include <dSearchPath.h>
|
#include <dSearchPath.h>
|
||||||
|
#include <pointerTo.h>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
@ -54,8 +55,8 @@ private:
|
|||||||
bool new_file);
|
bool new_file);
|
||||||
|
|
||||||
|
|
||||||
typedef set<FltExternalReference *> Refs;
|
typedef set<PT(FltExternalReference)> Refs;
|
||||||
typedef set<FltTexture *> Textures;
|
typedef set<PT(FltTexture)> Textures;
|
||||||
|
|
||||||
void scan_flt(FltRecord *record, Refs &refs, Textures &textures);
|
void scan_flt(FltRecord *record, Refs &refs, Textures &textures);
|
||||||
|
|
||||||
|
@ -567,6 +567,55 @@ dispatch_int(const string &opt, const string &arg, void *var) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: ProgramBase::dispatch_int_pair
|
||||||
|
// Access: Protected
|
||||||
|
// Description: Standard dispatch function for an option that takes
|
||||||
|
// a pair of integer parameters. The data pointer is to
|
||||||
|
// an array of two integers.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool ProgramBase::
|
||||||
|
dispatch_int_pair(const string &opt, const string &arg, void *var) {
|
||||||
|
if (arg.empty()) {
|
||||||
|
nout << "-" << opt
|
||||||
|
<< " requires an pair of integers separated by a comma.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t comma = arg.find(',');
|
||||||
|
if (comma == string::npos) {
|
||||||
|
nout << "-" << opt
|
||||||
|
<< " requires an pair of integers separated by a comma.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string first = arg.substr(0, comma);
|
||||||
|
string second = arg.substr(comma + 1);
|
||||||
|
|
||||||
|
int *ip = (int *)var;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
const char *first_str = first.c_str();
|
||||||
|
ip[0] = strtol(first_str, &endptr, 0);
|
||||||
|
|
||||||
|
if (*endptr != '\0') {
|
||||||
|
nout << "Invalid integer parameter for -" << opt << ": "
|
||||||
|
<< first << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *second_str = second.c_str();
|
||||||
|
ip[1] = strtol(second_str, &endptr, 0);
|
||||||
|
|
||||||
|
if (*endptr != '\0') {
|
||||||
|
nout << "Invalid integer parameter for -" << opt << ": "
|
||||||
|
<< second << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: ProgramBase::dispatch_double
|
// Function: ProgramBase::dispatch_double
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
|
@ -61,6 +61,7 @@ protected:
|
|||||||
bool dispatch_none(const string &opt, const string &arg, void *);
|
bool dispatch_none(const string &opt, const string &arg, void *);
|
||||||
bool dispatch_count(const string &opt, const string &arg, void *var);
|
bool dispatch_count(const string &opt, const string &arg, void *var);
|
||||||
bool dispatch_int(const string &opt, const string &arg, void *var);
|
bool dispatch_int(const string &opt, const string &arg, void *var);
|
||||||
|
bool dispatch_int_pair(const string &opt, const string &arg, void *var);
|
||||||
bool dispatch_double(const string &opt, const string &arg, void *var);
|
bool dispatch_double(const string &opt, const string &arg, void *var);
|
||||||
bool dispatch_string(const string &opt, const string &arg, void *var);
|
bool dispatch_string(const string &opt, const string &arg, void *var);
|
||||||
bool dispatch_filename(const string &opt, const string &arg, void *var);
|
bool dispatch_filename(const string &opt, const string &arg, void *var);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user