Added :textureswap feature to create extra palettes with swapped textures

This commit is contained in:
Gyedo Jeon 2009-07-29 21:09:42 +00:00
parent 18af305b4a
commit 81bdd7146e
9 changed files with 416 additions and 9 deletions

View File

@ -424,6 +424,36 @@ prepare(TextureImage *texture) {
TexturePlacement *placement = new TexturePlacement(texture, this);
_placements.insert(placement);
// [gjeon] update swapTexture information
TextureSwapInfo::iterator tsi = _textureSwapInfo.find(texture->get_name());
if (tsi != _textureSwapInfo.end()) {
vector_string swapTextures = (*tsi).second;
vector_string::const_iterator wi;
wi = swapTextures.begin();
++wi;
++wi;
// [gjeon] since swapped texture usually didn't mapped to any egg file
// we need to create soucreTextureImage by using original texture file's info
const string originalTextureName = (*wi);
TextureImage *originalTexture = pal->get_texture(originalTextureName);
SourceTextureImage *source = originalTexture->get_preferred_source();
const Filename originalTextureFilename = source->get_filename();
const Filename originalTextureAlphaFilename = source->get_alpha_filename();
int originalTextureAlphaFileChannel = source->get_alpha_file_channel();
++wi;
while (wi != swapTextures.end()) {
const string &swapTextureName = (*wi);
TextureImage *swapTextureImage = pal->get_texture(swapTextureName);
Filename swapTextureFilename = Filename(originalTextureFilename.get_dirname(), swapTextureName + "." + originalTextureFilename.get_extension());
swapTextureImage->get_source(swapTextureFilename, originalTextureAlphaFilename, originalTextureAlphaFileChannel);
placement->_textureSwaps.push_back(swapTextureImage);
++wi;
}
}
return placement;
}
@ -779,3 +809,17 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_margin_override = scan.get_int16();
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteGroup::add_texture_swap_info
// Access: Public
// Description: Store textureswap information from textures.txa
////////////////////////////////////////////////////////////////////
void PaletteGroup::
add_texture_swap_info(const string sourceTextureName, const vector_string &swapTextures) {
TextureSwapInfo::iterator tsi = _textureSwapInfo.find(sourceTextureName);
if (tsi != _textureSwapInfo.end()) {
_textureSwapInfo.erase(tsi);
}
_textureSwapInfo.insert(TextureSwapInfo::value_type(sourceTextureName, swapTextures));
}

View File

@ -25,6 +25,7 @@
#include "pset.h"
#include "pvector.h"
#include "vector_string.h"
class EggFile;
class TexturePlacement;
@ -89,6 +90,8 @@ public:
void setup_shadow_images();
void update_images(bool redo_all);
void add_texture_swap_info(const string sourceTextureName, const vector_string &swapTextures);
private:
string _dirname;
int _egg_count;
@ -103,6 +106,9 @@ private:
typedef pmap<TextureProperties, PalettePage *> Pages;
Pages _pages;
typedef pmap<string, vector_string> TextureSwapInfo;
TextureSwapInfo _textureSwapInfo;
// The TypedWritable interface follows.
public:
static void register_with_read_factory();

View File

@ -155,6 +155,8 @@ PaletteImage() {
_index = 0;
_new_image = false;
_got_image = false;
_swapped_image = 0;
}
////////////////////////////////////////////////////////////////////
@ -173,10 +175,33 @@ PaletteImage(PalettePage *page, int index) :
_y_size = pal->_pal_y_size;
_new_image = true;
_got_image = false;
_swapped_image = 0;
setup_filename();
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PaletteImage::
PaletteImage(PalettePage *page, int index, unsigned swapIndex) :
_page(page),
_index(index),
_swapped_image(swapIndex)
{
_properties = page->get_properties();
_size_known = true;
_x_size = pal->_pal_x_size;
_y_size = pal->_pal_y_size;
_new_image = true;
_got_image = false;
setup_filename();
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::get_page
// Access: Public
@ -293,6 +318,17 @@ place(TexturePlacement *placement) {
if (find_hole(x, y, placement->get_x_size(), placement->get_y_size())) {
placement->place_at(this, x, y);
_placements.push_back(placement);
// [gjeon] create swappedImages
TexturePlacement::TextureSwaps::iterator tsi;
for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
if ((tsi - placement->_textureSwaps.begin()) >= _swappedImages.size()) {
PaletteImage *swappedImage = new PaletteImage(_page, _swappedImages.size(), tsi - placement->_textureSwaps.begin() + 1);
swappedImage->_masterPlacements = &_placements;
_swappedImages.push_back(swappedImage);
}
}
return true;
}
@ -314,7 +350,6 @@ unplace(TexturePlacement *placement) {
_placements.erase(pi);
pi = find(_placements.begin(), _placements.end(), placement);
}
_cleared_regions.push_back(ClearedRegion(placement));
}
@ -373,7 +408,7 @@ check_solitary() {
////////////////////////////////////////////////////////////////////
void PaletteImage::
optimal_resize() {
if (is_empty()) {
if (is_empty()) { // && (_swapped_image == 0)) {
return;
}
@ -392,12 +427,20 @@ optimal_resize() {
success = true;
resized_any = true;
}
} while (success);
if (resized_any) {
nout << "Resizing "
<< FilenameUnifier::make_user_filename(get_filename()) << " to "
<< _x_size << " " << _y_size << "\n";
// [gjeon] resize swapped images, also
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
swappedImage->resize_swapped_image(_x_size, _y_size);
}
}
}
@ -471,6 +514,22 @@ resize_image(int x_size, int y_size) {
return packed;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::resize_swapped_image
// Access: Public
// Description: Attempts to resize the palette image, and repack all
// of the textures within the new size. Returns true if
// successful, false otherwise. If this fails, it will
// still result in repacking all the textures in the
// original size.
////////////////////////////////////////////////////////////////////
void PaletteImage::
resize_swapped_image(int x_size, int y_size) {
// Finally, apply the new size and try to fit all the textures.
_x_size = x_size;
_y_size = y_size;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::write_placements
// Access: Public
@ -518,6 +577,13 @@ reset_image() {
void PaletteImage::
setup_shadow_image() {
_shadow_image.make_shadow_image(_basename);
// [gjeon] setup shadoe_image of swappedImages
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
swappedImage->setup_shadow_image();
}
}
////////////////////////////////////////////////////////////////////
@ -576,6 +642,25 @@ update_image(bool redo_all) {
needs_update = true;
}
}
// [gjeon] to find out all of the swappable textures is up to date
TexturePlacement::TextureSwaps::iterator tsi;
for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
TextureImage *swapTexture = (*tsi);
if (swapTexture->is_texture_named()) {
SourceTextureImage *sourceSwapTexture = swapTexture->get_preferred_source();
if (sourceSwapTexture != (SourceTextureImage *)NULL &&
sourceSwapTexture->get_filename().compare_timestamps(get_filename()) > 0) {
// The source image is newer than the palette image; we need to
// regenerate.
placement->mark_unfilled();
needs_update = true;
}
}
}
}
}
@ -585,12 +670,21 @@ update_image(bool redo_all) {
}
get_image();
// [gjeon] get swapped images, too
get_swapped_images();
// Set to black any parts of the image that we recently unplaced.
ClearedRegions::iterator ci;
for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
ClearedRegion &region = (*ci);
region.clear(_image);
// [gjeon] clear swapped images also
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
region.clear(swappedImage->_image);
}
}
_cleared_regions.clear();
@ -599,6 +693,14 @@ update_image(bool redo_all) {
TexturePlacement *placement = (*pi);
if (!placement->is_filled()) {
placement->fill_image(_image);
// [gjeon] fill swapped images
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
swappedImage->update_filename();
placement->fill_swapped_image(swappedImage->_image, si - _swappedImages.begin());
}
}
}
@ -609,6 +711,17 @@ update_image(bool redo_all) {
}
release_image();
// [gjeon] write and release swapped images
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
swappedImage->write(swappedImage->_image);
if (pal->_shadow_color_type != (PNMFileType *)NULL) {
swappedImage->_shadow_image.write(swappedImage->_image);
}
swappedImage->release_image();
}
}
////////////////////////////////////////////////////////////////////
@ -662,7 +775,6 @@ update_filename() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::setup_filename
// Access: Private
@ -713,6 +825,11 @@ setup_filename() {
++si;
}
}
if (_swapped_image > 0) {
_basename += "_swp_";
_basename += format_string(_swapped_image);
}
// We must end the basename with a dot, so that it does not appear
// to have a filename extension. Otherwise, an embedded dot in the
@ -731,7 +848,7 @@ setup_filename() {
if (_shadow_image.make_shadow_image(_basename)) {
any_changed = true;
}
return any_changed;
}
@ -849,6 +966,72 @@ get_image() {
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::get_swapped_image
// Access: Public
// Description: Reads or generates the PNMImage for swapped textures
////////////////////////////////////////////////////////////////////
void PaletteImage::
get_swapped_image(int index) {
if (_got_image) {
return;
}
if (!_new_image) {
if (pal->_shadow_color_type != (PNMFileType *)NULL) {
if (_shadow_image.get_filename().exists() && _shadow_image.read(_image)) {
_got_image = true;
return;
}
} else {
if (get_filename().exists() && read(_image)) {
_got_image = true;
return;
}
}
}
nout << "Generating new "
<< FilenameUnifier::make_user_filename(get_filename()) << "\n";
// We won't be using this any more.
_cleared_regions.clear();
_image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
_image.fill(pal->_background[0], pal->_background[1], pal->_background[2]);
if (_image.has_alpha()) {
_image.alpha_fill(pal->_background[3]);
}
_new_image = false;
_got_image = true;
// Now fill up the image.
Placements::iterator pi;
for (pi = _masterPlacements->begin(); pi != _masterPlacements->end(); ++pi) {
TexturePlacement *placement = (*pi);
if (placement->_textureSwaps.size() > index)
placement->fill_swapped_image(_image, index);
else
placement->fill_image(_image);
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::get_swapped_images
// Access: Public
// Description: Reads or generates the PNMImage that corresponds to
// the palette as it is known so far.
////////////////////////////////////////////////////////////////////
void PaletteImage::
get_swapped_images() {
SwappedImages::iterator si;
for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
PaletteImage *swappedImage = (*si);
swappedImage->get_swapped_image(si - _swappedImages.begin());
}
}
////////////////////////////////////////////////////////////////////
// Function: PaletteImage::release_image
// Access: Public

View File

@ -39,6 +39,7 @@ private:
public:
PaletteImage(PalettePage *page, int index);
PaletteImage(PalettePage *page, int index, unsigned swapIndex);
PalettePage *get_page() const;
@ -52,6 +53,7 @@ public:
void optimal_resize();
bool resize_image(int x_size, int y_size);
void resize_swapped_image(int x_size, int y_size);
void write_placements(ostream &out, int indent_level = 0) const;
void reset_image();
@ -67,6 +69,8 @@ private:
void get_image();
void release_image();
void remove_image();
void get_swapped_image(int index);
void get_swapped_images();
// The ClearedRegion object keeps track of TexturePlacements that
// were recently removed and thus need to be set to black.
@ -92,6 +96,8 @@ private:
typedef pvector<TexturePlacement *> Placements;
Placements _placements;
Placements *_masterPlacements;
PalettePage *_page;
int _index;
string _basename;
@ -99,9 +105,14 @@ private:
bool _new_image;
bool _got_image;
PNMImage _image;
unsigned _swapped_image; // 0 for non swapped image
ImageFile _shadow_image;
typedef pvector<PaletteImage *> SwappedImages;
SwappedImages _swappedImages;
// The TypedWritable interface follows.
public:
static void register_with_read_factory();

View File

@ -37,7 +37,7 @@ Palettizer *pal = (Palettizer *)NULL;
// allows us to easily update egg-palettize to write out additional
// information to its pi file, without having it increment the bam
// version number for all bam and boo files anywhere in the world.
int Palettizer::_pi_version = 19;
int Palettizer::_pi_version = 20;
// Updated to version 8 on 3/20/03 to remove extensions from texture key names.
// Updated to version 9 on 4/13/03 to add a few properties in various places.
// Updated to version 10 on 4/15/03 to add _alpha_file_channel.
@ -50,7 +50,7 @@ int Palettizer::_pi_version = 19;
// Updated to version 17 on 3/02/07 to add TextureImage::_txa_wrap_u etc.
// Updated to version 18 on 5/13/08 to add TextureProperties::_quality_level.
// Updated to version 19 on 7/16/09 to add PaletteGroup::_override_margin
// Updated to version 20 on 7/27/09 to add TexturePlacement::_swapTextures
int Palettizer::_min_pi_version = 8;
// Dropped support for versions 7 and below on 7/14/03.
@ -562,7 +562,6 @@ process_all(bool force_texture_read, const Filename &state_filename) {
EggFiles::const_iterator efi;
for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) {
EggFile *egg_file = (*efi).second;
egg_file->pre_txa_file();
_txa_file.match_egg(egg_file);
egg_file->post_txa_file();
@ -583,7 +582,6 @@ process_all(bool force_texture_read, const Filename &state_filename) {
// .txa file.
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
TextureImage *texture = (*ti).second;
if (force_texture_read || texture->is_newer_than(state_filename)) {
texture->read_source_image();
}

View File

@ -885,6 +885,109 @@ fill_image(PNMImage &image) {
_texture->release_source_image();
}
////////////////////////////////////////////////////////////////////
// Function: TexturePlacement::fill_swapped_image
// Access: Public
// Description: Fills in the rectangle of the swapped palette image
// represented by the texture placement with the image
// pixels.
////////////////////////////////////////////////////////////////////
void TexturePlacement::
fill_swapped_image(PNMImage &image, int index) {
nassertv(is_placed());
_is_filled = true;
// We determine the pixels to place the source image at by
// transforming the unit texture box: the upper-left and lower-right
// corners. These corners, in the final texture coordinate space,
// represent where on the palette image the original texture should
// be located.
LMatrix3d transform;
compute_tex_matrix(transform);
TexCoordd ul = TexCoordd(0.0, 1.0) * transform;
TexCoordd lr = TexCoordd(1.0, 0.0) * transform;
// Now we convert those texture coordinates back to pixel units.
int pal_x_size = _image->get_x_size();
int pal_y_size = _image->get_y_size();
int top = (int)floor((1.0 - ul[1]) * pal_y_size + 0.5);
int left = (int)floor(ul[0] * pal_x_size + 0.5);
int bottom = (int)floor((1.0 - lr[1]) * pal_y_size + 0.5);
int right = (int)floor(lr[0] * pal_x_size + 0.5);
// And now we can determine the size to scale the image to based on
// that. This may not be the same as texture->size() because of
// margins.
int x_size = right - left;
int y_size = bottom - top;
nassertv(x_size >= 0 && y_size >= 0);
// Now we get a PNMImage that represents the swapped texture at that
// size.
TextureSwaps::iterator tsi;
tsi = _textureSwaps.begin() + index;
TextureImage *swapTexture = (*tsi);
const PNMImage &source_full = swapTexture->read_source_image();
if (!source_full.is_valid()) {
flag_error_image(image);
return;
}
PNMImage source(x_size, y_size, source_full.get_num_channels(),
source_full.get_maxval());
source.quick_filter_from(source_full);
bool alpha = image.has_alpha();
bool source_alpha = source.has_alpha();
// Now copy the pixels. We do this by walking through the
// rectangular region on the palette image that we have reserved for
// this texture; for each pixel in this region, we determine its
// appropriate color based on its relation to the actual texture
// image location (determined above), and on whether the texture
// wraps or clamps.
for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) {
int sy = y - top;
if (_placed._wrap_v == EggTexture::WM_clamp) {
// Clamp at [0, y_size).
sy = max(min(sy, y_size - 1), 0);
} else {
// Wrap: sign-independent modulo.
sy = (sy < 0) ? y_size - 1 - ((-sy - 1) % y_size) : sy % y_size;
}
for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) {
int sx = x - left;
if (_placed._wrap_u == EggTexture::WM_clamp) {
// Clamp at [0, x_size).
sx = max(min(sx, x_size - 1), 0);
} else {
// Wrap: sign-independent modulo.
sx = (sx < 0) ? x_size - 1 - ((-sx - 1) % x_size) : sx % x_size;
}
image.set_xel(x, y, source.get_xel(sx, sy));
if (alpha) {
if (source_alpha) {
image.set_alpha(x, y, source.get_alpha(sx, sy));
} else {
image.set_alpha(x, y, 1.0);
}
}
}
}
swapTexture->release_source_image();
}
////////////////////////////////////////////////////////////////////
// Function: TexturePlacement::flag_error_image
// Access: Public
@ -1003,6 +1106,12 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
writer->write_pointer(datagram, (*ri));
}
datagram.add_int32(_textureSwaps.size());
TextureSwaps::const_iterator tsi;
for (tsi = _textureSwaps.begin(); tsi != _textureSwaps.end(); ++tsi) {
writer->write_pointer(datagram, (*tsi));
}
}
////////////////////////////////////////////////////////////////////
@ -1046,6 +1155,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
index++;
}
for (i = 0; i < _num_textureSwaps; i++) {
TextureImage *swapTexture;
DCAST_INTO_R(swapTexture, p_list[index], index);
_textureSwaps.push_back(swapTexture);
index++;
}
return index;
}
@ -1094,6 +1210,13 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_num_references = scan.get_int32();
manager->read_pointers(scan, _num_references);
if (Palettizer::_read_pi_version >= 20) {
_num_textureSwaps = scan.get_int32();
} else {
_num_textureSwaps = 0;
}
manager->read_pointers(scan, _num_textureSwaps);
}

View File

@ -91,8 +91,12 @@ public:
bool is_filled() const;
void mark_unfilled();
void fill_image(PNMImage &image);
void fill_swapped_image(PNMImage &image, int index);
void flag_error_image(PNMImage &image);
typedef pvector<TextureImage *> TextureSwaps;
TextureSwaps _textureSwaps;
private:
void compute_size_from_uvs(const TexCoordd &min_uv, const TexCoordd &max_uv);
@ -127,8 +131,8 @@ private:
// This value is only filled in while reading from the bam file;
// don't use it otherwise.
int _num_references;
int _margin_override;
int _num_textureSwaps;
public:
static TypeHandle get_class_type() {

View File

@ -91,6 +91,9 @@ read(istream &in, const string &filename) {
} else if (words[0] == ":cutout") {
okflag = parse_cutout_line(words);
} else if (words[0] == ":textureswap") {
okflag = parse_textureswap_line(words);
} else {
nout << "Invalid keyword " << words[0] << "\n";
okflag = false;
@ -577,6 +580,7 @@ parse_remap_line(const vector_string &words) {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_cutout_line
// Access: Private
@ -608,3 +612,36 @@ parse_cutout_line(const vector_string &words) {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_textureswap_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":textureswap" and indicates the relationships
// between textures to be swapped.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_textureswap_line(const vector_string &words) {
vector_string::const_iterator wi;
wi = words.begin();
assert (wi != words.end());
++wi;
const string &group_name = (*wi);
PaletteGroup *group = pal->get_palette_group(group_name);
++wi;
string sourceTextureName = (*wi);
++wi;
//vector_string swapTextures;
//copy(words.begin(), words.end(), swapTextures);
//group->add_texture_swap_info(sourceTextureName, swapTextures);
size_t dot = sourceTextureName.rfind('.');
if (dot != string::npos) {
sourceTextureName = sourceTextureName.substr(0, dot);
}
group->add_texture_swap_info(sourceTextureName, words);
return true;
}

View File

@ -55,6 +55,7 @@ private:
bool parse_round_line(const vector_string &words);
bool parse_remap_line(const vector_string &words);
bool parse_cutout_line(const vector_string &words);
bool parse_textureswap_line(const vector_string &words);
typedef pvector<TxaLine> Lines;
Lines _lines;