From 0d1eedcccc8b83fd5f5a9a75389fe8ac97d2c697 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Thu, 5 Mar 2015 11:52:57 +0100 Subject: Replace std::list to std::vector into tile.cpp (m_texture_trash) and move tile.hpp to src/client/ --- build/android/jni/Android.mk | 4 +- src/CMakeLists.txt | 1 - src/camera.h | 2 +- src/client/CMakeLists.txt | 3 +- src/client/tile.cpp | 1954 +++++++++++++++++++++++++++++++++++++++++ src/client/tile.h | 280 ++++++ src/content_cao.cpp | 2 +- src/content_cso.cpp | 2 +- src/content_mapblock.cpp | 2 +- src/game.cpp | 2 +- src/guiChatConsole.cpp | 2 +- src/guiEngine.cpp | 2 +- src/guiEngine.h | 2 +- src/guiFormSpecMenu.cpp | 2 +- src/guiTable.cpp | 2 +- src/hud.cpp | 2 +- src/itemdef.cpp | 2 +- src/mapblock_mesh.h | 2 +- src/nodedef.cpp | 2 +- src/nodedef.h | 2 +- src/particles.cpp | 2 +- src/particles.h | 2 +- src/shader.cpp | 2 +- src/sky.cpp | 2 +- src/subgame.cpp | 2 +- src/tile.cpp | 1955 ------------------------------------------ src/tile.h | 280 ------ src/touchscreengui.h | 2 +- src/wieldmesh.cpp | 2 +- 29 files changed, 2260 insertions(+), 2261 deletions(-) create mode 100644 src/client/tile.cpp create mode 100644 src/client/tile.h delete mode 100644 src/tile.cpp delete mode 100644 src/tile.h diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index c3982c370..a35084433 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -196,7 +196,6 @@ LOCAL_SRC_FILES := \ jni/src/staticobject.cpp \ jni/src/subgame.cpp \ jni/src/test.cpp \ - jni/src/tile.cpp \ jni/src/tool.cpp \ jni/src/treegen.cpp \ jni/src/version.cpp \ @@ -214,7 +213,8 @@ LOCAL_SRC_FILES := \ jni/src/database-leveldb.cpp \ jni/src/settings.cpp \ jni/src/wieldmesh.cpp \ - jni/src/client/clientlauncher.cpp + jni/src/client/clientlauncher.cpp \ + jni/src/client/tile.cpp # Network LOCAL_SRC_FILES += \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6aa1a35a1..d56ec18c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -499,7 +499,6 @@ set(minetest_SRCS particles.cpp shader.cpp sky.cpp - tile.cpp wieldmesh.cpp ${minetest_SCRIPT_SRCS} ) diff --git a/src/camera.h b/src/camera.h index 3f10b87d7..b53738860 100644 --- a/src/camera.h +++ b/src/camera.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "inventory.h" #include "mesh.h" -#include "tile.h" +#include "client/tile.h" #include "util/numeric.h" #include diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 8cd981aca..288acf14c 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,4 +1,5 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp PARENT_SCOPE -) \ No newline at end of file +) diff --git a/src/client/tile.cpp b/src/client/tile.cpp new file mode 100644 index 000000000..e5d02de7c --- /dev/null +++ b/src/client/tile.cpp @@ -0,0 +1,1954 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "tile.h" + +#include +#include "util/string.h" +#include "util/container.h" +#include "util/thread.h" +#include "util/numeric.h" +#include "irrlichttypes_extrabloated.h" +#include "debug.h" +#include "main.h" // for g_settings +#include "filesys.h" +#include "settings.h" +#include "mesh.h" +#include "log.h" +#include "gamedef.h" +#include "strfnd.h" +#include "util/string.h" // for parseColorString() + +#ifdef __ANDROID__ +#include +#endif + +/* + A cache from texture name to texture path +*/ +MutexedMap g_texturename_to_path_cache; + +/* + Replaces the filename extension. + eg: + std::string image = "a/image.png" + replace_ext(image, "jpg") + -> image = "a/image.jpg" + Returns true on success. +*/ +static bool replace_ext(std::string &path, const char *ext) +{ + if (ext == NULL) + return false; + // Find place of last dot, fail if \ or / found. + s32 last_dot_i = -1; + for (s32 i=path.size()-1; i>=0; i--) + { + if (path[i] == '.') + { + last_dot_i = i; + break; + } + + if (path[i] == '\\' || path[i] == '/') + break; + } + // If not found, return an empty string + if (last_dot_i == -1) + return false; + // Else make the new path + path = path.substr(0, last_dot_i+1) + ext; + return true; +} + +/* + Find out the full path of an image by trying different filename + extensions. + + If failed, return "". +*/ +std::string getImagePath(std::string path) +{ + // A NULL-ended list of possible image extensions + const char *extensions[] = { + "png", "jpg", "bmp", "tga", + "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); + if (r == false) + return ""; + if (fs::PathExists(path)) + return path; + } + while((++ext) != NULL); + + return ""; +} + +/* + Gets the path to a texture by first checking if the texture exists + in texture_path and if not, using the data path. + + Checks all supported extensions by replacing the original extension. + + If not found, returns "". + + Utilizes a thread-safe cache. +*/ +std::string getTexturePath(const std::string &filename) +{ + std::string fullpath = ""; + /* + Check from cache + */ + bool incache = g_texturename_to_path_cache.get(filename, &fullpath); + if (incache) + return fullpath; + + /* + Check from texture_path + */ + std::string texture_path = g_settings->get("texture_path"); + if (texture_path != "") + { + std::string testpath = texture_path + DIR_DELIM + filename; + // Check all filename extensions. Returns "" if not found. + fullpath = getImagePath(testpath); + } + + /* + Check from default data directory + */ + if (fullpath == "") + { + std::string base_path = porting::path_share + DIR_DELIM + "textures" + + DIR_DELIM + "base" + DIR_DELIM + "pack"; + std::string testpath = base_path + DIR_DELIM + filename; + // Check all filename extensions. Returns "" if not found. + fullpath = getImagePath(testpath); + } + + // Add to cache (also an empty result is cached) + g_texturename_to_path_cache.set(filename, fullpath); + + // Finally return it + return fullpath; +} + +void clearTextureNameCache() +{ + g_texturename_to_path_cache.clear(); +} + +/* + Stores internal information about a texture. +*/ + +struct TextureInfo +{ + std::string name; + video::ITexture *texture; + + TextureInfo( + const std::string &name_, + video::ITexture *texture_=NULL + ): + name(name_), + texture(texture_) + { + } +}; + +/* + SourceImageCache: A cache used for storing source images. +*/ + +class SourceImageCache +{ +public: + ~SourceImageCache() { + for (std::map::iterator iter = m_images.begin(); + iter != m_images.end(); iter++) { + iter->second->drop(); + } + m_images.clear(); + } + void insert(const std::string &name, video::IImage *img, + bool prefer_local, video::IVideoDriver *driver) + { + assert(img); + // Remove old image + std::map::iterator n; + n = m_images.find(name); + if (n != m_images.end()){ + if (n->second) + n->second->drop(); + } + + video::IImage* toadd = img; + bool need_to_grab = true; + + // Try to use local texture instead if asked to + if (prefer_local){ + std::string path = getTexturePath(name); + if (path != ""){ + video::IImage *img2 = driver->createImageFromFile(path.c_str()); + if (img2){ + toadd = img2; + need_to_grab = false; + } + } + } + + if (need_to_grab) + toadd->grab(); + m_images[name] = toadd; + } + video::IImage* get(const std::string &name) + { + std::map::iterator n; + n = m_images.find(name); + if (n != m_images.end()) + return n->second; + return NULL; + } + // Primarily fetches from cache, secondarily tries to read from filesystem + video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device) + { + std::map::iterator n; + n = m_images.find(name); + if (n != m_images.end()){ + n->second->grab(); // Grab for caller + return n->second; + } + video::IVideoDriver* driver = device->getVideoDriver(); + std::string path = getTexturePath(name); + if (path == ""){ + infostream<<"SourceImageCache::getOrLoad(): No path found for \"" + <createImageFromFile(path.c_str()); + + if (img){ + m_images[name] = img; + img->grab(); // Grab for caller + } + return img; + } +private: + std::map m_images; +}; + +/* + TextureSource +*/ + +class TextureSource : public IWritableTextureSource +{ +public: + TextureSource(IrrlichtDevice *device); + virtual ~TextureSource(); + + /* + Example case: + Now, assume a texture with the id 1 exists, and has the name + "stone.png^mineral1". + Then a random thread calls getTextureId for a texture called + "stone.png^mineral1^crack0". + ...Now, WTF should happen? Well: + - getTextureId strips off stuff recursively from the end until + the remaining part is found, or nothing is left when + something is stripped out + + But it is slow to search for textures by names and modify them + like that? + - ContentFeatures is made to contain ids for the basic plain + textures + - Crack textures can be slow by themselves, but the framework + must be fast. + + Example case #2: + - Assume a texture with the id 1 exists, and has the name + "stone.png^mineral_coal.png". + - Now getNodeTile() stumbles upon a node which uses + texture id 1, and determines that MATERIAL_FLAG_CRACK + must be applied to the tile + - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and + has received the current crack level 0 from the client. It + finds out the name of the texture with getTextureName(1), + appends "^crack0" to it and gets a new texture id with + getTextureId("stone.png^mineral_coal.png^crack0"). + + */ + + /* + Gets a texture id from cache or + - if main thread, generates the texture, adds to cache and returns id. + - if other thread, adds to request queue and waits for main thread. + + The id 0 points to a NULL texture. It is returned in case of error. + */ + u32 getTextureId(const std::string &name); + + // Finds out the name of a cached texture. + std::string getTextureName(u32 id); + + /* + If texture specified by the name pointed by the id doesn't + exist, create it, then return the cached texture. + + Can be called from any thread. If called from some other thread + and not found in cache, the call is queued to the main thread + for processing. + */ + video::ITexture* getTexture(u32 id); + + video::ITexture* getTexture(const std::string &name, u32 *id); + + // Returns a pointer to the irrlicht device + virtual IrrlichtDevice* getDevice() + { + return m_device; + } + + 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. + void processQueue(); + + // Insert an image into the cache without touching the filesystem. + // Shall be called from the main thread. + void insertSourceImage(const std::string &name, video::IImage *img); + + // Rebuild images and textures from the current set of source images + // Shall be called from the main thread. + void rebuildImagesAndTextures(); + + // Render a mesh to a texture. + // Returns NULL if render-to-texture failed. + // Shall be called from the main thread. + video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms); + + // Generates an image from a full string like + // "stone.png^mineral_coal.png^[crack:1:0". + // Shall be called from the main thread. + video::IImage* generateImage(const std::string &name); + + video::ITexture* getNormalTexture(const std::string &name); +private: + + // The id of the thread that is allowed to use irrlicht directly + threadid_t m_main_thread; + // The irrlicht device + IrrlichtDevice *m_device; + + // Cache of source images + // This should be only accessed from the main thread + SourceImageCache m_sourcecache; + + // Generate a texture + u32 generateTexture(const std::string &name); + + // Generate image based on a string like "stone.png" or "[crack:1:0". + // if baseimg is NULL, it is created. Otherwise stuff is made on it. + bool generateImagePart(std::string part_of_name, video::IImage *& baseimg); + + // 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. + std::vector m_textureinfo_cache; + // Maps a texture name to an index in the former. + std::map m_name_to_id; + // The two former containers are behind this mutex + JMutex m_textureinfo_cache_mutex; + + // Queued texture fetches (to be processed by the main thread) + RequestQueue m_get_texture_queue; + + // Textures that have been overwritten with other ones + // but can't be deleted because the ITexture* might still be used + std::vector m_texture_trash; + + // Cached settings needed for making textures from meshes + bool m_setting_trilinear_filter; + bool m_setting_bilinear_filter; + bool m_setting_anisotropic_filter; +}; + +IWritableTextureSource* createTextureSource(IrrlichtDevice *device) +{ + return new TextureSource(device); +} + +TextureSource::TextureSource(IrrlichtDevice *device): + m_device(device) +{ + assert(m_device); + + m_main_thread = get_current_thread_id(); + + // Add a NULL TextureInfo as the first index, named "" + m_textureinfo_cache.push_back(TextureInfo("")); + m_name_to_id[""] = 0; + + // Cache some settings + // Note: Since this is only done once, the game must be restarted + // for these settings to take effect + m_setting_trilinear_filter = g_settings->getBool("trilinear_filter"); + m_setting_bilinear_filter = g_settings->getBool("bilinear_filter"); + m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter"); +} + +TextureSource::~TextureSource() +{ + video::IVideoDriver* driver = m_device->getVideoDriver(); + + unsigned int textures_before = driver->getTextureCount(); + + for (std::vector::iterator iter = + m_textureinfo_cache.begin(); + iter != m_textureinfo_cache.end(); iter++) + { + //cleanup texture + if (iter->texture) + driver->removeTexture(iter->texture); + } + m_textureinfo_cache.clear(); + + for (std::vector::iterator iter = + m_texture_trash.begin(); iter != m_texture_trash.end(); + iter++) { + video::ITexture *t = *iter; + + //cleanup trashed texture + driver->removeTexture(t); + } + + infostream << "~TextureSource() "<< textures_before << "/" + << driver->getTextureCount() << std::endl; +} + +u32 TextureSource::getTextureId(const std::string &name) +{ + //infostream<<"getTextureId(): \""<::iterator n; + n = m_name_to_id.find(name); + if (n != m_name_to_id.end()) + { + return n->second; + } + } + + /* + Get texture + */ + if (get_current_thread_id() == m_main_thread) + { + return generateTexture(name); + } + else + { + infostream<<"getTextureId(): Queued: name=\""< result_queue; + + // Throw a request in + m_get_texture_queue.add(name, 0, 0, &result_queue); + + /*infostream<<"Waiting for texture from main thread, name=\"" + < + result = result_queue.pop_front(1000); + + if (result.key == name) { + return result.item; + } + } + } + catch(ItemNotFoundException &e) + { + errorstream<<"Waiting for texture " << name << " timed out."< imageTransformDimension(u32 transform, core::dimension2d dim); +// Apply transform to image data +void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); + +/* + This method generates all the textures +*/ +u32 TextureSource::generateTexture(const std::string &name) +{ + //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl; + + // Empty name means texture 0 + if (name == "") { + infostream<<"generateTexture(): name is empty"<::iterator n; + n = m_name_to_id.find(name); + if (n != m_name_to_id.end()) { + return n->second; + } + } + + /* + Calling only allowed from main thread + */ + if (get_current_thread_id() != m_main_thread) { + errorstream<<"TextureSource::generateTexture() " + "called not from main thread"<getVideoDriver(); + assert(driver); + + video::IImage *img = generateImage(name); + + video::ITexture *tex = NULL; + + if (img != NULL) { +#ifdef __ANDROID__ + img = Align2Npot2(img, driver); +#endif + // Create texture from resulting image + tex = driver->addTexture(name.c_str(), img); + img->drop(); + } + + /* + Add texture to caches (add NULL textures too) + */ + + JMutexAutoLock lock(m_textureinfo_cache_mutex); + + u32 id = m_textureinfo_cache.size(); + TextureInfo ti(name, tex); + m_textureinfo_cache.push_back(ti); + m_name_to_id[name] = id; + + return id; +} + +std::string TextureSource::getTextureName(u32 id) +{ + JMutexAutoLock lock(m_textureinfo_cache_mutex); + + if (id >= m_textureinfo_cache.size()) + { + errorstream<<"TextureSource::getTextureName(): id="<= m_textureinfo_cache.size()=" + <= m_textureinfo_cache.size()) + return NULL; + + return m_textureinfo_cache[id].texture; +} + +video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id) +{ + u32 actual_id = getTextureId(name); + if (id){ + *id = actual_id; + } + return getTexture(actual_id); +} + +void TextureSource::processQueue() +{ + /* + Fetch textures + */ + //NOTE this is only thread safe for ONE consumer thread! + if (!m_get_texture_queue.empty()) + { + GetRequest + request = m_get_texture_queue.pop(); + + /*infostream<<"TextureSource::processQueue(): " + <<"got texture request with " + <<"name=\""<getBool("inventory_image_hack") + ) { + // Get a scene manager + scene::ISceneManager *smgr_main = m_device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + const float scaling = 0.2; + + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), + v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius*scaling); + + core::dimension2d screen = driver->getScreenSize(); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + driver->clearZBuffer(); + smgr->drawAll(); + + core::dimension2d partsize(screen.Width * scaling,screen.Height * scaling); + + irr::video::IImage* rawImage = + driver->createImage(irr::video::ECF_A8R8G8B8, partsize); + + u8* pixels = static_cast(rawImage->lock()); + if (!pixels) + { + rawImage->drop(); + return NULL; + } + + core::rect source( + screen.Width /2 - (screen.Width * (scaling / 2)), + screen.Height/2 - (screen.Height * (scaling / 2)), + screen.Width /2 + (screen.Width * (scaling / 2)), + screen.Height/2 + (screen.Height * (scaling / 2)) + ); + + glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y, + partsize.Width, partsize.Height, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + driver->endScene(); + + // Drop scene manager + smgr->drop(); + + unsigned int pixelcount = partsize.Width*partsize.Height; + + u8* runptr = pixels; + for (unsigned int i=0; i < pixelcount; i++) { + + u8 B = *runptr; + u8 G = *(runptr+1); + u8 R = *(runptr+2); + u8 A = *(runptr+3); + + //BGRA -> RGBA + *runptr = R; + runptr ++; + *runptr = G; + runptr ++; + *runptr = B; + runptr ++; + *runptr = A; + runptr ++; + } + + video::IImage* inventory_image = + driver->createImage(irr::video::ECF_A8R8G8B8, params.dim); + + rawImage->copyToScaling(inventory_image); + rawImage->drop(); + + video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image); + inventory_image->drop(); + + if (rtt == NULL) { + errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl; + return NULL; + } + + driver->makeColorKeyTexture(rtt, v2s32(0,0)); + + if (params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; + } +#endif + + if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) + { + static bool warned = false; + if (!warned) + { + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"EVDF_RENDER_TO_TARGET not supported."<addRenderTargetTexture( + params.dim, params.rtt_texture_name.c_str(), + video::ECF_A8R8G8B8); + if (rtt == NULL) + { + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"addRenderTargetTexture returned NULL."<setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) { + driver->removeTexture(rtt); + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"failed to set render target"<getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + smgr->drawAll(); + driver->endScene(); + + // Drop scene manager + smgr->drop(); + + // Unset render target + driver->setRenderTarget(0, false, true, 0); + + if (params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; +} + +video::IImage* TextureSource::generateImage(const std::string &name) +{ + /* + Get the base image + */ + + const char separator = '^'; + const char paren_open = '('; + const char paren_close = ')'; + + // Find last separator in the name + s32 last_separator_pos = -1; + u8 paren_bal = 0; + for (s32 i = name.size() - 1; i >= 0; i--) { + switch(name[i]) { + case separator: + if (paren_bal == 0) { + last_separator_pos = i; + i = -1; // break out of loop + } + break; + case paren_open: + if (paren_bal == 0) { + errorstream << "generateImage(): unbalanced parentheses" + << "(extranous '(') while generating texture \"" + << name << "\"" << std::endl; + return NULL; + } + paren_bal--; + break; + case paren_close: + paren_bal++; + break; + default: + break; + } + } + if (paren_bal > 0) { + errorstream << "generateImage(): unbalanced parentheses" + << "(missing matching '(') while generating texture \"" + << name << "\"" << std::endl; + return NULL; + } + + + video::IImage *baseimg = NULL; + + /* + If separator was found, make the base image + using a recursive call. + */ + if (last_separator_pos != -1) { + baseimg = generateImage(name.substr(0, last_separator_pos)); + } + + + video::IVideoDriver* driver = m_device->getVideoDriver(); + assert(driver); + + /* + Parse out the last part of the name of the image and act + according to it + */ + + std::string last_part_of_name = name.substr(last_separator_pos + 1); + + /* + If this name is enclosed in parentheses, generate it + and blit it onto the base image + */ + if (last_part_of_name[0] == paren_open + && last_part_of_name[last_part_of_name.size() - 1] == paren_close) { + std::string name2 = last_part_of_name.substr(1, + last_part_of_name.size() - 2); + video::IImage *tmp = generateImage(name2); + if (!tmp) { + errorstream << "generateImage(): " + "Failed to generate \"" << name2 << "\"" + << std::endl; + return NULL; + } + core::dimension2d dim = tmp->getDimension(); + if (!baseimg) + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim); + tmp->drop(); + } else if (!generateImagePart(last_part_of_name, baseimg)) { + // Generate image according to part of name + errorstream << "generateImage(): " + "Failed to generate \"" << last_part_of_name << "\"" + << std::endl; + } + + // If no resulting image, print a warning + if (baseimg == NULL) { + errorstream << "generateImage(): baseimg is NULL (attempted to" + " create texture \"" << name << "\")" << std::endl; + } + + return baseimg; +} + +#ifdef __ANDROID__ +#include +/** + * Check and align image to npot2 if required by hardware + * @param image image to check for npot2 alignment + * @param driver driver to use for image operations + * @return image or copy of image aligned to npot2 + */ +video::IImage * Align2Npot2(video::IImage * image, + video::IVideoDriver* driver) +{ + if (image == NULL) { + return image; + } + + core::dimension2d dim = image->getDimension(); + + std::string extensions = (char*) glGetString(GL_EXTENSIONS); + if (extensions.find("GL_OES_texture_npot") != std::string::npos) { + return image; + } + + unsigned int height = npot2(dim.Height); + unsigned int width = npot2(dim.Width); + + if ((dim.Height == height) && + (dim.Width == width)) { + return image; + } + + if (dim.Height > height) { + height *= 2; + } + + if (dim.Width > width) { + width *= 2; + } + + video::IImage *targetimage = + driver->createImage(video::ECF_A8R8G8B8, + core::dimension2d(width, height)); + + if (targetimage != NULL) { + image->copyToScaling(targetimage); + } + image->drop(); + return targetimage; +} + +#endif + +bool TextureSource::generateImagePart(std::string part_of_name, + video::IImage *& baseimg) +{ + video::IVideoDriver* driver = m_device->getVideoDriver(); + assert(driver); + + // Stuff starting with [ are special commands + if (part_of_name.size() == 0 || part_of_name[0] != '[') + { + video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); +#ifdef __ANDROID__ + image = Align2Npot2(image, driver); +#endif + if (image == NULL) { + if (part_of_name != "") { + if (part_of_name.find("_normal.png") == std::string::npos){ + errorstream<<"generateImage(): Could not load image \"" + < dim(2,2); + core::dimension2d dim(1,1); + image = driver->createImage(video::ECF_A8R8G8B8, dim); + assert(image); + /*image->setPixel(0,0, video::SColor(255,255,0,0)); + image->setPixel(1,0, video::SColor(255,0,255,0)); + image->setPixel(0,1, video::SColor(255,0,0,255)); + image->setPixel(1,1, video::SColor(255,255,0,255));*/ + image->setPixel(0,0, video::SColor(255,myrand()%256, + myrand()%256,myrand()%256)); + /*image->setPixel(1,0, video::SColor(255,myrand()%256, + myrand()%256,myrand()%256)); + image->setPixel(0,1, video::SColor(255,myrand()%256, + myrand()%256,myrand()%256)); + image->setPixel(1,1, video::SColor(255,myrand()%256, + myrand()%256,myrand()%256));*/ + } + + // If base image is NULL, load as base. + if (baseimg == NULL) + { + //infostream<<"Setting "< dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + image->copyTo(baseimg); + } + // Else blit on base. + else + { + //infostream<<"Blitting "< dim = image->getDimension(); + //core::dimension2d dim(16,16); + // Position to copy the blitted to in the base image + core::position2d pos_to(0,0); + // Position to copy the blitted from in the blitted image + core::position2d pos_from(0,0); + // Blit + /*image->copyToWithAlpha(baseimg, pos_to, + core::rect(pos_from, dim), + video::SColor(255,255,255,255), + NULL);*/ + blit_with_alpha(image, baseimg, pos_from, pos_to, dim); + } + //cleanup + image->drop(); + } + else + { + // A special texture modification + + /*infostream<<"generateImage(): generating special " + <<"modification \""<= 0) + { + draw_crack(img_crack, baseimg, + use_overlay, frame_count, + progression, driver); + img_crack->drop(); + } + } + /* + [combine:WxH:X,Y=filename:X,Y=filename2 + Creates a bigger texture from an amount of smaller ones + */ + else if (part_of_name.substr(0,8) == "[combine") + { + Strfnd sf(part_of_name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + //infostream<<"combined w="< dim = img->getDimension(); + infostream<<"Size "< pos_base(x, y); + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop(); + /*img2->copyToWithAlpha(baseimg, pos_base, + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL);*/ + blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim); + img2->drop(); + } else { + errorstream << "generateImagePart(): Failed to load image \"" + << filename << "\" for [combine" << std::endl; + } + } + } + /* + "[brighten" + */ + else if (part_of_name.substr(0,9) == "[brighten") + { + if (baseimg == NULL) { + errorstream<<"generateImagePart(): baseimg==NULL " + <<"for part_of_name=\""< dim = baseimg->getDimension(); + + // Set alpha to full + for (u32 y=0; ygetPixel(x,y); + c.setAlpha(255); + baseimg->setPixel(x,y,c); + } + } + /* + "[makealpha:R,G,B" + Convert one color to transparent. + */ + else if (part_of_name.substr(0,11) == "[makealpha:") + { + if (baseimg == NULL) { + errorstream<<"generateImagePart(): baseimg == NULL " + <<"for part_of_name=\""< dim = baseimg->getDimension(); + + /*video::IImage *oldbaseimg = baseimg; + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + oldbaseimg->copyTo(baseimg); + oldbaseimg->drop();*/ + + // Set alpha to full + for (u32 y=0; ygetPixel(x,y); + u32 r = c.getRed(); + u32 g = c.getGreen(); + u32 b = c.getBlue(); + if (!(r == r1 && g == g1 && b == b1)) + continue; + c.setAlpha(0); + 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<<"generateImagePart(): 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 &. + Create an "inventory cube". + NOTE: This should be used only on its own. + Example (a grass block (not actually used in game): + "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png" + */ + else if (part_of_name.substr(0,14) == "[inventorycube") + { + if (baseimg != NULL){ + errorstream<<"generateImagePart(): baseimg != NULL " + <<"for part_of_name=\""<getDimension().Height == npot2(img_top->getDimension().Height)); + assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width)); + + assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height)); + assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width)); + + assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height)); + assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width)); +#endif + + // Create textures from images + video::ITexture *texture_top = driver->addTexture( + (imagename_top + "__temp__").c_str(), img_top); + video::ITexture *texture_left = driver->addTexture( + (imagename_left + "__temp__").c_str(), img_left); + video::ITexture *texture_right = driver->addTexture( + (imagename_right + "__temp__").c_str(), img_right); + assert(texture_top && texture_left && texture_right); + + // Drop images + img_top->drop(); + img_left->drop(); + img_right->drop(); + + /* + Draw a cube mesh into a render target texture + */ + scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1)); + setMeshColor(cube, video::SColor(255, 255, 255, 255)); + cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right); + cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right); + cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left); + cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left); + + TextureFromMeshParams params; + params.mesh = cube; + params.dim.set(64, 64); + params.rtt_texture_name = part_of_name + "_RTT"; + // We will delete the rtt texture ourselves + params.delete_texture_on_shutdown = false; + params.camera_position.set(0, 1.0, -1.5); + params.camera_position.rotateXZBy(45); + params.camera_lookat.set(0, 0, 0); + // Set orthogonal projection + params.camera_projection_matrix.buildProjectionMatrixOrthoLH( + 1.65, 1.65, 0, 100); + + params.ambient_light.set(1.0, 0.2, 0.2, 0.2); + params.light_position.set(10, 100, -50); + params.light_color.set(1.0, 0.5, 0.5, 0.5); + params.light_radius = 1000; + + video::ITexture *rtt = generateTextureFromMesh(params); + + // Drop mesh + cube->drop(); + + // Free textures + driver->removeTexture(texture_top); + driver->removeTexture(texture_left); + driver->removeTexture(texture_right); + + if (rtt == NULL) { + baseimg = generateImage(imagename_top); + return true; + } + + // Create image of render target + video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim); + assert(image); + + // Cleanup texture + driver->removeTexture(rtt); + + baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim); + + if (image) { + image->copyTo(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 = m_sourcecache.getOrLoad(filename, m_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<<"generateImagePart(): 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<<"generateImagePart(): 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; + } + /* + [mask:filename + Applies a mask to an image + */ + else if (part_of_name.substr(0,6) == "[mask:") + { + if (baseimg == NULL) { + errorstream << "generateImage(): baseimg == NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + Strfnd sf(part_of_name); + sf.next(":"); + std::string filename = sf.next(":"); + + video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); + if (img) { + apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0), + img->getDimension()); + } else { + errorstream << "generateImage(): Failed to load \"" + << filename << "\"."; + } + } + /* + [colorize:color + Overlays image with given color + color = color as ColorString + */ + else if (part_of_name.substr(0,10) == "[colorize:") { + Strfnd sf(part_of_name); + sf.next(":"); + std::string color_str = sf.next(":"); + std::string ratio_str = sf.next(":"); + + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg != NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + video::SColor color; + int ratio = -1; + + if (!parseColorString(color_str, color, false)) + return false; + + if (is_number(ratio_str)) + ratio = mystoi(ratio_str, 0, 255); + + core::dimension2d dim = baseimg->getDimension(); + video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim); + + if (!img) { + errorstream << "generateImagePart(): Could not create image " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + img->fill(video::SColor(color)); + // Overlay the colored image + blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio); + img->drop(); + } + else + { + errorstream << "generateImagePart(): Invalid " + " modification: \"" << part_of_name << "\"" << std::endl; + } + } + + return true; +} + +/* + 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); + } +} + +/* + Draw an image on top of an another one, using the alpha channel of the + source image; only modify fully opaque pixels in destinaion +*/ +static void blit_with_alpha_overlay(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); + if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) + { + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + +/* + Draw an image on top of an another one, using the specified ratio + modify all partially-opaque pixels in the destination. +*/ +static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio) +{ + for (u32 y0 = 0; y0 < size.Y; y0++) + for (u32 x0 = 0; x0 < size.X; x0++) + { + s32 src_x = src_pos.X + x0; + s32 src_y = src_pos.Y + y0; + s32 dst_x = dst_pos.X + x0; + s32 dst_y = dst_pos.Y + y0; + video::SColor src_c = src->getPixel(src_x, src_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0) + { + if (ratio == -1) + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + else + dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + +/* + Apply mask to destination +*/ +static void apply_mask(video::IImage *mask, video::IImage *dst, + v2s32 mask_pos, v2s32 dst_pos, v2u32 size) +{ + for (u32 y0 = 0; y0 < size.Y; y0++) { + for (u32 x0 = 0; x0 < size.X; x0++) { + s32 mask_x = x0 + mask_pos.X; + s32 mask_y = y0 + mask_pos.Y; + s32 dst_x = x0 + dst_pos.X; + s32 dst_y = y0 + dst_pos.Y; + video::SColor mask_c = mask->getPixel(mask_x, mask_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + dst_c.color &= mask_c.color; + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + +static void draw_crack(video::IImage *crack, video::IImage *dst, + bool use_overlay, s32 frame_count, s32 progression, + video::IVideoDriver *driver) +{ + // Dimension of destination image + core::dimension2d dim_dst = dst->getDimension(); + // Dimension of original image + core::dimension2d dim_crack = crack->getDimension(); + // Count of crack stages + s32 crack_count = dim_crack.Height / dim_crack.Width; + // Limit frame_count + if (frame_count > (s32) dim_dst.Height) + frame_count = dim_dst.Height; + if (frame_count < 1) + frame_count = 1; + // Limit progression + if (progression > crack_count-1) + progression = crack_count-1; + // Dimension of a single crack stage + core::dimension2d dim_crack_cropped( + dim_crack.Width, + dim_crack.Width + ); + // Dimension of the scaled crack stage, + // which is the same as the dimension of a single destination frame + core::dimension2d dim_crack_scaled( + dim_dst.Width, + dim_dst.Height / frame_count + ); + // Create cropped and scaled crack images + video::IImage *crack_cropped = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_cropped); + video::IImage *crack_scaled = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_scaled); + + if (crack_cropped && crack_scaled) + { + // Crop crack image + v2s32 pos_crack(0, progression*dim_crack.Width); + crack->copyTo(crack_cropped, + v2s32(0,0), + core::rect(pos_crack, dim_crack_cropped)); + // Scale crack image by copying + crack_cropped->copyToScaling(crack_scaled); + // Copy or overlay crack image onto each frame + for (s32 i = 0; i < frame_count; ++i) + { + v2s32 dst_pos(0, dim_crack_scaled.Height * i); + if (use_overlay) + { + blit_with_alpha_overlay(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + else + { + blit_with_alpha(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + } + } + + if (crack_scaled) + crack_scaled->drop(); + + if (crack_cropped) + crack_cropped->drop(); +} + +void brighten(video::IImage *image) +{ + if (image == NULL) + return; + + core::dimension2d dim = image->getDimension(); + + for (u32 y=0; ygetPixel(x,y); + c.setRed(0.5 * 255 + 0.5 * (float)c.getRed()); + c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen()); + c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue()); + image->setPixel(x,y,c); + } +} + +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 <= 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); + } +} + +video::ITexture* TextureSource::getNormalTexture(const std::string &name) +{ + u32 id; + if (isKnownSourceImage("override_normal.png")) + return getTexture("override_normal.png", &id); + std::string fname_base = name; + std::string normal_ext = "_normal.png"; + size_t pos = fname_base.find("."); + std::string fname_normal = fname_base.substr(0, pos) + normal_ext; + if (isKnownSourceImage(fname_normal)) { + // look for image extension and replace it + size_t i = 0; + while ((i = fname_base.find(".", i)) != std::string::npos) { + fname_base.replace(i, 4, normal_ext); + i += normal_ext.length(); + } + return getTexture(fname_base, &id); + } + return NULL; +} diff --git a/src/client/tile.h b/src/client/tile.h new file mode 100644 index 000000000..ea7a9135a --- /dev/null +++ b/src/client/tile.h @@ -0,0 +1,280 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#ifndef TILE_HEADER +#define TILE_HEADER + +#include "irrlichttypes.h" +#include "irr_v2d.h" +#include "irr_v3d.h" +#include +#include +#include "threads.h" +#include +#include + +class IGameDef; + +/* + tile.{h,cpp}: Texture handling stuff. +*/ + +/* + Find out the full path of an image by trying different filename + extensions. + + If failed, return "". + + TODO: Should probably be moved out from here, because things needing + this function do not need anything else from this header +*/ +std::string getImagePath(std::string path); + +/* + Gets the path to a texture by first checking if the texture exists + in texture_path and if not, using the data path. + + Checks all supported extensions by replacing the original extension. + + If not found, returns "". + + Utilizes a thread-safe cache. +*/ +std::string getTexturePath(const std::string &filename); + +void clearTextureNameCache(); + +/* + ITextureSource::generateTextureFromMesh parameters +*/ +namespace irr {namespace scene {class IMesh;}} +struct TextureFromMeshParams +{ + scene::IMesh *mesh; + core::dimension2d dim; + std::string rtt_texture_name; + bool delete_texture_on_shutdown; + v3f camera_position; + v3f camera_lookat; + core::CMatrix4 camera_projection_matrix; + video::SColorf ambient_light; + v3f light_position; + video::SColorf light_color; + f32 light_radius; +}; + +/* + TextureSource creates and caches textures. +*/ + +class ISimpleTextureSource +{ +public: + ISimpleTextureSource(){} + virtual ~ISimpleTextureSource(){} + virtual video::ITexture* getTexture( + const std::string &name, u32 *id = NULL) = 0; +}; + +class ITextureSource : public ISimpleTextureSource +{ +public: + ITextureSource(){} + virtual ~ITextureSource(){} + virtual u32 getTextureId(const std::string &name)=0; + virtual std::string getTextureName(u32 id)=0; + virtual video::ITexture* getTexture(u32 id)=0; + virtual video::ITexture* getTexture( + const std::string &name, u32 *id = NULL)=0; + virtual IrrlichtDevice* getDevice()=0; + virtual bool isKnownSourceImage(const std::string &name)=0; + virtual video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms)=0; + virtual video::ITexture* getNormalTexture(const std::string &name)=0; +}; + +class IWritableTextureSource : public ITextureSource +{ +public: + IWritableTextureSource(){} + virtual ~IWritableTextureSource(){} + virtual u32 getTextureId(const std::string &name)=0; + virtual std::string getTextureName(u32 id)=0; + virtual video::ITexture* getTexture(u32 id)=0; + virtual video::ITexture* getTexture( + const std::string &name, u32 *id = NULL)=0; + virtual IrrlichtDevice* getDevice()=0; + virtual bool isKnownSourceImage(const std::string &name)=0; + virtual video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms)=0; + + virtual void processQueue()=0; + virtual void insertSourceImage(const std::string &name, video::IImage *img)=0; + virtual void rebuildImagesAndTextures()=0; + virtual video::ITexture* getNormalTexture(const std::string &name)=0; +}; + +IWritableTextureSource* createTextureSource(IrrlichtDevice *device); + +#ifdef __ANDROID__ +/** + * @param size get next npot2 value + * @return npot2 value + */ +inline unsigned int npot2(unsigned int size) +{ + if (size == 0) return 0; + unsigned int npot = 1; + + while ((size >>= 1) > 0) { + npot <<= 1; + } + return npot; +} + +video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver); +#endif + +enum MaterialType{ + TILE_MATERIAL_BASIC, + TILE_MATERIAL_ALPHA, + TILE_MATERIAL_LIQUID_TRANSPARENT, + TILE_MATERIAL_LIQUID_OPAQUE, + TILE_MATERIAL_WAVING_LEAVES, + TILE_MATERIAL_WAVING_PLANTS +}; + +// Material flags +// Should backface culling be enabled? +#define MATERIAL_FLAG_BACKFACE_CULLING 0x01 +// Should a crack be drawn? +#define MATERIAL_FLAG_CRACK 0x02 +// Should the crack be drawn on transparent pixels (unset) or not (set)? +// Ignored if MATERIAL_FLAG_CRACK is not set. +#define MATERIAL_FLAG_CRACK_OVERLAY 0x04 +// Animation made up by splitting the texture to vertical frames, as +// defined by extra parameters +#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08 +#define MATERIAL_FLAG_HIGHLIGHTED 0x10 + +/* + This fully defines the looks of a tile. + The SMaterial of a tile is constructed according to this. +*/ +struct FrameSpec +{ + FrameSpec(): + texture_id(0), + texture(NULL), + normal_texture(NULL) + { + } + u32 texture_id; + video::ITexture *texture; + video::ITexture *normal_texture; +}; + +struct TileSpec +{ + TileSpec(): + texture_id(0), + texture(NULL), + normal_texture(NULL), + alpha(255), + material_type(TILE_MATERIAL_BASIC), + material_flags( + //0 // <- DEBUG, Use the one below + MATERIAL_FLAG_BACKFACE_CULLING + ), + shader_id(0), + animation_frame_count(1), + animation_frame_length_ms(0), + rotation(0) + { + } + + bool operator==(const TileSpec &other) const + { + return ( + texture_id == other.texture_id && + /* texture == other.texture && */ + alpha == other.alpha && + material_type == other.material_type && + material_flags == other.material_flags && + rotation == other.rotation + ); + } + + bool operator!=(const TileSpec &other) const + { + return !(*this == other); + } + + // Sets everything else except the texture in the material + void applyMaterialOptions(video::SMaterial &material) const + { + switch (material_type) { + case TILE_MATERIAL_BASIC: + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + break; + case TILE_MATERIAL_ALPHA: + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + break; + case TILE_MATERIAL_LIQUID_TRANSPARENT: + material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + break; + case TILE_MATERIAL_LIQUID_OPAQUE: + material.MaterialType = video::EMT_SOLID; + break; + case TILE_MATERIAL_WAVING_LEAVES: + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + break; + case TILE_MATERIAL_WAVING_PLANTS: + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + break; + } + material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) + ? true : false; + } + + void applyMaterialOptionsWithShaders(video::SMaterial &material) const + { + material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) + ? true : false; + } + + u32 texture_id; + video::ITexture *texture; + video::ITexture *normal_texture; + + // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used) + u8 alpha; + // Material parameters + u8 material_type; + u8 material_flags; + u32 shader_id; + // Animation parameters + u8 animation_frame_count; + u16 animation_frame_length_ms; + std::vector frames; + + u8 rotation; +}; + +#endif diff --git a/src/content_cao.cpp b/src/content_cao.cpp index ddfa3b032..5c3c7b54c 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" // For IntervalLimiter #include "util/serialize.h" #include "util/mathconstants.h" -#include "tile.h" +#include "client/tile.h" #include "environment.h" #include "collision.h" #include "settings.h" diff --git a/src/content_cso.cpp b/src/content_cso.cpp index 4779b20d1..c337472ff 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cso.h" #include -#include "tile.h" +#include "client/tile.h" #include "environment.h" #include "gamedef.h" #include "log.h" diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 54b9ac99f..5bb787d01 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector #include "settings.h" #include "nodedef.h" -#include "tile.h" +#include "client/tile.h" #include "mesh.h" #include #include "gamedef.h" diff --git a/src/game.cpp b/src/game.cpp index 1fe1c4163..af76d2e02 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -51,7 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodemetadata.h" #include "main.h" // For g_settings #include "itemdef.h" -#include "tile.h" // For TextureSource +#include "client/tile.h" // For TextureSource #include "shader.h" // For ShaderSource #include "logoutputbuffer.h" #include "subgame.h" diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 8210e0bf4..b52589044 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "main.h" // for g_settings #include "porting.h" -#include "tile.h" +#include "client/tile.h" #include "fontengine.h" #include "log.h" #include "gettext.h" diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index de6f44134..b45011c2c 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "fontengine.h" #ifdef __ANDROID__ -#include "tile.h" +#include "client/tile.h" #include #endif diff --git a/src/guiEngine.h b/src/guiEngine.h index 0be8634dd..e57573220 100644 --- a/src/guiEngine.h +++ b/src/guiEngine.h @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modalMenu.h" #include "guiFormSpecMenu.h" #include "sound.h" -#include "tile.h" +#include "client/tile.h" /******************************************************************************/ /* Typedefs and macros */ diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index a3cc03131..581da894b 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "log.h" -#include "tile.h" // ITextureSource +#include "client/tile.h" // ITextureSource #include "hud.h" // drawItemStack #include "filesys.h" #include "gettime.h" diff --git a/src/guiTable.cpp b/src/guiTable.cpp index 05db228da..a7a53f581 100644 --- a/src/guiTable.cpp +++ b/src/guiTable.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "debug.h" #include "log.h" -#include "tile.h" +#include "client/tile.h" #include "gettime.h" #include "util/string.h" #include "util/numeric.h" diff --git a/src/hud.cpp b/src/hud.cpp index 29ebb8103..aabaa066c 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "itemdef.h" #include "inventory.h" -#include "tile.h" +#include "client/tile.h" #include "localplayer.h" #include "camera.h" #include "porting.h" diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 3b4d2596a..381773bb4 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock_mesh.h" #include "mesh.h" #include "wieldmesh.h" -#include "tile.h" +#include "client/tile.h" #endif #include "log.h" #include "main.h" // g_settings diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 98af92c74..b334ce469 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAPBLOCK_MESH_HEADER #include "irrlichttypes_extrabloated.h" -#include "tile.h" +#include "client/tile.h" #include "voxel.h" #include diff --git a/src/nodedef.cpp b/src/nodedef.cpp index bcf51a072..ac49b4f3c 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_settings #include "itemdef.h" #ifndef SERVER -#include "tile.h" +#include "client/tile.h" #include "mesh.h" #include #endif diff --git a/src/nodedef.h b/src/nodedef.h index 7280bb8ea..52ef29e50 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "mapnode.h" #ifndef SERVER -#include "tile.h" +#include "client/tile.h" #include "shader.h" #endif #include "itemgroup.h" diff --git a/src/particles.cpp b/src/particles.cpp index 64b8936bd..a137aaaba 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "main.h" // For g_profiler and g_settings #include "settings.h" -#include "tile.h" +#include "client/tile.h" #include "gamedef.h" #include "collision.h" #include diff --git a/src/particles.h b/src/particles.h index d7f1147f1..2bc2e7bfa 100644 --- a/src/particles.h +++ b/src/particles.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_extrabloated.h" -#include "tile.h" +#include "client/tile.h" #include "localplayer.h" #include "environment.h" diff --git a/src/shader.cpp b/src/shader.cpp index 167045804..8c572ed49 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "gamedef.h" #include "strfnd.h" // trim() -#include "tile.h" +#include "client/tile.h" /* A cache from shader name to shader path diff --git a/src/sky.cpp b/src/sky.cpp index 664ed694b..ac8e2cbf6 100644 --- a/src/sky.cpp +++ b/src/sky.cpp @@ -3,7 +3,7 @@ #include "ISceneManager.h" #include "ICameraSceneNode.h" #include "S3DVertex.h" -#include "tile.h" +#include "client/tile.h" #include "noise.h" // easeCurve #include "main.h" // g_profiler #include "profiler.h" diff --git a/src/subgame.cpp b/src/subgame.cpp index fd2679eae..ccf477e8f 100644 --- a/src/subgame.cpp +++ b/src/subgame.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "strfnd.h" #ifndef SERVER -#include "tile.h" // getImagePath +#include "client/tile.h" // getImagePath #endif #include "util/string.h" diff --git a/src/tile.cpp b/src/tile.cpp deleted file mode 100644 index 81b362d61..000000000 --- a/src/tile.cpp +++ /dev/null @@ -1,1955 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -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 Lesser General Public License for more details. - -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. -*/ - -#include "tile.h" - -#include -#include "util/string.h" -#include "util/container.h" -#include "util/thread.h" -#include "util/numeric.h" -#include "irrlichttypes_extrabloated.h" -#include "debug.h" -#include "main.h" // for g_settings -#include "filesys.h" -#include "settings.h" -#include "mesh.h" -#include "log.h" -#include "gamedef.h" -#include "strfnd.h" -#include "util/string.h" // for parseColorString() - -#ifdef __ANDROID__ -#include -#endif - -/* - A cache from texture name to texture path -*/ -MutexedMap g_texturename_to_path_cache; - -/* - Replaces the filename extension. - eg: - std::string image = "a/image.png" - replace_ext(image, "jpg") - -> image = "a/image.jpg" - Returns true on success. -*/ -static bool replace_ext(std::string &path, const char *ext) -{ - if (ext == NULL) - return false; - // Find place of last dot, fail if \ or / found. - s32 last_dot_i = -1; - for (s32 i=path.size()-1; i>=0; i--) - { - if (path[i] == '.') - { - last_dot_i = i; - break; - } - - if (path[i] == '\\' || path[i] == '/') - break; - } - // If not found, return an empty string - if (last_dot_i == -1) - return false; - // Else make the new path - path = path.substr(0, last_dot_i+1) + ext; - return true; -} - -/* - Find out the full path of an image by trying different filename - extensions. - - If failed, return "". -*/ -std::string getImagePath(std::string path) -{ - // A NULL-ended list of possible image extensions - const char *extensions[] = { - "png", "jpg", "bmp", "tga", - "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); - if (r == false) - return ""; - if (fs::PathExists(path)) - return path; - } - while((++ext) != NULL); - - return ""; -} - -/* - Gets the path to a texture by first checking if the texture exists - in texture_path and if not, using the data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getTexturePath(const std::string &filename) -{ - std::string fullpath = ""; - /* - Check from cache - */ - bool incache = g_texturename_to_path_cache.get(filename, &fullpath); - if (incache) - return fullpath; - - /* - Check from texture_path - */ - std::string texture_path = g_settings->get("texture_path"); - if (texture_path != "") - { - std::string testpath = texture_path + DIR_DELIM + filename; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - /* - Check from default data directory - */ - if (fullpath == "") - { - std::string base_path = porting::path_share + DIR_DELIM + "textures" - + DIR_DELIM + "base" + DIR_DELIM + "pack"; - std::string testpath = base_path + DIR_DELIM + filename; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - // Add to cache (also an empty result is cached) - g_texturename_to_path_cache.set(filename, fullpath); - - // Finally return it - return fullpath; -} - -void clearTextureNameCache() -{ - g_texturename_to_path_cache.clear(); -} - -/* - Stores internal information about a texture. -*/ - -struct TextureInfo -{ - std::string name; - video::ITexture *texture; - - TextureInfo( - const std::string &name_, - video::ITexture *texture_=NULL - ): - name(name_), - texture(texture_) - { - } -}; - -/* - SourceImageCache: A cache used for storing source images. -*/ - -class SourceImageCache -{ -public: - ~SourceImageCache() { - for (std::map::iterator iter = m_images.begin(); - iter != m_images.end(); iter++) { - iter->second->drop(); - } - m_images.clear(); - } - void insert(const std::string &name, video::IImage *img, - bool prefer_local, video::IVideoDriver *driver) - { - assert(img); - // Remove old image - std::map::iterator n; - n = m_images.find(name); - if (n != m_images.end()){ - if (n->second) - n->second->drop(); - } - - video::IImage* toadd = img; - bool need_to_grab = true; - - // Try to use local texture instead if asked to - if (prefer_local){ - std::string path = getTexturePath(name); - if (path != ""){ - video::IImage *img2 = driver->createImageFromFile(path.c_str()); - if (img2){ - toadd = img2; - need_to_grab = false; - } - } - } - - if (need_to_grab) - toadd->grab(); - m_images[name] = toadd; - } - video::IImage* get(const std::string &name) - { - std::map::iterator n; - n = m_images.find(name); - if (n != m_images.end()) - return n->second; - return NULL; - } - // Primarily fetches from cache, secondarily tries to read from filesystem - video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device) - { - std::map::iterator n; - n = m_images.find(name); - if (n != m_images.end()){ - n->second->grab(); // Grab for caller - return n->second; - } - video::IVideoDriver* driver = device->getVideoDriver(); - std::string path = getTexturePath(name); - if (path == ""){ - infostream<<"SourceImageCache::getOrLoad(): No path found for \"" - <createImageFromFile(path.c_str()); - - if (img){ - m_images[name] = img; - img->grab(); // Grab for caller - } - return img; - } -private: - std::map m_images; -}; - -/* - TextureSource -*/ - -class TextureSource : public IWritableTextureSource -{ -public: - TextureSource(IrrlichtDevice *device); - virtual ~TextureSource(); - - /* - Example case: - Now, assume a texture with the id 1 exists, and has the name - "stone.png^mineral1". - Then a random thread calls getTextureId for a texture called - "stone.png^mineral1^crack0". - ...Now, WTF should happen? Well: - - getTextureId strips off stuff recursively from the end until - the remaining part is found, or nothing is left when - something is stripped out - - But it is slow to search for textures by names and modify them - like that? - - ContentFeatures is made to contain ids for the basic plain - textures - - Crack textures can be slow by themselves, but the framework - must be fast. - - Example case #2: - - Assume a texture with the id 1 exists, and has the name - "stone.png^mineral_coal.png". - - Now getNodeTile() stumbles upon a node which uses - texture id 1, and determines that MATERIAL_FLAG_CRACK - must be applied to the tile - - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and - has received the current crack level 0 from the client. It - finds out the name of the texture with getTextureName(1), - appends "^crack0" to it and gets a new texture id with - getTextureId("stone.png^mineral_coal.png^crack0"). - - */ - - /* - Gets a texture id from cache or - - if main thread, generates the texture, adds to cache and returns id. - - if other thread, adds to request queue and waits for main thread. - - The id 0 points to a NULL texture. It is returned in case of error. - */ - u32 getTextureId(const std::string &name); - - // Finds out the name of a cached texture. - std::string getTextureName(u32 id); - - /* - If texture specified by the name pointed by the id doesn't - exist, create it, then return the cached texture. - - Can be called from any thread. If called from some other thread - and not found in cache, the call is queued to the main thread - for processing. - */ - video::ITexture* getTexture(u32 id); - - video::ITexture* getTexture(const std::string &name, u32 *id); - - // Returns a pointer to the irrlicht device - virtual IrrlichtDevice* getDevice() - { - return m_device; - } - - 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. - void processQueue(); - - // Insert an image into the cache without touching the filesystem. - // Shall be called from the main thread. - void insertSourceImage(const std::string &name, video::IImage *img); - - // Rebuild images and textures from the current set of source images - // Shall be called from the main thread. - void rebuildImagesAndTextures(); - - // Render a mesh to a texture. - // Returns NULL if render-to-texture failed. - // Shall be called from the main thread. - video::ITexture* generateTextureFromMesh( - const TextureFromMeshParams ¶ms); - - // Generates an image from a full string like - // "stone.png^mineral_coal.png^[crack:1:0". - // Shall be called from the main thread. - video::IImage* generateImage(const std::string &name); - - video::ITexture* getNormalTexture(const std::string &name); -private: - - // The id of the thread that is allowed to use irrlicht directly - threadid_t m_main_thread; - // The irrlicht device - IrrlichtDevice *m_device; - - // Cache of source images - // This should be only accessed from the main thread - SourceImageCache m_sourcecache; - - // Generate a texture - u32 generateTexture(const std::string &name); - - // Generate image based on a string like "stone.png" or "[crack:1:0". - // if baseimg is NULL, it is created. Otherwise stuff is made on it. - bool generateImagePart(std::string part_of_name, video::IImage *& baseimg); - - // 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. - std::vector m_textureinfo_cache; - // Maps a texture name to an index in the former. - std::map m_name_to_id; - // The two former containers are behind this mutex - JMutex m_textureinfo_cache_mutex; - - // Queued texture fetches (to be processed by the main thread) - RequestQueue m_get_texture_queue; - - // Textures that have been overwritten with other ones - // but can't be deleted because the ITexture* might still be used - std::list m_texture_trash; - - // Cached settings needed for making textures from meshes - bool m_setting_trilinear_filter; - bool m_setting_bilinear_filter; - bool m_setting_anisotropic_filter; -}; - -IWritableTextureSource* createTextureSource(IrrlichtDevice *device) -{ - return new TextureSource(device); -} - -TextureSource::TextureSource(IrrlichtDevice *device): - m_device(device) -{ - assert(m_device); - - m_main_thread = get_current_thread_id(); - - // Add a NULL TextureInfo as the first index, named "" - m_textureinfo_cache.push_back(TextureInfo("")); - m_name_to_id[""] = 0; - - // Cache some settings - // Note: Since this is only done once, the game must be restarted - // for these settings to take effect - m_setting_trilinear_filter = g_settings->getBool("trilinear_filter"); - m_setting_bilinear_filter = g_settings->getBool("bilinear_filter"); - m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter"); -} - -TextureSource::~TextureSource() -{ - video::IVideoDriver* driver = m_device->getVideoDriver(); - - unsigned int textures_before = driver->getTextureCount(); - - for (std::vector::iterator iter = - m_textureinfo_cache.begin(); - iter != m_textureinfo_cache.end(); iter++) - { - //cleanup texture - if (iter->texture) - driver->removeTexture(iter->texture); - } - m_textureinfo_cache.clear(); - - for (std::list::iterator iter = - m_texture_trash.begin(); iter != m_texture_trash.end(); - iter++) - { - video::ITexture *t = *iter; - - //cleanup trashed texture - driver->removeTexture(t); - } - - infostream << "~TextureSource() "<< textures_before << "/" - << driver->getTextureCount() << std::endl; -} - -u32 TextureSource::getTextureId(const std::string &name) -{ - //infostream<<"getTextureId(): \""<::iterator n; - n = m_name_to_id.find(name); - if (n != m_name_to_id.end()) - { - return n->second; - } - } - - /* - Get texture - */ - if (get_current_thread_id() == m_main_thread) - { - return generateTexture(name); - } - else - { - infostream<<"getTextureId(): Queued: name=\""< result_queue; - - // Throw a request in - m_get_texture_queue.add(name, 0, 0, &result_queue); - - /*infostream<<"Waiting for texture from main thread, name=\"" - < - result = result_queue.pop_front(1000); - - if (result.key == name) { - return result.item; - } - } - } - catch(ItemNotFoundException &e) - { - errorstream<<"Waiting for texture " << name << " timed out."< imageTransformDimension(u32 transform, core::dimension2d dim); -// Apply transform to image data -void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); - -/* - This method generates all the textures -*/ -u32 TextureSource::generateTexture(const std::string &name) -{ - //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl; - - // Empty name means texture 0 - if (name == "") { - infostream<<"generateTexture(): name is empty"<::iterator n; - n = m_name_to_id.find(name); - if (n != m_name_to_id.end()) { - return n->second; - } - } - - /* - Calling only allowed from main thread - */ - if (get_current_thread_id() != m_main_thread) { - errorstream<<"TextureSource::generateTexture() " - "called not from main thread"<getVideoDriver(); - assert(driver); - - video::IImage *img = generateImage(name); - - video::ITexture *tex = NULL; - - if (img != NULL) { -#ifdef __ANDROID__ - img = Align2Npot2(img, driver); -#endif - // Create texture from resulting image - tex = driver->addTexture(name.c_str(), img); - img->drop(); - } - - /* - Add texture to caches (add NULL textures too) - */ - - JMutexAutoLock lock(m_textureinfo_cache_mutex); - - u32 id = m_textureinfo_cache.size(); - TextureInfo ti(name, tex); - m_textureinfo_cache.push_back(ti); - m_name_to_id[name] = id; - - return id; -} - -std::string TextureSource::getTextureName(u32 id) -{ - JMutexAutoLock lock(m_textureinfo_cache_mutex); - - if (id >= m_textureinfo_cache.size()) - { - errorstream<<"TextureSource::getTextureName(): id="<= m_textureinfo_cache.size()=" - <= m_textureinfo_cache.size()) - return NULL; - - return m_textureinfo_cache[id].texture; -} - -video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id) -{ - u32 actual_id = getTextureId(name); - if (id){ - *id = actual_id; - } - return getTexture(actual_id); -} - -void TextureSource::processQueue() -{ - /* - Fetch textures - */ - //NOTE this is only thread safe for ONE consumer thread! - if (!m_get_texture_queue.empty()) - { - GetRequest - request = m_get_texture_queue.pop(); - - /*infostream<<"TextureSource::processQueue(): " - <<"got texture request with " - <<"name=\""<getBool("inventory_image_hack") - ) { - // Get a scene manager - scene::ISceneManager *smgr_main = m_device->getSceneManager(); - assert(smgr_main); - scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); - assert(smgr); - - const float scaling = 0.2; - - scene::IMeshSceneNode* meshnode = - smgr->addMeshSceneNode(params.mesh, NULL, - -1, v3f(0,0,0), v3f(0,0,0), - v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true); - meshnode->setMaterialFlag(video::EMF_LIGHTING, true); - meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); - meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); - meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); - meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, - params.camera_position, params.camera_lookat); - // second parameter of setProjectionMatrix (isOrthogonal) is ignored - camera->setProjectionMatrix(params.camera_projection_matrix, false); - - smgr->setAmbientLight(params.ambient_light); - smgr->addLightSceneNode(0, - params.light_position, - params.light_color, - params.light_radius*scaling); - - core::dimension2d screen = driver->getScreenSize(); - - // Render scene - driver->beginScene(true, true, video::SColor(0,0,0,0)); - driver->clearZBuffer(); - smgr->drawAll(); - - core::dimension2d partsize(screen.Width * scaling,screen.Height * scaling); - - irr::video::IImage* rawImage = - driver->createImage(irr::video::ECF_A8R8G8B8, partsize); - - u8* pixels = static_cast(rawImage->lock()); - if (!pixels) - { - rawImage->drop(); - return NULL; - } - - core::rect source( - screen.Width /2 - (screen.Width * (scaling / 2)), - screen.Height/2 - (screen.Height * (scaling / 2)), - screen.Width /2 + (screen.Width * (scaling / 2)), - screen.Height/2 + (screen.Height * (scaling / 2)) - ); - - glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y, - partsize.Width, partsize.Height, GL_RGBA, - GL_UNSIGNED_BYTE, pixels); - - driver->endScene(); - - // Drop scene manager - smgr->drop(); - - unsigned int pixelcount = partsize.Width*partsize.Height; - - u8* runptr = pixels; - for (unsigned int i=0; i < pixelcount; i++) { - - u8 B = *runptr; - u8 G = *(runptr+1); - u8 R = *(runptr+2); - u8 A = *(runptr+3); - - //BGRA -> RGBA - *runptr = R; - runptr ++; - *runptr = G; - runptr ++; - *runptr = B; - runptr ++; - *runptr = A; - runptr ++; - } - - video::IImage* inventory_image = - driver->createImage(irr::video::ECF_A8R8G8B8, params.dim); - - rawImage->copyToScaling(inventory_image); - rawImage->drop(); - - video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image); - inventory_image->drop(); - - if (rtt == NULL) { - errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl; - return NULL; - } - - driver->makeColorKeyTexture(rtt, v2s32(0,0)); - - if (params.delete_texture_on_shutdown) - m_texture_trash.push_back(rtt); - - return rtt; - } -#endif - - if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) - { - static bool warned = false; - if (!warned) - { - errorstream<<"TextureSource::generateTextureFromMesh(): " - <<"EVDF_RENDER_TO_TARGET not supported."<addRenderTargetTexture( - params.dim, params.rtt_texture_name.c_str(), - video::ECF_A8R8G8B8); - if (rtt == NULL) - { - errorstream<<"TextureSource::generateTextureFromMesh(): " - <<"addRenderTargetTexture returned NULL."<setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) { - driver->removeTexture(rtt); - errorstream<<"TextureSource::generateTextureFromMesh(): " - <<"failed to set render target"<getSceneManager(); - assert(smgr_main); - scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); - assert(smgr); - - scene::IMeshSceneNode* meshnode = - smgr->addMeshSceneNode(params.mesh, NULL, - -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); - meshnode->setMaterialFlag(video::EMF_LIGHTING, true); - meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); - meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); - meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); - meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, - params.camera_position, params.camera_lookat); - // second parameter of setProjectionMatrix (isOrthogonal) is ignored - camera->setProjectionMatrix(params.camera_projection_matrix, false); - - smgr->setAmbientLight(params.ambient_light); - smgr->addLightSceneNode(0, - params.light_position, - params.light_color, - params.light_radius); - - // Render scene - driver->beginScene(true, true, video::SColor(0,0,0,0)); - smgr->drawAll(); - driver->endScene(); - - // Drop scene manager - smgr->drop(); - - // Unset render target - driver->setRenderTarget(0, false, true, 0); - - if (params.delete_texture_on_shutdown) - m_texture_trash.push_back(rtt); - - return rtt; -} - -video::IImage* TextureSource::generateImage(const std::string &name) -{ - /* - Get the base image - */ - - const char separator = '^'; - const char paren_open = '('; - const char paren_close = ')'; - - // Find last separator in the name - s32 last_separator_pos = -1; - u8 paren_bal = 0; - for (s32 i = name.size() - 1; i >= 0; i--) { - switch(name[i]) { - case separator: - if (paren_bal == 0) { - last_separator_pos = i; - i = -1; // break out of loop - } - break; - case paren_open: - if (paren_bal == 0) { - errorstream << "generateImage(): unbalanced parentheses" - << "(extranous '(') while generating texture \"" - << name << "\"" << std::endl; - return NULL; - } - paren_bal--; - break; - case paren_close: - paren_bal++; - break; - default: - break; - } - } - if (paren_bal > 0) { - errorstream << "generateImage(): unbalanced parentheses" - << "(missing matching '(') while generating texture \"" - << name << "\"" << std::endl; - return NULL; - } - - - video::IImage *baseimg = NULL; - - /* - If separator was found, make the base image - using a recursive call. - */ - if (last_separator_pos != -1) { - baseimg = generateImage(name.substr(0, last_separator_pos)); - } - - - video::IVideoDriver* driver = m_device->getVideoDriver(); - assert(driver); - - /* - Parse out the last part of the name of the image and act - according to it - */ - - std::string last_part_of_name = name.substr(last_separator_pos + 1); - - /* - If this name is enclosed in parentheses, generate it - and blit it onto the base image - */ - if (last_part_of_name[0] == paren_open - && last_part_of_name[last_part_of_name.size() - 1] == paren_close) { - std::string name2 = last_part_of_name.substr(1, - last_part_of_name.size() - 2); - video::IImage *tmp = generateImage(name2); - if (!tmp) { - errorstream << "generateImage(): " - "Failed to generate \"" << name2 << "\"" - << std::endl; - return NULL; - } - core::dimension2d dim = tmp->getDimension(); - if (!baseimg) - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim); - tmp->drop(); - } else if (!generateImagePart(last_part_of_name, baseimg)) { - // Generate image according to part of name - errorstream << "generateImage(): " - "Failed to generate \"" << last_part_of_name << "\"" - << std::endl; - } - - // If no resulting image, print a warning - if (baseimg == NULL) { - errorstream << "generateImage(): baseimg is NULL (attempted to" - " create texture \"" << name << "\")" << std::endl; - } - - return baseimg; -} - -#ifdef __ANDROID__ -#include -/** - * Check and align image to npot2 if required by hardware - * @param image image to check for npot2 alignment - * @param driver driver to use for image operations - * @return image or copy of image aligned to npot2 - */ -video::IImage * Align2Npot2(video::IImage * image, - video::IVideoDriver* driver) -{ - if (image == NULL) { - return image; - } - - core::dimension2d dim = image->getDimension(); - - std::string extensions = (char*) glGetString(GL_EXTENSIONS); - if (extensions.find("GL_OES_texture_npot") != std::string::npos) { - return image; - } - - unsigned int height = npot2(dim.Height); - unsigned int width = npot2(dim.Width); - - if ((dim.Height == height) && - (dim.Width == width)) { - return image; - } - - if (dim.Height > height) { - height *= 2; - } - - if (dim.Width > width) { - width *= 2; - } - - video::IImage *targetimage = - driver->createImage(video::ECF_A8R8G8B8, - core::dimension2d(width, height)); - - if (targetimage != NULL) { - image->copyToScaling(targetimage); - } - image->drop(); - return targetimage; -} - -#endif - -bool TextureSource::generateImagePart(std::string part_of_name, - video::IImage *& baseimg) -{ - video::IVideoDriver* driver = m_device->getVideoDriver(); - assert(driver); - - // Stuff starting with [ are special commands - if (part_of_name.size() == 0 || part_of_name[0] != '[') - { - video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); -#ifdef __ANDROID__ - image = Align2Npot2(image, driver); -#endif - if (image == NULL) { - if (part_of_name != "") { - if (part_of_name.find("_normal.png") == std::string::npos){ - errorstream<<"generateImage(): Could not load image \"" - < dim(2,2); - core::dimension2d dim(1,1); - image = driver->createImage(video::ECF_A8R8G8B8, dim); - assert(image); - /*image->setPixel(0,0, video::SColor(255,255,0,0)); - image->setPixel(1,0, video::SColor(255,0,255,0)); - image->setPixel(0,1, video::SColor(255,0,0,255)); - image->setPixel(1,1, video::SColor(255,255,0,255));*/ - image->setPixel(0,0, video::SColor(255,myrand()%256, - myrand()%256,myrand()%256)); - /*image->setPixel(1,0, video::SColor(255,myrand()%256, - myrand()%256,myrand()%256)); - image->setPixel(0,1, video::SColor(255,myrand()%256, - myrand()%256,myrand()%256)); - image->setPixel(1,1, video::SColor(255,myrand()%256, - myrand()%256,myrand()%256));*/ - } - - // If base image is NULL, load as base. - if (baseimg == NULL) - { - //infostream<<"Setting "< dim = image->getDimension(); - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - image->copyTo(baseimg); - } - // Else blit on base. - else - { - //infostream<<"Blitting "< dim = image->getDimension(); - //core::dimension2d dim(16,16); - // Position to copy the blitted to in the base image - core::position2d pos_to(0,0); - // Position to copy the blitted from in the blitted image - core::position2d pos_from(0,0); - // Blit - /*image->copyToWithAlpha(baseimg, pos_to, - core::rect(pos_from, dim), - video::SColor(255,255,255,255), - NULL);*/ - blit_with_alpha(image, baseimg, pos_from, pos_to, dim); - } - //cleanup - image->drop(); - } - else - { - // A special texture modification - - /*infostream<<"generateImage(): generating special " - <<"modification \""<= 0) - { - draw_crack(img_crack, baseimg, - use_overlay, frame_count, - progression, driver); - img_crack->drop(); - } - } - /* - [combine:WxH:X,Y=filename:X,Y=filename2 - Creates a bigger texture from an amount of smaller ones - */ - else if (part_of_name.substr(0,8) == "[combine") - { - Strfnd sf(part_of_name); - sf.next(":"); - u32 w0 = stoi(sf.next("x")); - u32 h0 = stoi(sf.next(":")); - //infostream<<"combined w="< dim = img->getDimension(); - infostream<<"Size "< pos_base(x, y); - video::IImage *img2 = - driver->createImage(video::ECF_A8R8G8B8, dim); - img->copyTo(img2); - img->drop(); - /*img2->copyToWithAlpha(baseimg, pos_base, - core::rect(v2s32(0,0), dim), - video::SColor(255,255,255,255), - NULL);*/ - blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim); - img2->drop(); - } else { - errorstream << "generateImagePart(): Failed to load image \"" - << filename << "\" for [combine" << std::endl; - } - } - } - /* - "[brighten" - */ - else if (part_of_name.substr(0,9) == "[brighten") - { - if (baseimg == NULL) { - errorstream<<"generateImagePart(): baseimg==NULL " - <<"for part_of_name=\""< dim = baseimg->getDimension(); - - // Set alpha to full - for (u32 y=0; ygetPixel(x,y); - c.setAlpha(255); - baseimg->setPixel(x,y,c); - } - } - /* - "[makealpha:R,G,B" - Convert one color to transparent. - */ - else if (part_of_name.substr(0,11) == "[makealpha:") - { - if (baseimg == NULL) { - errorstream<<"generateImagePart(): baseimg == NULL " - <<"for part_of_name=\""< dim = baseimg->getDimension(); - - /*video::IImage *oldbaseimg = baseimg; - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - oldbaseimg->copyTo(baseimg); - oldbaseimg->drop();*/ - - // Set alpha to full - for (u32 y=0; ygetPixel(x,y); - u32 r = c.getRed(); - u32 g = c.getGreen(); - u32 b = c.getBlue(); - if (!(r == r1 && g == g1 && b == b1)) - continue; - c.setAlpha(0); - 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<<"generateImagePart(): 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 &. - Create an "inventory cube". - NOTE: This should be used only on its own. - Example (a grass block (not actually used in game): - "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png" - */ - else if (part_of_name.substr(0,14) == "[inventorycube") - { - if (baseimg != NULL){ - errorstream<<"generateImagePart(): baseimg != NULL " - <<"for part_of_name=\""<getDimension().Height == npot2(img_top->getDimension().Height)); - assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width)); - - assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height)); - assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width)); - - assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height)); - assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width)); -#endif - - // Create textures from images - video::ITexture *texture_top = driver->addTexture( - (imagename_top + "__temp__").c_str(), img_top); - video::ITexture *texture_left = driver->addTexture( - (imagename_left + "__temp__").c_str(), img_left); - video::ITexture *texture_right = driver->addTexture( - (imagename_right + "__temp__").c_str(), img_right); - assert(texture_top && texture_left && texture_right); - - // Drop images - img_top->drop(); - img_left->drop(); - img_right->drop(); - - /* - Draw a cube mesh into a render target texture - */ - scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1)); - setMeshColor(cube, video::SColor(255, 255, 255, 255)); - cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top); - cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top); - cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right); - cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right); - cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left); - cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left); - - TextureFromMeshParams params; - params.mesh = cube; - params.dim.set(64, 64); - params.rtt_texture_name = part_of_name + "_RTT"; - // We will delete the rtt texture ourselves - params.delete_texture_on_shutdown = false; - params.camera_position.set(0, 1.0, -1.5); - params.camera_position.rotateXZBy(45); - params.camera_lookat.set(0, 0, 0); - // Set orthogonal projection - params.camera_projection_matrix.buildProjectionMatrixOrthoLH( - 1.65, 1.65, 0, 100); - - params.ambient_light.set(1.0, 0.2, 0.2, 0.2); - params.light_position.set(10, 100, -50); - params.light_color.set(1.0, 0.5, 0.5, 0.5); - params.light_radius = 1000; - - video::ITexture *rtt = generateTextureFromMesh(params); - - // Drop mesh - cube->drop(); - - // Free textures - driver->removeTexture(texture_top); - driver->removeTexture(texture_left); - driver->removeTexture(texture_right); - - if (rtt == NULL) { - baseimg = generateImage(imagename_top); - return true; - } - - // Create image of render target - video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim); - assert(image); - - // Cleanup texture - driver->removeTexture(rtt); - - baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim); - - if (image) { - image->copyTo(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 = m_sourcecache.getOrLoad(filename, m_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<<"generateImagePart(): 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<<"generateImagePart(): 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; - } - /* - [mask:filename - Applies a mask to an image - */ - else if (part_of_name.substr(0,6) == "[mask:") - { - if (baseimg == NULL) { - errorstream << "generateImage(): baseimg == NULL " - << "for part_of_name=\"" << part_of_name - << "\", cancelling." << std::endl; - return false; - } - Strfnd sf(part_of_name); - sf.next(":"); - std::string filename = sf.next(":"); - - video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); - if (img) { - apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0), - img->getDimension()); - } else { - errorstream << "generateImage(): Failed to load \"" - << filename << "\"."; - } - } - /* - [colorize:color - Overlays image with given color - color = color as ColorString - */ - else if (part_of_name.substr(0,10) == "[colorize:") { - Strfnd sf(part_of_name); - sf.next(":"); - std::string color_str = sf.next(":"); - std::string ratio_str = sf.next(":"); - - if (baseimg == NULL) { - errorstream << "generateImagePart(): baseimg != NULL " - << "for part_of_name=\"" << part_of_name - << "\", cancelling." << std::endl; - return false; - } - - video::SColor color; - int ratio = -1; - - if (!parseColorString(color_str, color, false)) - return false; - - if (is_number(ratio_str)) - ratio = mystoi(ratio_str, 0, 255); - - core::dimension2d dim = baseimg->getDimension(); - video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim); - - if (!img) { - errorstream << "generateImagePart(): Could not create image " - << "for part_of_name=\"" << part_of_name - << "\", cancelling." << std::endl; - return false; - } - - img->fill(video::SColor(color)); - // Overlay the colored image - blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio); - img->drop(); - } - else - { - errorstream << "generateImagePart(): Invalid " - " modification: \"" << part_of_name << "\"" << std::endl; - } - } - - return true; -} - -/* - 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); - } -} - -/* - Draw an image on top of an another one, using the alpha channel of the - source image; only modify fully opaque pixels in destinaion -*/ -static void blit_with_alpha_overlay(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); - if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) - { - dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); - dst->setPixel(dst_x, dst_y, dst_c); - } - } -} - -/* - Draw an image on top of an another one, using the specified ratio - modify all partially-opaque pixels in the destination. -*/ -static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst, - v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio) -{ - for (u32 y0 = 0; y0 < size.Y; y0++) - for (u32 x0 = 0; x0 < size.X; x0++) - { - s32 src_x = src_pos.X + x0; - s32 src_y = src_pos.Y + y0; - s32 dst_x = dst_pos.X + x0; - s32 dst_y = dst_pos.Y + y0; - video::SColor src_c = src->getPixel(src_x, src_y); - video::SColor dst_c = dst->getPixel(dst_x, dst_y); - if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0) - { - if (ratio == -1) - dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); - else - dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f); - dst->setPixel(dst_x, dst_y, dst_c); - } - } -} - -/* - Apply mask to destination -*/ -static void apply_mask(video::IImage *mask, video::IImage *dst, - v2s32 mask_pos, v2s32 dst_pos, v2u32 size) -{ - for (u32 y0 = 0; y0 < size.Y; y0++) { - for (u32 x0 = 0; x0 < size.X; x0++) { - s32 mask_x = x0 + mask_pos.X; - s32 mask_y = y0 + mask_pos.Y; - s32 dst_x = x0 + dst_pos.X; - s32 dst_y = y0 + dst_pos.Y; - video::SColor mask_c = mask->getPixel(mask_x, mask_y); - video::SColor dst_c = dst->getPixel(dst_x, dst_y); - dst_c.color &= mask_c.color; - dst->setPixel(dst_x, dst_y, dst_c); - } - } -} - -static void draw_crack(video::IImage *crack, video::IImage *dst, - bool use_overlay, s32 frame_count, s32 progression, - video::IVideoDriver *driver) -{ - // Dimension of destination image - core::dimension2d dim_dst = dst->getDimension(); - // Dimension of original image - core::dimension2d dim_crack = crack->getDimension(); - // Count of crack stages - s32 crack_count = dim_crack.Height / dim_crack.Width; - // Limit frame_count - if (frame_count > (s32) dim_dst.Height) - frame_count = dim_dst.Height; - if (frame_count < 1) - frame_count = 1; - // Limit progression - if (progression > crack_count-1) - progression = crack_count-1; - // Dimension of a single crack stage - core::dimension2d dim_crack_cropped( - dim_crack.Width, - dim_crack.Width - ); - // Dimension of the scaled crack stage, - // which is the same as the dimension of a single destination frame - core::dimension2d dim_crack_scaled( - dim_dst.Width, - dim_dst.Height / frame_count - ); - // Create cropped and scaled crack images - video::IImage *crack_cropped = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_cropped); - video::IImage *crack_scaled = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_scaled); - - if (crack_cropped && crack_scaled) - { - // Crop crack image - v2s32 pos_crack(0, progression*dim_crack.Width); - crack->copyTo(crack_cropped, - v2s32(0,0), - core::rect(pos_crack, dim_crack_cropped)); - // Scale crack image by copying - crack_cropped->copyToScaling(crack_scaled); - // Copy or overlay crack image onto each frame - for (s32 i = 0; i < frame_count; ++i) - { - v2s32 dst_pos(0, dim_crack_scaled.Height * i); - if (use_overlay) - { - blit_with_alpha_overlay(crack_scaled, dst, - v2s32(0,0), dst_pos, - dim_crack_scaled); - } - else - { - blit_with_alpha(crack_scaled, dst, - v2s32(0,0), dst_pos, - dim_crack_scaled); - } - } - } - - if (crack_scaled) - crack_scaled->drop(); - - if (crack_cropped) - crack_cropped->drop(); -} - -void brighten(video::IImage *image) -{ - if (image == NULL) - return; - - core::dimension2d dim = image->getDimension(); - - for (u32 y=0; ygetPixel(x,y); - c.setRed(0.5 * 255 + 0.5 * (float)c.getRed()); - c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen()); - c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue()); - image->setPixel(x,y,c); - } -} - -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 <= 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); - } -} - -video::ITexture* TextureSource::getNormalTexture(const std::string &name) -{ - u32 id; - if (isKnownSourceImage("override_normal.png")) - return getTexture("override_normal.png", &id); - std::string fname_base = name; - std::string normal_ext = "_normal.png"; - size_t pos = fname_base.find("."); - std::string fname_normal = fname_base.substr(0, pos) + normal_ext; - if (isKnownSourceImage(fname_normal)) { - // look for image extension and replace it - size_t i = 0; - while ((i = fname_base.find(".", i)) != std::string::npos) { - fname_base.replace(i, 4, normal_ext); - i += normal_ext.length(); - } - return getTexture(fname_base, &id); - } - return NULL; -} diff --git a/src/tile.h b/src/tile.h deleted file mode 100644 index ea7a9135a..000000000 --- a/src/tile.h +++ /dev/null @@ -1,280 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -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 Lesser General Public License for more details. - -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. -*/ - -#ifndef TILE_HEADER -#define TILE_HEADER - -#include "irrlichttypes.h" -#include "irr_v2d.h" -#include "irr_v3d.h" -#include -#include -#include "threads.h" -#include -#include - -class IGameDef; - -/* - tile.{h,cpp}: Texture handling stuff. -*/ - -/* - Find out the full path of an image by trying different filename - extensions. - - If failed, return "". - - TODO: Should probably be moved out from here, because things needing - this function do not need anything else from this header -*/ -std::string getImagePath(std::string path); - -/* - Gets the path to a texture by first checking if the texture exists - in texture_path and if not, using the data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getTexturePath(const std::string &filename); - -void clearTextureNameCache(); - -/* - ITextureSource::generateTextureFromMesh parameters -*/ -namespace irr {namespace scene {class IMesh;}} -struct TextureFromMeshParams -{ - scene::IMesh *mesh; - core::dimension2d dim; - std::string rtt_texture_name; - bool delete_texture_on_shutdown; - v3f camera_position; - v3f camera_lookat; - core::CMatrix4 camera_projection_matrix; - video::SColorf ambient_light; - v3f light_position; - video::SColorf light_color; - f32 light_radius; -}; - -/* - TextureSource creates and caches textures. -*/ - -class ISimpleTextureSource -{ -public: - ISimpleTextureSource(){} - virtual ~ISimpleTextureSource(){} - virtual video::ITexture* getTexture( - const std::string &name, u32 *id = NULL) = 0; -}; - -class ITextureSource : public ISimpleTextureSource -{ -public: - ITextureSource(){} - virtual ~ITextureSource(){} - virtual u32 getTextureId(const std::string &name)=0; - virtual std::string getTextureName(u32 id)=0; - virtual video::ITexture* getTexture(u32 id)=0; - virtual video::ITexture* getTexture( - const std::string &name, u32 *id = NULL)=0; - virtual IrrlichtDevice* getDevice()=0; - virtual bool isKnownSourceImage(const std::string &name)=0; - virtual video::ITexture* generateTextureFromMesh( - const TextureFromMeshParams ¶ms)=0; - virtual video::ITexture* getNormalTexture(const std::string &name)=0; -}; - -class IWritableTextureSource : public ITextureSource -{ -public: - IWritableTextureSource(){} - virtual ~IWritableTextureSource(){} - virtual u32 getTextureId(const std::string &name)=0; - virtual std::string getTextureName(u32 id)=0; - virtual video::ITexture* getTexture(u32 id)=0; - virtual video::ITexture* getTexture( - const std::string &name, u32 *id = NULL)=0; - virtual IrrlichtDevice* getDevice()=0; - virtual bool isKnownSourceImage(const std::string &name)=0; - virtual video::ITexture* generateTextureFromMesh( - const TextureFromMeshParams ¶ms)=0; - - virtual void processQueue()=0; - virtual void insertSourceImage(const std::string &name, video::IImage *img)=0; - virtual void rebuildImagesAndTextures()=0; - virtual video::ITexture* getNormalTexture(const std::string &name)=0; -}; - -IWritableTextureSource* createTextureSource(IrrlichtDevice *device); - -#ifdef __ANDROID__ -/** - * @param size get next npot2 value - * @return npot2 value - */ -inline unsigned int npot2(unsigned int size) -{ - if (size == 0) return 0; - unsigned int npot = 1; - - while ((size >>= 1) > 0) { - npot <<= 1; - } - return npot; -} - -video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver); -#endif - -enum MaterialType{ - TILE_MATERIAL_BASIC, - TILE_MATERIAL_ALPHA, - TILE_MATERIAL_LIQUID_TRANSPARENT, - TILE_MATERIAL_LIQUID_OPAQUE, - TILE_MATERIAL_WAVING_LEAVES, - TILE_MATERIAL_WAVING_PLANTS -}; - -// Material flags -// Should backface culling be enabled? -#define MATERIAL_FLAG_BACKFACE_CULLING 0x01 -// Should a crack be drawn? -#define MATERIAL_FLAG_CRACK 0x02 -// Should the crack be drawn on transparent pixels (unset) or not (set)? -// Ignored if MATERIAL_FLAG_CRACK is not set. -#define MATERIAL_FLAG_CRACK_OVERLAY 0x04 -// Animation made up by splitting the texture to vertical frames, as -// defined by extra parameters -#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08 -#define MATERIAL_FLAG_HIGHLIGHTED 0x10 - -/* - This fully defines the looks of a tile. - The SMaterial of a tile is constructed according to this. -*/ -struct FrameSpec -{ - FrameSpec(): - texture_id(0), - texture(NULL), - normal_texture(NULL) - { - } - u32 texture_id; - video::ITexture *texture; - video::ITexture *normal_texture; -}; - -struct TileSpec -{ - TileSpec(): - texture_id(0), - texture(NULL), - normal_texture(NULL), - alpha(255), - material_type(TILE_MATERIAL_BASIC), - material_flags( - //0 // <- DEBUG, Use the one below - MATERIAL_FLAG_BACKFACE_CULLING - ), - shader_id(0), - animation_frame_count(1), - animation_frame_length_ms(0), - rotation(0) - { - } - - bool operator==(const TileSpec &other) const - { - return ( - texture_id == other.texture_id && - /* texture == other.texture && */ - alpha == other.alpha && - material_type == other.material_type && - material_flags == other.material_flags && - rotation == other.rotation - ); - } - - bool operator!=(const TileSpec &other) const - { - return !(*this == other); - } - - // Sets everything else except the texture in the material - void applyMaterialOptions(video::SMaterial &material) const - { - switch (material_type) { - case TILE_MATERIAL_BASIC: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - case TILE_MATERIAL_ALPHA: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - break; - case TILE_MATERIAL_LIQUID_TRANSPARENT: - material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; - break; - case TILE_MATERIAL_LIQUID_OPAQUE: - material.MaterialType = video::EMT_SOLID; - break; - case TILE_MATERIAL_WAVING_LEAVES: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - case TILE_MATERIAL_WAVING_PLANTS: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - } - material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) - ? true : false; - } - - void applyMaterialOptionsWithShaders(video::SMaterial &material) const - { - material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) - ? true : false; - } - - u32 texture_id; - video::ITexture *texture; - video::ITexture *normal_texture; - - // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used) - u8 alpha; - // Material parameters - u8 material_type; - u8 material_flags; - u32 shader_id; - // Animation parameters - u8 animation_frame_count; - u16 animation_frame_length_ms; - std::vector frames; - - u8 rotation; -}; - -#endif diff --git a/src/touchscreengui.h b/src/touchscreengui.h index 4fe731513..a00df5c47 100644 --- a/src/touchscreengui.h +++ b/src/touchscreengui.h @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "game.h" -#include "tile.h" +#include "client/tile.h" using namespace irr; using namespace irr::core; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 7c16bddcb..a2be55544 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "mesh.h" #include "mapblock_mesh.h" -#include "tile.h" +#include "client/tile.h" #include "log.h" #include "util/numeric.h" #include -- cgit v1.2.3