From 81bdd7146eae894ce7c6d57cc8d7f8b4f14d325b Mon Sep 17 00:00:00 2001 From: Gyedo Jeon Date: Wed, 29 Jul 2009 21:09:42 +0000 Subject: [PATCH] Added :textureswap feature to create extra palettes with swapped textures --- pandatool/src/palettizer/paletteGroup.cxx | 44 ++++ pandatool/src/palettizer/paletteGroup.h | 6 + pandatool/src/palettizer/paletteImage.cxx | 191 +++++++++++++++++- pandatool/src/palettizer/paletteImage.h | 11 + pandatool/src/palettizer/palettizer.cxx | 6 +- pandatool/src/palettizer/texturePlacement.cxx | 123 +++++++++++ pandatool/src/palettizer/texturePlacement.h | 6 +- pandatool/src/palettizer/txaFile.cxx | 37 ++++ pandatool/src/palettizer/txaFile.h | 1 + 9 files changed, 416 insertions(+), 9 deletions(-) diff --git a/pandatool/src/palettizer/paletteGroup.cxx b/pandatool/src/palettizer/paletteGroup.cxx index e6124bfc71..02fc14374a 100644 --- a/pandatool/src/palettizer/paletteGroup.cxx +++ b/pandatool/src/palettizer/paletteGroup.cxx @@ -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)); +} diff --git a/pandatool/src/palettizer/paletteGroup.h b/pandatool/src/palettizer/paletteGroup.h index 37e672bc5d..b39240d803 100644 --- a/pandatool/src/palettizer/paletteGroup.h +++ b/pandatool/src/palettizer/paletteGroup.h @@ -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 Pages; Pages _pages; + typedef pmap TextureSwapInfo; + TextureSwapInfo _textureSwapInfo; + // The TypedWritable interface follows. public: static void register_with_read_factory(); diff --git a/pandatool/src/palettizer/paletteImage.cxx b/pandatool/src/palettizer/paletteImage.cxx index fe6824a256..36a42c9e7d 100644 --- a/pandatool/src/palettizer/paletteImage.cxx +++ b/pandatool/src/palettizer/paletteImage.cxx @@ -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 ®ion = (*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 diff --git a/pandatool/src/palettizer/paletteImage.h b/pandatool/src/palettizer/paletteImage.h index 440d4ac7f9..0ad50e8eea 100644 --- a/pandatool/src/palettizer/paletteImage.h +++ b/pandatool/src/palettizer/paletteImage.h @@ -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 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 SwappedImages; + SwappedImages _swappedImages; + // The TypedWritable interface follows. public: static void register_with_read_factory(); diff --git a/pandatool/src/palettizer/palettizer.cxx b/pandatool/src/palettizer/palettizer.cxx index e374cbb0b1..8a1874efe1 100644 --- a/pandatool/src/palettizer/palettizer.cxx +++ b/pandatool/src/palettizer/palettizer.cxx @@ -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(); } diff --git a/pandatool/src/palettizer/texturePlacement.cxx b/pandatool/src/palettizer/texturePlacement.cxx index 20feb92779..f572684cdd 100644 --- a/pandatool/src/palettizer/texturePlacement.cxx +++ b/pandatool/src/palettizer/texturePlacement.cxx @@ -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); } diff --git a/pandatool/src/palettizer/texturePlacement.h b/pandatool/src/palettizer/texturePlacement.h index e3381644be..b0a51adae4 100644 --- a/pandatool/src/palettizer/texturePlacement.h +++ b/pandatool/src/palettizer/texturePlacement.h @@ -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 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() { diff --git a/pandatool/src/palettizer/txaFile.cxx b/pandatool/src/palettizer/txaFile.cxx index 7e6698c9d8..79ef1b9330 100644 --- a/pandatool/src/palettizer/txaFile.cxx +++ b/pandatool/src/palettizer/txaFile.cxx @@ -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; +} diff --git a/pandatool/src/palettizer/txaFile.h b/pandatool/src/palettizer/txaFile.h index 81b78a8471..241309740d 100644 --- a/pandatool/src/palettizer/txaFile.h +++ b/pandatool/src/palettizer/txaFile.h @@ -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 Lines; Lines _lines;