X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftile.cpp;h=7cad1b83692399392cacbf6ede423d0c543fc014;hb=d5029958b9017ad89775bc4f68c4de3db603e618;hp=4af7a327273d0059ad32aecdff3416836f6d2132;hpb=895b970a10f5b37a5dec6f668e745c1531fb6ebf;p=oweals%2Fminetest.git diff --git a/src/tile.cpp b/src/tile.cpp index 4af7a3272..7cad1b836 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -3,16 +3,16 @@ Minetest-c55 Copyright (C) 2010-2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "main.h" // for g_settings #include "filesys.h" -#include "utility.h" #include "settings.h" #include "mesh.h" #include @@ -29,6 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" // For texture atlas making #include "nodedef.h" // For texture atlas making #include "gamedef.h" +#include "util/string.h" +#include "util/container.h" +#include "util/thread.h" +#include "util/numeric.h" /* A cache from texture name to texture path @@ -82,7 +85,10 @@ static std::string getImagePath(std::string path) "pcx", "ppm", "psd", "wal", "rgb", NULL }; - + // If there is no extension, add one + if(removeStringEnd(path, extensions) == "") + path = path + ".png"; + // Check paths until something is found to exist const char **ext = extensions; do{ bool r = replace_ext(path, *ext); @@ -127,6 +133,18 @@ std::string getTexturePath(const std::string &filename) fullpath = getImagePath(testpath); } + /* + Check from $user/textures/all + */ + if(fullpath == "") + { + std::string texture_path = porting::path_user + DIR_DELIM + + "textures" + DIR_DELIM + "all"; + std::string testpath = texture_path + DIR_DELIM + filename; + // Check all filename extensions. Returns "" if not found. + fullpath = getImagePath(testpath); + } + /* Check from default data directory */ @@ -354,6 +372,18 @@ public: // Update new texture pointer and texture coordinates to an // AtlasPointer based on it's texture id void updateAP(AtlasPointer &ap); + + bool isKnownSourceImage(const std::string &name) + { + bool is_known = false; + bool cache_found = m_source_image_existence.get(name, &is_known); + if(cache_found) + return is_known; + // Not found in cache; find out if a local file exists + is_known = (getTexturePath(name) != ""); + m_source_image_existence.set(name, is_known); + return is_known; + } // Processes queued texture requests from other threads. // Shall be called from the main thread. @@ -382,6 +412,9 @@ private: // This should be only accessed from the main thread SourceImageCache m_sourcecache; + // Thread-safe cache of what source images are known (true = known) + MutexedMap m_source_image_existence; + // A texture id is index in this array. // The first position contains a NULL texture. core::array m_atlaspointer_cache; @@ -486,8 +519,19 @@ u32 TextureSource::getTextureId(const std::string &name) // Overlay image on top of another image (used for cracks) void overlay(video::IImage *image, video::IImage *overlay); +// Draw an image on top of an another one, using the alpha channel of the +// source image +static void blit_with_alpha(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size); + // Brighten image void brighten(video::IImage *image); +// Parse a transform name +u32 parseImageTransform(const std::string& s); +// Apply transform to image dimension +core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim); +// Apply transform to image data +void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); /* Generate image based on a string like "stone.png" or "[crack0". @@ -752,6 +796,7 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im assert(get_current_thread_id() == m_main_thread); m_sourcecache.insert(name, img, true, m_device->getVideoDriver()); + m_source_image_existence.set(name, true); } void TextureSource::rebuildImagesAndTextures() @@ -808,7 +853,10 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) JMutexAutoLock lock(m_atlaspointer_cache_mutex); // Create an image of the right size - core::dimension2d atlas_dim(1024,1024); + core::dimension2d max_dim = driver->getMaxTextureSize(); + core::dimension2d atlas_dim(2048,2048); + atlas_dim.Width = MYMIN(atlas_dim.Width, max_dim.Width); + atlas_dim.Height = MYMIN(atlas_dim.Height, max_dim.Height); video::IImage *atlas_img = driver->createImage(video::ECF_A8R8G8B8, atlas_dim); //assert(atlas_img); @@ -833,7 +881,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) const ContentFeatures &f = ndef->get(j); for(u32 i=0; i<6; i++) { - std::string name = f.tname_tiles[i]; + std::string name = f.tiledef[i].name; sourcelist[name] = true; } } @@ -849,16 +897,17 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) infostream< pos_in_atlas(0,0); + pos_in_atlas.X = column_padding; pos_in_atlas.Y = padding; for(core::map::Iterator @@ -879,8 +928,8 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) core::dimension2d dim = img2->getDimension(); - // Don't add to atlas if image is large - core::dimension2d max_size_in_atlas(32,32); + // Don't add to atlas if image is too large + core::dimension2d max_size_in_atlas(64,64); if(dim.Width > max_size_in_atlas.Width || dim.Height > max_size_in_atlas.Height) { @@ -892,14 +941,14 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) // Wrap columns and stop making atlas if atlas is full if(pos_in_atlas.Y + dim.Height > atlas_dim.Height) { - if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){ + if(pos_in_atlas.X > (s32)atlas_dim.Width - column_width - column_padding){ errorstream<<"TextureSource::buildMainAtlas(): " <<"Atlas is full, not adding more textures." <setPixel(x,dst_y,c); } + for(u32 side=0; side<2; side++) // left and right + for(s32 x0=0; x0getPixel(src_x, src_y); + atlas_img->setPixel(dst_x,dst_y,c); + } + img2->drop(); /* @@ -1144,10 +1216,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, // Position to copy the blitted from in the blitted image core::position2d pos_from(0,0); // Blit - image->copyToWithAlpha(baseimg, pos_to, + /*image->copyToWithAlpha(baseimg, pos_to, core::rect(pos_from, dim), video::SColor(255,255,255,255), - NULL); + NULL);*/ + blit_with_alpha(image, baseimg, pos_from, pos_to, dim); // Drop image image->drop(); } @@ -1217,7 +1290,8 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, It is an image with a number of cracking stages horizontally tiled. */ - video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device); + video::IImage *img_crack = sourcecache->getOrLoad( + "crack_anylength.png", device); if(img_crack && progression >= 0) { @@ -1256,11 +1330,13 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, } else { - img_crack_scaled->copyToWithAlpha( + /*img_crack_scaled->copyToWithAlpha( baseimg, v2s32(0,0), core::rect(v2s32(0,0), dim_base), - video::SColor(255,255,255,255)); + video::SColor(255,255,255,255));*/ + blit_with_alpha(img_crack_scaled, baseimg, + v2s32(0,0), v2s32(0,0), dim_base); } } @@ -1285,7 +1361,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, u32 h0 = stoi(sf.next(":")); infostream<<"combined w="<createImage(video::ECF_A8R8G8B8, dim); img->copyTo(img2); img->drop(); - img2->copyToWithAlpha(baseimg, pos_base, + /*img2->copyToWithAlpha(baseimg, pos_base, core::rect(v2s32(0,0), dim), video::SColor(255,255,255,255), - NULL); + NULL);*/ + blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim); img2->drop(); } else @@ -1401,6 +1482,46 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, baseimg->setPixel(x,y,c); } } + /* + "[transformN" + Rotates and/or flips the image. + + N can be a number (between 0 and 7) or a transform name. + Rotations are counter-clockwise. + 0 I identity + 1 R90 rotate by 90 degrees + 2 R180 rotate by 180 degrees + 3 R270 rotate by 270 degrees + 4 FX flip X + 5 FXR90 flip X then rotate by 90 degrees + 6 FY flip Y + 7 FYR90 flip Y then rotate by 90 degrees + + Note: Transform names can be concatenated to produce + their product (applies the first then the second). + The resulting transform will be equivalent to one of the + eight existing ones, though (see: dihedral group). + */ + else if(part_of_name.substr(0,10) == "[transform") + { + if(baseimg == NULL) + { + errorstream<<"generate_image(): baseimg==NULL " + <<"for part_of_name=\""< dim = imageTransformDimension( + transform, baseimg->getDimension()); + video::IImage *image = driver->createImage( + baseimg->getColorFormat(), dim); + assert(image); + imageTransform(transform, baseimg, image); + baseimg->drop(); + baseimg = image; + } /* [inventorycube{topimage{leftimage{rightimage In every subimage, replace ^ with &. @@ -1514,6 +1635,86 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, image->drop(); } } + /* + [lowpart:percent:filename + Adds the lower part of a texture + */ + else if(part_of_name.substr(0,9) == "[lowpart:") + { + Strfnd sf(part_of_name); + sf.next(":"); + u32 percent = stoi(sf.next(":")); + std::string filename = sf.next(":"); + //infostream<<"power part "<createImage(video::ECF_A8R8G8B8, v2u32(16,16)); + video::IImage *img = sourcecache->getOrLoad(filename, device); + if(img) + { + core::dimension2d dim = img->getDimension(); + core::position2d pos_base(0, 0); + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop(); + core::position2d clippos(0, 0); + clippos.Y = dim.Height * (100-percent) / 100; + core::dimension2d clipdim = dim; + clipdim.Height = clipdim.Height * percent / 100 + 1; + core::rect cliprect(clippos, clipdim); + img2->copyToWithAlpha(baseimg, pos_base, + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + &cliprect); + img2->drop(); + } + } + /* + [verticalframe:N:I + Crops a frame of a vertical animation. + N = frame count, I = frame index + */ + else if(part_of_name.substr(0,15) == "[verticalframe:") + { + Strfnd sf(part_of_name); + sf.next(":"); + u32 frame_count = stoi(sf.next(":")); + u32 frame_index = stoi(sf.next(":")); + + if(baseimg == NULL){ + errorstream<<"generate_image(): baseimg!=NULL " + <<"for part_of_name=\""<getDimension(); + frame_size.Y /= frame_count; + + video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, + frame_size); + if(!img){ + errorstream<<"generate_image(): Could not create image " + <<"for part_of_name=\""<fill(video::SColor(0,0,0,0)); + + core::dimension2d dim = frame_size; + core::position2d pos_dst(0, 0); + core::position2d pos_src(0, frame_index * frame_size.Y); + baseimg->copyToWithAlpha(img, pos_dst, + core::rect(pos_src, dim), + video::SColor(255,255,255,255), + NULL); + // Replace baseimg + baseimg->drop(); + baseimg = img; + } else { errorstream<<"generate_image(): Invalid " @@ -1555,6 +1756,30 @@ void overlay(video::IImage *image, video::IImage *overlay) } } +/* + Draw an image on top of an another one, using the alpha channel of the + source image + + This exists because IImage::copyToWithAlpha() doesn't seem to always + work. +*/ +static void blit_with_alpha(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size) +{ + for(u32 y0=0; y0getPixel(src_x, src_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } +} + void brighten(video::IImage *image) { if(image == NULL) @@ -1573,3 +1798,106 @@ void brighten(video::IImage *image) } } +u32 parseImageTransform(const std::string& s) +{ + int total_transform = 0; + + std::string transform_names[8]; + transform_names[0] = "i"; + transform_names[1] = "r90"; + transform_names[2] = "r180"; + transform_names[3] = "r270"; + transform_names[4] = "fx"; + transform_names[6] = "fy"; + + std::size_t pos = 0; + while(pos < s.size()) + { + int transform = -1; + for(int i = 0; i <= 7; ++i) + { + const std::string &name_i = transform_names[i]; + + if(s[pos] == ('0' + i)) + { + transform = i; + pos++; + break; + } + else if(!(name_i.empty()) && + lowercase(s.substr(pos, name_i.size())) == name_i) + { + transform = i; + pos += name_i.size(); + break; + } + } + if(transform < 0) + break; + + // Multiply total_transform and transform in the group D4 + int new_total = 0; + if(transform < 4) + new_total = (transform + total_transform) % 4; + else + new_total = (transform - total_transform + 8) % 4; + if((transform >= 4) ^ (total_transform >= 4)) + new_total += 4; + + total_transform = new_total; + } + return total_transform; +} + +core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim) +{ + if(transform % 2 == 0) + return dim; + else + return core::dimension2d(dim.Height, dim.Width); +} + +void imageTransform(u32 transform, video::IImage *src, video::IImage *dst) +{ + if(src == NULL || dst == NULL) + return; + + core::dimension2d srcdim = src->getDimension(); + core::dimension2d dstdim = dst->getDimension(); + + assert(dstdim == imageTransformDimension(transform, srcdim)); + assert(transform >= 0 && transform <= 7); + + /* + Compute the transformation from source coordinates (sx,sy) + to destination coordinates (dx,dy). + */ + int sxn = 0; + int syn = 2; + if(transform == 0) // identity + sxn = 0, syn = 2; // sx = dx, sy = dy + else if(transform == 1) // rotate by 90 degrees ccw + sxn = 3, syn = 0; // sx = (H-1) - dy, sy = dx + else if(transform == 2) // rotate by 180 degrees + sxn = 1, syn = 3; // sx = (W-1) - dx, sy = (H-1) - dy + else if(transform == 3) // rotate by 270 degrees ccw + sxn = 2, syn = 1; // sx = dy, sy = (W-1) - dx + else if(transform == 4) // flip x + sxn = 1, syn = 2; // sx = (W-1) - dx, sy = dy + else if(transform == 5) // flip x then rotate by 90 degrees ccw + sxn = 2, syn = 0; // sx = dy, sy = dx + else if(transform == 6) // flip y + sxn = 0, syn = 3; // sx = dx, sy = (H-1) - dy + else if(transform == 7) // flip y then rotate by 90 degrees ccw + sxn = 3, syn = 1; // sx = (H-1) - dy, sy = (W-1) - dx + + for(u32 dy=0; dygetPixel(sx,sy); + dst->setPixel(dx,dy,c); + } +}