tile.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "tile.h"
  17. #include <algorithm>
  18. #include <ICameraSceneNode.h>
  19. #include <IrrCompileConfig.h>
  20. #include "util/string.h"
  21. #include "util/container.h"
  22. #include "util/thread.h"
  23. #include "filesys.h"
  24. #include "settings.h"
  25. #include "mesh.h"
  26. #include "gamedef.h"
  27. #include "util/strfnd.h"
  28. #include "imagefilters.h"
  29. #include "guiscalingfilter.h"
  30. #include "renderingengine.h"
  31. #if ENABLE_GLES
  32. #ifdef _IRR_COMPILE_WITH_OGLES1_
  33. #include <GLES/gl.h>
  34. #else
  35. #include <GLES2/gl2.h>
  36. #endif
  37. #endif
  38. /*
  39. A cache from texture name to texture path
  40. */
  41. MutexedMap<std::string, std::string> g_texturename_to_path_cache;
  42. /*
  43. Replaces the filename extension.
  44. eg:
  45. std::string image = "a/image.png"
  46. replace_ext(image, "jpg")
  47. -> image = "a/image.jpg"
  48. Returns true on success.
  49. */
  50. static bool replace_ext(std::string &path, const char *ext)
  51. {
  52. if (ext == NULL)
  53. return false;
  54. // Find place of last dot, fail if \ or / found.
  55. s32 last_dot_i = -1;
  56. for (s32 i=path.size()-1; i>=0; i--)
  57. {
  58. if (path[i] == '.')
  59. {
  60. last_dot_i = i;
  61. break;
  62. }
  63. if (path[i] == '\\' || path[i] == '/')
  64. break;
  65. }
  66. // If not found, return an empty string
  67. if (last_dot_i == -1)
  68. return false;
  69. // Else make the new path
  70. path = path.substr(0, last_dot_i+1) + ext;
  71. return true;
  72. }
  73. /*
  74. Find out the full path of an image by trying different filename
  75. extensions.
  76. If failed, return "".
  77. */
  78. std::string getImagePath(std::string path)
  79. {
  80. // A NULL-ended list of possible image extensions
  81. const char *extensions[] = {
  82. "png", "jpg", "bmp", "tga",
  83. "pcx", "ppm", "psd", "wal", "rgb",
  84. NULL
  85. };
  86. // If there is no extension, add one
  87. if (removeStringEnd(path, extensions).empty())
  88. path = path + ".png";
  89. // Check paths until something is found to exist
  90. const char **ext = extensions;
  91. do{
  92. bool r = replace_ext(path, *ext);
  93. if (!r)
  94. return "";
  95. if (fs::PathExists(path))
  96. return path;
  97. }
  98. while((++ext) != NULL);
  99. return "";
  100. }
  101. /*
  102. Gets the path to a texture by first checking if the texture exists
  103. in texture_path and if not, using the data path.
  104. Checks all supported extensions by replacing the original extension.
  105. If not found, returns "".
  106. Utilizes a thread-safe cache.
  107. */
  108. std::string getTexturePath(const std::string &filename, bool *is_base_pack)
  109. {
  110. std::string fullpath;
  111. // This can set a wrong value on cached textures, but is irrelevant because
  112. // is_base_pack is only passed when initializing the textures the first time
  113. if (is_base_pack)
  114. *is_base_pack = false;
  115. /*
  116. Check from cache
  117. */
  118. bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
  119. if (incache)
  120. return fullpath;
  121. /*
  122. Check from texture_path
  123. */
  124. for (const auto &path : getTextureDirs()) {
  125. std::string testpath = path + DIR_DELIM;
  126. testpath.append(filename);
  127. // Check all filename extensions. Returns "" if not found.
  128. fullpath = getImagePath(testpath);
  129. if (!fullpath.empty())
  130. break;
  131. }
  132. /*
  133. Check from default data directory
  134. */
  135. if (fullpath.empty())
  136. {
  137. std::string base_path = porting::path_share + DIR_DELIM + "textures"
  138. + DIR_DELIM + "base" + DIR_DELIM + "pack";
  139. std::string testpath = base_path + DIR_DELIM + filename;
  140. // Check all filename extensions. Returns "" if not found.
  141. fullpath = getImagePath(testpath);
  142. if (is_base_pack && !fullpath.empty())
  143. *is_base_pack = true;
  144. }
  145. // Add to cache (also an empty result is cached)
  146. g_texturename_to_path_cache.set(filename, fullpath);
  147. // Finally return it
  148. return fullpath;
  149. }
  150. void clearTextureNameCache()
  151. {
  152. g_texturename_to_path_cache.clear();
  153. }
  154. /*
  155. Stores internal information about a texture.
  156. */
  157. struct TextureInfo
  158. {
  159. std::string name;
  160. video::ITexture *texture;
  161. TextureInfo(
  162. const std::string &name_,
  163. video::ITexture *texture_=NULL
  164. ):
  165. name(name_),
  166. texture(texture_)
  167. {
  168. }
  169. };
  170. /*
  171. SourceImageCache: A cache used for storing source images.
  172. */
  173. class SourceImageCache
  174. {
  175. public:
  176. ~SourceImageCache() {
  177. for (auto &m_image : m_images) {
  178. m_image.second->drop();
  179. }
  180. m_images.clear();
  181. }
  182. void insert(const std::string &name, video::IImage *img, bool prefer_local)
  183. {
  184. assert(img); // Pre-condition
  185. // Remove old image
  186. std::map<std::string, video::IImage*>::iterator n;
  187. n = m_images.find(name);
  188. if (n != m_images.end()){
  189. if (n->second)
  190. n->second->drop();
  191. }
  192. video::IImage* toadd = img;
  193. bool need_to_grab = true;
  194. // Try to use local texture instead if asked to
  195. if (prefer_local) {
  196. bool is_base_pack;
  197. std::string path = getTexturePath(name, &is_base_pack);
  198. // Ignore base pack
  199. if (!path.empty() && !is_base_pack) {
  200. video::IImage *img2 = RenderingEngine::get_video_driver()->
  201. createImageFromFile(path.c_str());
  202. if (img2){
  203. toadd = img2;
  204. need_to_grab = false;
  205. }
  206. }
  207. }
  208. if (need_to_grab)
  209. toadd->grab();
  210. m_images[name] = toadd;
  211. }
  212. video::IImage* get(const std::string &name)
  213. {
  214. std::map<std::string, video::IImage*>::iterator n;
  215. n = m_images.find(name);
  216. if (n != m_images.end())
  217. return n->second;
  218. return NULL;
  219. }
  220. // Primarily fetches from cache, secondarily tries to read from filesystem
  221. video::IImage *getOrLoad(const std::string &name)
  222. {
  223. std::map<std::string, video::IImage*>::iterator n;
  224. n = m_images.find(name);
  225. if (n != m_images.end()){
  226. n->second->grab(); // Grab for caller
  227. return n->second;
  228. }
  229. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  230. std::string path = getTexturePath(name);
  231. if (path.empty()) {
  232. infostream<<"SourceImageCache::getOrLoad(): No path found for \""
  233. <<name<<"\""<<std::endl;
  234. return NULL;
  235. }
  236. infostream<<"SourceImageCache::getOrLoad(): Loading path \""<<path
  237. <<"\""<<std::endl;
  238. video::IImage *img = driver->createImageFromFile(path.c_str());
  239. if (img){
  240. m_images[name] = img;
  241. img->grab(); // Grab for caller
  242. }
  243. return img;
  244. }
  245. private:
  246. std::map<std::string, video::IImage*> m_images;
  247. };
  248. /*
  249. TextureSource
  250. */
  251. class TextureSource : public IWritableTextureSource
  252. {
  253. public:
  254. TextureSource();
  255. virtual ~TextureSource();
  256. /*
  257. Example case:
  258. Now, assume a texture with the id 1 exists, and has the name
  259. "stone.png^mineral1".
  260. Then a random thread calls getTextureId for a texture called
  261. "stone.png^mineral1^crack0".
  262. ...Now, WTF should happen? Well:
  263. - getTextureId strips off stuff recursively from the end until
  264. the remaining part is found, or nothing is left when
  265. something is stripped out
  266. But it is slow to search for textures by names and modify them
  267. like that?
  268. - ContentFeatures is made to contain ids for the basic plain
  269. textures
  270. - Crack textures can be slow by themselves, but the framework
  271. must be fast.
  272. Example case #2:
  273. - Assume a texture with the id 1 exists, and has the name
  274. "stone.png^mineral_coal.png".
  275. - Now getNodeTile() stumbles upon a node which uses
  276. texture id 1, and determines that MATERIAL_FLAG_CRACK
  277. must be applied to the tile
  278. - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
  279. has received the current crack level 0 from the client. It
  280. finds out the name of the texture with getTextureName(1),
  281. appends "^crack0" to it and gets a new texture id with
  282. getTextureId("stone.png^mineral_coal.png^crack0").
  283. */
  284. /*
  285. Gets a texture id from cache or
  286. - if main thread, generates the texture, adds to cache and returns id.
  287. - if other thread, adds to request queue and waits for main thread.
  288. The id 0 points to a NULL texture. It is returned in case of error.
  289. */
  290. u32 getTextureId(const std::string &name);
  291. // Finds out the name of a cached texture.
  292. std::string getTextureName(u32 id);
  293. /*
  294. If texture specified by the name pointed by the id doesn't
  295. exist, create it, then return the cached texture.
  296. Can be called from any thread. If called from some other thread
  297. and not found in cache, the call is queued to the main thread
  298. for processing.
  299. */
  300. video::ITexture* getTexture(u32 id);
  301. video::ITexture* getTexture(const std::string &name, u32 *id = NULL);
  302. /*
  303. Get a texture specifically intended for mesh
  304. application, i.e. not HUD, compositing, or other 2D
  305. use. This texture may be a different size and may
  306. have had additional filters applied.
  307. */
  308. video::ITexture* getTextureForMesh(const std::string &name, u32 *id);
  309. virtual Palette* getPalette(const std::string &name);
  310. bool isKnownSourceImage(const std::string &name)
  311. {
  312. bool is_known = false;
  313. bool cache_found = m_source_image_existence.get(name, &is_known);
  314. if (cache_found)
  315. return is_known;
  316. // Not found in cache; find out if a local file exists
  317. is_known = (!getTexturePath(name).empty());
  318. m_source_image_existence.set(name, is_known);
  319. return is_known;
  320. }
  321. // Processes queued texture requests from other threads.
  322. // Shall be called from the main thread.
  323. void processQueue();
  324. // Insert an image into the cache without touching the filesystem.
  325. // Shall be called from the main thread.
  326. void insertSourceImage(const std::string &name, video::IImage *img);
  327. // Rebuild images and textures from the current set of source images
  328. // Shall be called from the main thread.
  329. void rebuildImagesAndTextures();
  330. video::ITexture* getNormalTexture(const std::string &name);
  331. video::SColor getTextureAverageColor(const std::string &name);
  332. video::ITexture *getShaderFlagsTexture(bool normamap_present);
  333. private:
  334. // The id of the thread that is allowed to use irrlicht directly
  335. std::thread::id m_main_thread;
  336. // Cache of source images
  337. // This should be only accessed from the main thread
  338. SourceImageCache m_sourcecache;
  339. // Generate a texture
  340. u32 generateTexture(const std::string &name);
  341. // Generate image based on a string like "stone.png" or "[crack:1:0".
  342. // if baseimg is NULL, it is created. Otherwise stuff is made on it.
  343. bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
  344. /*! Generates an image from a full string like
  345. * "stone.png^mineral_coal.png^[crack:1:0".
  346. * Shall be called from the main thread.
  347. * The returned Image should be dropped.
  348. */
  349. video::IImage* generateImage(const std::string &name);
  350. // Thread-safe cache of what source images are known (true = known)
  351. MutexedMap<std::string, bool> m_source_image_existence;
  352. // A texture id is index in this array.
  353. // The first position contains a NULL texture.
  354. std::vector<TextureInfo> m_textureinfo_cache;
  355. // Maps a texture name to an index in the former.
  356. std::map<std::string, u32> m_name_to_id;
  357. // The two former containers are behind this mutex
  358. std::mutex m_textureinfo_cache_mutex;
  359. // Queued texture fetches (to be processed by the main thread)
  360. RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
  361. // Textures that have been overwritten with other ones
  362. // but can't be deleted because the ITexture* might still be used
  363. std::vector<video::ITexture*> m_texture_trash;
  364. // Maps image file names to loaded palettes.
  365. std::unordered_map<std::string, Palette> m_palettes;
  366. // Cached settings needed for making textures from meshes
  367. bool m_setting_trilinear_filter;
  368. bool m_setting_bilinear_filter;
  369. bool m_setting_anisotropic_filter;
  370. };
  371. IWritableTextureSource *createTextureSource()
  372. {
  373. return new TextureSource();
  374. }
  375. TextureSource::TextureSource()
  376. {
  377. m_main_thread = std::this_thread::get_id();
  378. // Add a NULL TextureInfo as the first index, named ""
  379. m_textureinfo_cache.emplace_back("");
  380. m_name_to_id[""] = 0;
  381. // Cache some settings
  382. // Note: Since this is only done once, the game must be restarted
  383. // for these settings to take effect
  384. m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
  385. m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
  386. m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
  387. }
  388. TextureSource::~TextureSource()
  389. {
  390. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  391. unsigned int textures_before = driver->getTextureCount();
  392. for (const auto &iter : m_textureinfo_cache) {
  393. //cleanup texture
  394. if (iter.texture)
  395. driver->removeTexture(iter.texture);
  396. }
  397. m_textureinfo_cache.clear();
  398. for (auto t : m_texture_trash) {
  399. //cleanup trashed texture
  400. driver->removeTexture(t);
  401. }
  402. infostream << "~TextureSource() before cleanup: "<< textures_before
  403. << " after: " << driver->getTextureCount() << std::endl;
  404. }
  405. u32 TextureSource::getTextureId(const std::string &name)
  406. {
  407. //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
  408. {
  409. /*
  410. See if texture already exists
  411. */
  412. MutexAutoLock lock(m_textureinfo_cache_mutex);
  413. std::map<std::string, u32>::iterator n;
  414. n = m_name_to_id.find(name);
  415. if (n != m_name_to_id.end())
  416. {
  417. return n->second;
  418. }
  419. }
  420. /*
  421. Get texture
  422. */
  423. if (std::this_thread::get_id() == m_main_thread) {
  424. return generateTexture(name);
  425. }
  426. infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
  427. // We're gonna ask the result to be put into here
  428. static ResultQueue<std::string, u32, u8, u8> result_queue;
  429. // Throw a request in
  430. m_get_texture_queue.add(name, 0, 0, &result_queue);
  431. try {
  432. while(true) {
  433. // Wait result for a second
  434. GetResult<std::string, u32, u8, u8>
  435. result = result_queue.pop_front(1000);
  436. if (result.key == name) {
  437. return result.item;
  438. }
  439. }
  440. } catch(ItemNotFoundException &e) {
  441. errorstream << "Waiting for texture " << name << " timed out." << std::endl;
  442. return 0;
  443. }
  444. infostream << "getTextureId(): Failed" << std::endl;
  445. return 0;
  446. }
  447. // Draw an image on top of an another one, using the alpha channel of the
  448. // source image
  449. static void blit_with_alpha(video::IImage *src, video::IImage *dst,
  450. v2s32 src_pos, v2s32 dst_pos, v2u32 size);
  451. // Like blit_with_alpha, but only modifies destination pixels that
  452. // are fully opaque
  453. static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
  454. v2s32 src_pos, v2s32 dst_pos, v2u32 size);
  455. // Apply a color to an image. Uses an int (0-255) to calculate the ratio.
  456. // If the ratio is 255 or -1 and keep_alpha is true, then it multiples the
  457. // color alpha with the destination alpha.
  458. // Otherwise, any pixels that are not fully transparent get the color alpha.
  459. static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
  460. const video::SColor &color, int ratio, bool keep_alpha);
  461. // paint a texture using the given color
  462. static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size,
  463. const video::SColor &color);
  464. // Apply a mask to an image
  465. static void apply_mask(video::IImage *mask, video::IImage *dst,
  466. v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
  467. // Draw or overlay a crack
  468. static void draw_crack(video::IImage *crack, video::IImage *dst,
  469. bool use_overlay, s32 frame_count, s32 progression,
  470. video::IVideoDriver *driver, u8 tiles = 1);
  471. // Brighten image
  472. void brighten(video::IImage *image);
  473. // Parse a transform name
  474. u32 parseImageTransform(const std::string& s);
  475. // Apply transform to image dimension
  476. core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
  477. // Apply transform to image data
  478. void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
  479. /*
  480. This method generates all the textures
  481. */
  482. u32 TextureSource::generateTexture(const std::string &name)
  483. {
  484. //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl;
  485. // Empty name means texture 0
  486. if (name.empty()) {
  487. infostream<<"generateTexture(): name is empty"<<std::endl;
  488. return 0;
  489. }
  490. {
  491. /*
  492. See if texture already exists
  493. */
  494. MutexAutoLock lock(m_textureinfo_cache_mutex);
  495. std::map<std::string, u32>::iterator n;
  496. n = m_name_to_id.find(name);
  497. if (n != m_name_to_id.end()) {
  498. return n->second;
  499. }
  500. }
  501. /*
  502. Calling only allowed from main thread
  503. */
  504. if (std::this_thread::get_id() != m_main_thread) {
  505. errorstream<<"TextureSource::generateTexture() "
  506. "called not from main thread"<<std::endl;
  507. return 0;
  508. }
  509. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  510. sanity_check(driver);
  511. video::IImage *img = generateImage(name);
  512. video::ITexture *tex = NULL;
  513. if (img != NULL) {
  514. #if ENABLE_GLES
  515. img = Align2Npot2(img, driver);
  516. #endif
  517. // Create texture from resulting image
  518. tex = driver->addTexture(name.c_str(), img);
  519. guiScalingCache(io::path(name.c_str()), driver, img);
  520. img->drop();
  521. }
  522. /*
  523. Add texture to caches (add NULL textures too)
  524. */
  525. MutexAutoLock lock(m_textureinfo_cache_mutex);
  526. u32 id = m_textureinfo_cache.size();
  527. TextureInfo ti(name, tex);
  528. m_textureinfo_cache.push_back(ti);
  529. m_name_to_id[name] = id;
  530. return id;
  531. }
  532. std::string TextureSource::getTextureName(u32 id)
  533. {
  534. MutexAutoLock lock(m_textureinfo_cache_mutex);
  535. if (id >= m_textureinfo_cache.size())
  536. {
  537. errorstream<<"TextureSource::getTextureName(): id="<<id
  538. <<" >= m_textureinfo_cache.size()="
  539. <<m_textureinfo_cache.size()<<std::endl;
  540. return "";
  541. }
  542. return m_textureinfo_cache[id].name;
  543. }
  544. video::ITexture* TextureSource::getTexture(u32 id)
  545. {
  546. MutexAutoLock lock(m_textureinfo_cache_mutex);
  547. if (id >= m_textureinfo_cache.size())
  548. return NULL;
  549. return m_textureinfo_cache[id].texture;
  550. }
  551. video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
  552. {
  553. u32 actual_id = getTextureId(name);
  554. if (id){
  555. *id = actual_id;
  556. }
  557. return getTexture(actual_id);
  558. }
  559. video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *id)
  560. {
  561. static thread_local bool filter_needed =
  562. g_settings->getBool("texture_clean_transparent") ||
  563. ((m_setting_trilinear_filter || m_setting_bilinear_filter) &&
  564. g_settings->getS32("texture_min_size") > 1);
  565. // Avoid duplicating texture if it won't actually change
  566. if (filter_needed)
  567. return getTexture(name + "^[applyfiltersformesh", id);
  568. return getTexture(name, id);
  569. }
  570. Palette* TextureSource::getPalette(const std::string &name)
  571. {
  572. // Only the main thread may load images
  573. sanity_check(std::this_thread::get_id() == m_main_thread);
  574. if (name.empty())
  575. return NULL;
  576. auto it = m_palettes.find(name);
  577. if (it == m_palettes.end()) {
  578. // Create palette
  579. video::IImage *img = generateImage(name);
  580. if (!img) {
  581. warningstream << "TextureSource::getPalette(): palette \"" << name
  582. << "\" could not be loaded." << std::endl;
  583. return NULL;
  584. }
  585. Palette new_palette;
  586. u32 w = img->getDimension().Width;
  587. u32 h = img->getDimension().Height;
  588. // Real area of the image
  589. u32 area = h * w;
  590. if (area == 0)
  591. return NULL;
  592. if (area > 256) {
  593. warningstream << "TextureSource::getPalette(): the specified"
  594. << " palette image \"" << name << "\" is larger than 256"
  595. << " pixels, using the first 256." << std::endl;
  596. area = 256;
  597. } else if (256 % area != 0)
  598. warningstream << "TextureSource::getPalette(): the "
  599. << "specified palette image \"" << name << "\" does not "
  600. << "contain power of two pixels." << std::endl;
  601. // We stretch the palette so it will fit 256 values
  602. // This many param2 values will have the same color
  603. u32 step = 256 / area;
  604. // For each pixel in the image
  605. for (u32 i = 0; i < area; i++) {
  606. video::SColor c = img->getPixel(i % w, i / w);
  607. // Fill in palette with 'step' colors
  608. for (u32 j = 0; j < step; j++)
  609. new_palette.push_back(c);
  610. }
  611. img->drop();
  612. // Fill in remaining elements
  613. while (new_palette.size() < 256)
  614. new_palette.emplace_back(0xFFFFFFFF);
  615. m_palettes[name] = new_palette;
  616. it = m_palettes.find(name);
  617. }
  618. if (it != m_palettes.end())
  619. return &((*it).second);
  620. return NULL;
  621. }
  622. void TextureSource::processQueue()
  623. {
  624. /*
  625. Fetch textures
  626. */
  627. //NOTE this is only thread safe for ONE consumer thread!
  628. if (!m_get_texture_queue.empty())
  629. {
  630. GetRequest<std::string, u32, u8, u8>
  631. request = m_get_texture_queue.pop();
  632. /*infostream<<"TextureSource::processQueue(): "
  633. <<"got texture request with "
  634. <<"name=\""<<request.key<<"\""
  635. <<std::endl;*/
  636. m_get_texture_queue.pushResult(request, generateTexture(request.key));
  637. }
  638. }
  639. void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
  640. {
  641. //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
  642. sanity_check(std::this_thread::get_id() == m_main_thread);
  643. m_sourcecache.insert(name, img, true);
  644. m_source_image_existence.set(name, true);
  645. }
  646. void TextureSource::rebuildImagesAndTextures()
  647. {
  648. MutexAutoLock lock(m_textureinfo_cache_mutex);
  649. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  650. sanity_check(driver);
  651. infostream << "TextureSource: recreating " << m_textureinfo_cache.size()
  652. << " textures" << std::endl;
  653. // Recreate textures
  654. for (TextureInfo &ti : m_textureinfo_cache) {
  655. video::IImage *img = generateImage(ti.name);
  656. #if ENABLE_GLES
  657. img = Align2Npot2(img, driver);
  658. #endif
  659. // Create texture from resulting image
  660. video::ITexture *t = NULL;
  661. if (img) {
  662. t = driver->addTexture(ti.name.c_str(), img);
  663. guiScalingCache(io::path(ti.name.c_str()), driver, img);
  664. img->drop();
  665. }
  666. video::ITexture *t_old = ti.texture;
  667. // Replace texture
  668. ti.texture = t;
  669. if (t_old)
  670. m_texture_trash.push_back(t_old);
  671. }
  672. }
  673. inline static void applyShadeFactor(video::SColor &color, u32 factor)
  674. {
  675. u32 f = core::clamp<u32>(factor, 0, 256);
  676. color.setRed(color.getRed() * f / 256);
  677. color.setGreen(color.getGreen() * f / 256);
  678. color.setBlue(color.getBlue() * f / 256);
  679. }
  680. static video::IImage *createInventoryCubeImage(
  681. video::IImage *top, video::IImage *left, video::IImage *right)
  682. {
  683. core::dimension2du size_top = top->getDimension();
  684. core::dimension2du size_left = left->getDimension();
  685. core::dimension2du size_right = right->getDimension();
  686. u32 size = npot2(std::max({
  687. size_top.Width, size_top.Height,
  688. size_left.Width, size_left.Height,
  689. size_right.Width, size_right.Height,
  690. }));
  691. // It must be divisible by 4, to let everything work correctly.
  692. // But it is a power of 2, so being at least 4 is the same.
  693. // And the resulting texture should't be too large as well.
  694. size = core::clamp<u32>(size, 4, 64);
  695. // With such parameters, the cube fits exactly, touching each image line
  696. // from `0` to `cube_size - 1`. (Note that division is exact here).
  697. u32 cube_size = 9 * size;
  698. u32 offset = size / 2;
  699. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  700. auto lock_image = [size, driver] (video::IImage *&image) -> const u32 * {
  701. image->grab();
  702. core::dimension2du dim = image->getDimension();
  703. video::ECOLOR_FORMAT format = image->getColorFormat();
  704. if (dim.Width != size || dim.Height != size || format != video::ECF_A8R8G8B8) {
  705. video::IImage *scaled = driver->createImage(video::ECF_A8R8G8B8, {size, size});
  706. image->copyToScaling(scaled);
  707. image->drop();
  708. image = scaled;
  709. }
  710. sanity_check(image->getPitch() == 4 * size);
  711. return reinterpret_cast<u32 *>(image->lock());
  712. };
  713. auto free_image = [] (video::IImage *image) -> void {
  714. image->unlock();
  715. image->drop();
  716. };
  717. video::IImage *result = driver->createImage(video::ECF_A8R8G8B8, {cube_size, cube_size});
  718. sanity_check(result->getPitch() == 4 * cube_size);
  719. result->fill(video::SColor(0x00000000u));
  720. u32 *target = reinterpret_cast<u32 *>(result->lock());
  721. // Draws single cube face
  722. // `shade_factor` is face brightness, in range [0.0, 1.0]
  723. // (xu, xv, x1; yu, yv, y1) form coordinate transformation matrix
  724. // `offsets` list pixels to be drawn for single source pixel
  725. auto draw_image = [=] (video::IImage *image, float shade_factor,
  726. s16 xu, s16 xv, s16 x1,
  727. s16 yu, s16 yv, s16 y1,
  728. std::initializer_list<v2s16> offsets) -> void {
  729. u32 brightness = core::clamp<u32>(256 * shade_factor, 0, 256);
  730. const u32 *source = lock_image(image);
  731. for (u16 v = 0; v < size; v++) {
  732. for (u16 u = 0; u < size; u++) {
  733. video::SColor pixel(*source);
  734. applyShadeFactor(pixel, brightness);
  735. s16 x = xu * u + xv * v + x1;
  736. s16 y = yu * u + yv * v + y1;
  737. for (const auto &off : offsets)
  738. target[(y + off.Y) * cube_size + (x + off.X) + offset] = pixel.color;
  739. source++;
  740. }
  741. }
  742. free_image(image);
  743. };
  744. draw_image(top, 1.000000f,
  745. 4, -4, 4 * (size - 1),
  746. 2, 2, 0,
  747. {
  748. {2, 0}, {3, 0}, {4, 0}, {5, 0},
  749. {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1},
  750. {2, 2}, {3, 2}, {4, 2}, {5, 2},
  751. });
  752. draw_image(left, 0.836660f,
  753. 4, 0, 0,
  754. 2, 5, 2 * size,
  755. {
  756. {0, 0}, {1, 0},
  757. {0, 1}, {1, 1}, {2, 1}, {3, 1},
  758. {0, 2}, {1, 2}, {2, 2}, {3, 2},
  759. {0, 3}, {1, 3}, {2, 3}, {3, 3},
  760. {0, 4}, {1, 4}, {2, 4}, {3, 4},
  761. {2, 5}, {3, 5},
  762. });
  763. draw_image(right, 0.670820f,
  764. 4, 0, 4 * size,
  765. -2, 5, 4 * size - 2,
  766. {
  767. {2, 0}, {3, 0},
  768. {0, 1}, {1, 1}, {2, 1}, {3, 1},
  769. {0, 2}, {1, 2}, {2, 2}, {3, 2},
  770. {0, 3}, {1, 3}, {2, 3}, {3, 3},
  771. {0, 4}, {1, 4}, {2, 4}, {3, 4},
  772. {0, 5}, {1, 5},
  773. });
  774. result->unlock();
  775. return result;
  776. }
  777. video::IImage* TextureSource::generateImage(const std::string &name)
  778. {
  779. // Get the base image
  780. const char separator = '^';
  781. const char escape = '\\';
  782. const char paren_open = '(';
  783. const char paren_close = ')';
  784. // Find last separator in the name
  785. s32 last_separator_pos = -1;
  786. u8 paren_bal = 0;
  787. for (s32 i = name.size() - 1; i >= 0; i--) {
  788. if (i > 0 && name[i-1] == escape)
  789. continue;
  790. switch (name[i]) {
  791. case separator:
  792. if (paren_bal == 0) {
  793. last_separator_pos = i;
  794. i = -1; // break out of loop
  795. }
  796. break;
  797. case paren_open:
  798. if (paren_bal == 0) {
  799. errorstream << "generateImage(): unbalanced parentheses"
  800. << "(extranous '(') while generating texture \""
  801. << name << "\"" << std::endl;
  802. return NULL;
  803. }
  804. paren_bal--;
  805. break;
  806. case paren_close:
  807. paren_bal++;
  808. break;
  809. default:
  810. break;
  811. }
  812. }
  813. if (paren_bal > 0) {
  814. errorstream << "generateImage(): unbalanced parentheses"
  815. << "(missing matching '(') while generating texture \""
  816. << name << "\"" << std::endl;
  817. return NULL;
  818. }
  819. video::IImage *baseimg = NULL;
  820. /*
  821. If separator was found, make the base image
  822. using a recursive call.
  823. */
  824. if (last_separator_pos != -1) {
  825. baseimg = generateImage(name.substr(0, last_separator_pos));
  826. }
  827. /*
  828. Parse out the last part of the name of the image and act
  829. according to it
  830. */
  831. std::string last_part_of_name = name.substr(last_separator_pos + 1);
  832. /*
  833. If this name is enclosed in parentheses, generate it
  834. and blit it onto the base image
  835. */
  836. if (last_part_of_name[0] == paren_open
  837. && last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
  838. std::string name2 = last_part_of_name.substr(1,
  839. last_part_of_name.size() - 2);
  840. video::IImage *tmp = generateImage(name2);
  841. if (!tmp) {
  842. errorstream << "generateImage(): "
  843. "Failed to generate \"" << name2 << "\""
  844. << std::endl;
  845. return NULL;
  846. }
  847. core::dimension2d<u32> dim = tmp->getDimension();
  848. if (baseimg) {
  849. blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
  850. tmp->drop();
  851. } else {
  852. baseimg = tmp;
  853. }
  854. } else if (!generateImagePart(last_part_of_name, baseimg)) {
  855. // Generate image according to part of name
  856. errorstream << "generateImage(): "
  857. "Failed to generate \"" << last_part_of_name << "\""
  858. << std::endl;
  859. }
  860. // If no resulting image, print a warning
  861. if (baseimg == NULL) {
  862. errorstream << "generateImage(): baseimg is NULL (attempted to"
  863. " create texture \"" << name << "\")" << std::endl;
  864. }
  865. return baseimg;
  866. }
  867. #if ENABLE_GLES
  868. static inline u16 get_GL_major_version()
  869. {
  870. const GLubyte *gl_version = glGetString(GL_VERSION);
  871. return (u16) (gl_version[0] - '0');
  872. }
  873. /**
  874. * Check if hardware requires npot2 aligned textures
  875. * @return true if alignment NOT(!) requires, false otherwise
  876. */
  877. bool hasNPotSupport()
  878. {
  879. // Only GLES2 is trusted to correctly report npot support
  880. // Note: we cache the boolean result, the GL context will never change.
  881. static const bool supported = get_GL_major_version() > 1 &&
  882. glGetString(GL_EXTENSIONS) &&
  883. strstr((char *)glGetString(GL_EXTENSIONS), "GL_OES_texture_npot");
  884. return supported;
  885. }
  886. /**
  887. * Check and align image to npot2 if required by hardware
  888. * @param image image to check for npot2 alignment
  889. * @param driver driver to use for image operations
  890. * @return image or copy of image aligned to npot2
  891. */
  892. video::IImage * Align2Npot2(video::IImage * image,
  893. video::IVideoDriver* driver)
  894. {
  895. if (image == NULL)
  896. return image;
  897. if (hasNPotSupport())
  898. return image;
  899. core::dimension2d<u32> dim = image->getDimension();
  900. unsigned int height = npot2(dim.Height);
  901. unsigned int width = npot2(dim.Width);
  902. if (dim.Height == height && dim.Width == width)
  903. return image;
  904. if (dim.Height > height)
  905. height *= 2;
  906. if (dim.Width > width)
  907. width *= 2;
  908. video::IImage *targetimage =
  909. driver->createImage(video::ECF_A8R8G8B8,
  910. core::dimension2d<u32>(width, height));
  911. if (targetimage != NULL)
  912. image->copyToScaling(targetimage);
  913. image->drop();
  914. return targetimage;
  915. }
  916. #endif
  917. static std::string unescape_string(const std::string &str, const char esc = '\\')
  918. {
  919. std::string out;
  920. size_t pos = 0, cpos;
  921. out.reserve(str.size());
  922. while (1) {
  923. cpos = str.find_first_of(esc, pos);
  924. if (cpos == std::string::npos) {
  925. out += str.substr(pos);
  926. break;
  927. }
  928. out += str.substr(pos, cpos - pos) + str[cpos + 1];
  929. pos = cpos + 2;
  930. }
  931. return out;
  932. }
  933. bool TextureSource::generateImagePart(std::string part_of_name,
  934. video::IImage *& baseimg)
  935. {
  936. const char escape = '\\'; // same as in generateImage()
  937. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  938. sanity_check(driver);
  939. // Stuff starting with [ are special commands
  940. if (part_of_name.empty() || part_of_name[0] != '[') {
  941. video::IImage *image = m_sourcecache.getOrLoad(part_of_name);
  942. #if ENABLE_GLES
  943. image = Align2Npot2(image, driver);
  944. #endif
  945. if (image == NULL) {
  946. if (!part_of_name.empty()) {
  947. // Do not create normalmap dummies
  948. if (part_of_name.find("_normal.png") != std::string::npos) {
  949. warningstream << "generateImage(): Could not load normal map \""
  950. << part_of_name << "\"" << std::endl;
  951. return true;
  952. }
  953. errorstream << "generateImage(): Could not load image \""
  954. << part_of_name << "\" while building texture; "
  955. "Creating a dummy image" << std::endl;
  956. }
  957. // Just create a dummy image
  958. //core::dimension2d<u32> dim(2,2);
  959. core::dimension2d<u32> dim(1,1);
  960. image = driver->createImage(video::ECF_A8R8G8B8, dim);
  961. sanity_check(image != NULL);
  962. /*image->setPixel(0,0, video::SColor(255,255,0,0));
  963. image->setPixel(1,0, video::SColor(255,0,255,0));
  964. image->setPixel(0,1, video::SColor(255,0,0,255));
  965. image->setPixel(1,1, video::SColor(255,255,0,255));*/
  966. image->setPixel(0,0, video::SColor(255,myrand()%256,
  967. myrand()%256,myrand()%256));
  968. /*image->setPixel(1,0, video::SColor(255,myrand()%256,
  969. myrand()%256,myrand()%256));
  970. image->setPixel(0,1, video::SColor(255,myrand()%256,
  971. myrand()%256,myrand()%256));
  972. image->setPixel(1,1, video::SColor(255,myrand()%256,
  973. myrand()%256,myrand()%256));*/
  974. }
  975. // If base image is NULL, load as base.
  976. if (baseimg == NULL)
  977. {
  978. //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
  979. /*
  980. Copy it this way to get an alpha channel.
  981. Otherwise images with alpha cannot be blitted on
  982. images that don't have alpha in the original file.
  983. */
  984. core::dimension2d<u32> dim = image->getDimension();
  985. baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  986. image->copyTo(baseimg);
  987. }
  988. // Else blit on base.
  989. else
  990. {
  991. //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
  992. // Size of the copied area
  993. core::dimension2d<u32> dim = image->getDimension();
  994. //core::dimension2d<u32> dim(16,16);
  995. // Position to copy the blitted to in the base image
  996. core::position2d<s32> pos_to(0,0);
  997. // Position to copy the blitted from in the blitted image
  998. core::position2d<s32> pos_from(0,0);
  999. // Blit
  1000. /*image->copyToWithAlpha(baseimg, pos_to,
  1001. core::rect<s32>(pos_from, dim),
  1002. video::SColor(255,255,255,255),
  1003. NULL);*/
  1004. core::dimension2d<u32> dim_dst = baseimg->getDimension();
  1005. if (dim == dim_dst) {
  1006. blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
  1007. } else if (dim.Width * dim.Height < dim_dst.Width * dim_dst.Height) {
  1008. // Upscale overlying image
  1009. video::IImage *scaled_image = RenderingEngine::get_video_driver()->
  1010. createImage(video::ECF_A8R8G8B8, dim_dst);
  1011. image->copyToScaling(scaled_image);
  1012. blit_with_alpha(scaled_image, baseimg, pos_from, pos_to, dim_dst);
  1013. scaled_image->drop();
  1014. } else {
  1015. // Upscale base image
  1016. video::IImage *scaled_base = RenderingEngine::get_video_driver()->
  1017. createImage(video::ECF_A8R8G8B8, dim);
  1018. baseimg->copyToScaling(scaled_base);
  1019. baseimg->drop();
  1020. baseimg = scaled_base;
  1021. blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
  1022. }
  1023. }
  1024. //cleanup
  1025. image->drop();
  1026. }
  1027. else
  1028. {
  1029. // A special texture modification
  1030. /*infostream<<"generateImage(): generating special "
  1031. <<"modification \""<<part_of_name<<"\""
  1032. <<std::endl;*/
  1033. /*
  1034. [crack:N:P
  1035. [cracko:N:P
  1036. Adds a cracking texture
  1037. N = animation frame count, P = crack progression
  1038. */
  1039. if (str_starts_with(part_of_name, "[crack"))
  1040. {
  1041. if (baseimg == NULL) {
  1042. errorstream<<"generateImagePart(): baseimg == NULL "
  1043. <<"for part_of_name=\""<<part_of_name
  1044. <<"\", cancelling."<<std::endl;
  1045. return false;
  1046. }
  1047. // Crack image number and overlay option
  1048. // Format: crack[o][:<tiles>]:<frame_count>:<frame>
  1049. bool use_overlay = (part_of_name[6] == 'o');
  1050. Strfnd sf(part_of_name);
  1051. sf.next(":");
  1052. s32 frame_count = stoi(sf.next(":"));
  1053. s32 progression = stoi(sf.next(":"));
  1054. s32 tiles = 1;
  1055. // Check whether there is the <tiles> argument, that is,
  1056. // whether there are 3 arguments. If so, shift values
  1057. // as the first and not the last argument is optional.
  1058. auto s = sf.next(":");
  1059. if (!s.empty()) {
  1060. tiles = frame_count;
  1061. frame_count = progression;
  1062. progression = stoi(s);
  1063. }
  1064. if (progression >= 0) {
  1065. /*
  1066. Load crack image.
  1067. It is an image with a number of cracking stages
  1068. horizontally tiled.
  1069. */
  1070. video::IImage *img_crack = m_sourcecache.getOrLoad(
  1071. "crack_anylength.png");
  1072. if (img_crack) {
  1073. draw_crack(img_crack, baseimg,
  1074. use_overlay, frame_count,
  1075. progression, driver, tiles);
  1076. img_crack->drop();
  1077. }
  1078. }
  1079. }
  1080. /*
  1081. [combine:WxH:X,Y=filename:X,Y=filename2
  1082. Creates a bigger texture from any amount of smaller ones
  1083. */
  1084. else if (str_starts_with(part_of_name, "[combine"))
  1085. {
  1086. Strfnd sf(part_of_name);
  1087. sf.next(":");
  1088. u32 w0 = stoi(sf.next("x"));
  1089. u32 h0 = stoi(sf.next(":"));
  1090. core::dimension2d<u32> dim(w0,h0);
  1091. if (baseimg == NULL) {
  1092. baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1093. baseimg->fill(video::SColor(0,0,0,0));
  1094. }
  1095. while (!sf.at_end()) {
  1096. u32 x = stoi(sf.next(","));
  1097. u32 y = stoi(sf.next("="));
  1098. std::string filename = unescape_string(sf.next_esc(":", escape), escape);
  1099. infostream<<"Adding \""<<filename
  1100. <<"\" to combined ("<<x<<","<<y<<")"
  1101. <<std::endl;
  1102. video::IImage *img = generateImage(filename);
  1103. if (img) {
  1104. core::dimension2d<u32> dim = img->getDimension();
  1105. core::position2d<s32> pos_base(x, y);
  1106. video::IImage *img2 =
  1107. driver->createImage(video::ECF_A8R8G8B8, dim);
  1108. img->copyTo(img2);
  1109. img->drop();
  1110. /*img2->copyToWithAlpha(baseimg, pos_base,
  1111. core::rect<s32>(v2s32(0,0), dim),
  1112. video::SColor(255,255,255,255),
  1113. NULL);*/
  1114. blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
  1115. img2->drop();
  1116. } else {
  1117. errorstream << "generateImagePart(): Failed to load image \""
  1118. << filename << "\" for [combine" << std::endl;
  1119. }
  1120. }
  1121. }
  1122. /*
  1123. [brighten
  1124. */
  1125. else if (str_starts_with(part_of_name, "[brighten"))
  1126. {
  1127. if (baseimg == NULL) {
  1128. errorstream<<"generateImagePart(): baseimg==NULL "
  1129. <<"for part_of_name=\""<<part_of_name
  1130. <<"\", cancelling."<<std::endl;
  1131. return false;
  1132. }
  1133. brighten(baseimg);
  1134. }
  1135. /*
  1136. [noalpha
  1137. Make image completely opaque.
  1138. Used for the leaves texture when in old leaves mode, so
  1139. that the transparent parts don't look completely black
  1140. when simple alpha channel is used for rendering.
  1141. */
  1142. else if (str_starts_with(part_of_name, "[noalpha"))
  1143. {
  1144. if (baseimg == NULL){
  1145. errorstream<<"generateImagePart(): baseimg==NULL "
  1146. <<"for part_of_name=\""<<part_of_name
  1147. <<"\", cancelling."<<std::endl;
  1148. return false;
  1149. }
  1150. core::dimension2d<u32> dim = baseimg->getDimension();
  1151. // Set alpha to full
  1152. for (u32 y=0; y<dim.Height; y++)
  1153. for (u32 x=0; x<dim.Width; x++)
  1154. {
  1155. video::SColor c = baseimg->getPixel(x,y);
  1156. c.setAlpha(255);
  1157. baseimg->setPixel(x,y,c);
  1158. }
  1159. }
  1160. /*
  1161. [makealpha:R,G,B
  1162. Convert one color to transparent.
  1163. */
  1164. else if (str_starts_with(part_of_name, "[makealpha:"))
  1165. {
  1166. if (baseimg == NULL) {
  1167. errorstream<<"generateImagePart(): baseimg == NULL "
  1168. <<"for part_of_name=\""<<part_of_name
  1169. <<"\", cancelling."<<std::endl;
  1170. return false;
  1171. }
  1172. Strfnd sf(part_of_name.substr(11));
  1173. u32 r1 = stoi(sf.next(","));
  1174. u32 g1 = stoi(sf.next(","));
  1175. u32 b1 = stoi(sf.next(""));
  1176. core::dimension2d<u32> dim = baseimg->getDimension();
  1177. /*video::IImage *oldbaseimg = baseimg;
  1178. baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1179. oldbaseimg->copyTo(baseimg);
  1180. oldbaseimg->drop();*/
  1181. // Set alpha to full
  1182. for (u32 y=0; y<dim.Height; y++)
  1183. for (u32 x=0; x<dim.Width; x++)
  1184. {
  1185. video::SColor c = baseimg->getPixel(x,y);
  1186. u32 r = c.getRed();
  1187. u32 g = c.getGreen();
  1188. u32 b = c.getBlue();
  1189. if (!(r == r1 && g == g1 && b == b1))
  1190. continue;
  1191. c.setAlpha(0);
  1192. baseimg->setPixel(x,y,c);
  1193. }
  1194. }
  1195. /*
  1196. [transformN
  1197. Rotates and/or flips the image.
  1198. N can be a number (between 0 and 7) or a transform name.
  1199. Rotations are counter-clockwise.
  1200. 0 I identity
  1201. 1 R90 rotate by 90 degrees
  1202. 2 R180 rotate by 180 degrees
  1203. 3 R270 rotate by 270 degrees
  1204. 4 FX flip X
  1205. 5 FXR90 flip X then rotate by 90 degrees
  1206. 6 FY flip Y
  1207. 7 FYR90 flip Y then rotate by 90 degrees
  1208. Note: Transform names can be concatenated to produce
  1209. their product (applies the first then the second).
  1210. The resulting transform will be equivalent to one of the
  1211. eight existing ones, though (see: dihedral group).
  1212. */
  1213. else if (str_starts_with(part_of_name, "[transform"))
  1214. {
  1215. if (baseimg == NULL) {
  1216. errorstream<<"generateImagePart(): baseimg == NULL "
  1217. <<"for part_of_name=\""<<part_of_name
  1218. <<"\", cancelling."<<std::endl;
  1219. return false;
  1220. }
  1221. u32 transform = parseImageTransform(part_of_name.substr(10));
  1222. core::dimension2d<u32> dim = imageTransformDimension(
  1223. transform, baseimg->getDimension());
  1224. video::IImage *image = driver->createImage(
  1225. baseimg->getColorFormat(), dim);
  1226. sanity_check(image != NULL);
  1227. imageTransform(transform, baseimg, image);
  1228. baseimg->drop();
  1229. baseimg = image;
  1230. }
  1231. /*
  1232. [inventorycube{topimage{leftimage{rightimage
  1233. In every subimage, replace ^ with &.
  1234. Create an "inventory cube".
  1235. NOTE: This should be used only on its own.
  1236. Example (a grass block (not actually used in game):
  1237. "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
  1238. */
  1239. else if (str_starts_with(part_of_name, "[inventorycube"))
  1240. {
  1241. if (baseimg != NULL){
  1242. errorstream<<"generateImagePart(): baseimg != NULL "
  1243. <<"for part_of_name=\""<<part_of_name
  1244. <<"\", cancelling."<<std::endl;
  1245. return false;
  1246. }
  1247. str_replace(part_of_name, '&', '^');
  1248. Strfnd sf(part_of_name);
  1249. sf.next("{");
  1250. std::string imagename_top = sf.next("{");
  1251. std::string imagename_left = sf.next("{");
  1252. std::string imagename_right = sf.next("{");
  1253. // Generate images for the faces of the cube
  1254. video::IImage *img_top = generateImage(imagename_top);
  1255. video::IImage *img_left = generateImage(imagename_left);
  1256. video::IImage *img_right = generateImage(imagename_right);
  1257. if (img_top == NULL || img_left == NULL || img_right == NULL) {
  1258. errorstream << "generateImagePart(): Failed to create textures"
  1259. << " for inventorycube \"" << part_of_name << "\""
  1260. << std::endl;
  1261. baseimg = generateImage(imagename_top);
  1262. return true;
  1263. }
  1264. baseimg = createInventoryCubeImage(img_top, img_left, img_right);
  1265. // Face images are not needed anymore
  1266. img_top->drop();
  1267. img_left->drop();
  1268. img_right->drop();
  1269. return true;
  1270. }
  1271. /*
  1272. [lowpart:percent:filename
  1273. Adds the lower part of a texture
  1274. */
  1275. else if (str_starts_with(part_of_name, "[lowpart:"))
  1276. {
  1277. Strfnd sf(part_of_name);
  1278. sf.next(":");
  1279. u32 percent = stoi(sf.next(":"));
  1280. std::string filename = unescape_string(sf.next_esc(":", escape), escape);
  1281. if (baseimg == NULL)
  1282. baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
  1283. video::IImage *img = generateImage(filename);
  1284. if (img)
  1285. {
  1286. core::dimension2d<u32> dim = img->getDimension();
  1287. core::position2d<s32> pos_base(0, 0);
  1288. video::IImage *img2 =
  1289. driver->createImage(video::ECF_A8R8G8B8, dim);
  1290. img->copyTo(img2);
  1291. img->drop();
  1292. core::position2d<s32> clippos(0, 0);
  1293. clippos.Y = dim.Height * (100-percent) / 100;
  1294. core::dimension2d<u32> clipdim = dim;
  1295. clipdim.Height = clipdim.Height * percent / 100 + 1;
  1296. core::rect<s32> cliprect(clippos, clipdim);
  1297. img2->copyToWithAlpha(baseimg, pos_base,
  1298. core::rect<s32>(v2s32(0,0), dim),
  1299. video::SColor(255,255,255,255),
  1300. &cliprect);
  1301. img2->drop();
  1302. }
  1303. }
  1304. /*
  1305. [verticalframe:N:I
  1306. Crops a frame of a vertical animation.
  1307. N = frame count, I = frame index
  1308. */
  1309. else if (str_starts_with(part_of_name, "[verticalframe:"))
  1310. {
  1311. Strfnd sf(part_of_name);
  1312. sf.next(":");
  1313. u32 frame_count = stoi(sf.next(":"));
  1314. u32 frame_index = stoi(sf.next(":"));
  1315. if (baseimg == NULL){
  1316. errorstream<<"generateImagePart(): baseimg != NULL "
  1317. <<"for part_of_name=\""<<part_of_name
  1318. <<"\", cancelling."<<std::endl;
  1319. return false;
  1320. }
  1321. v2u32 frame_size = baseimg->getDimension();
  1322. frame_size.Y /= frame_count;
  1323. video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
  1324. frame_size);
  1325. if (!img){
  1326. errorstream<<"generateImagePart(): Could not create image "
  1327. <<"for part_of_name=\""<<part_of_name
  1328. <<"\", cancelling."<<std::endl;
  1329. return false;
  1330. }
  1331. // Fill target image with transparency
  1332. img->fill(video::SColor(0,0,0,0));
  1333. core::dimension2d<u32> dim = frame_size;
  1334. core::position2d<s32> pos_dst(0, 0);
  1335. core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
  1336. baseimg->copyToWithAlpha(img, pos_dst,
  1337. core::rect<s32>(pos_src, dim),
  1338. video::SColor(255,255,255,255),
  1339. NULL);
  1340. // Replace baseimg
  1341. baseimg->drop();
  1342. baseimg = img;
  1343. }
  1344. /*
  1345. [mask:filename
  1346. Applies a mask to an image
  1347. */
  1348. else if (str_starts_with(part_of_name, "[mask:"))
  1349. {
  1350. if (baseimg == NULL) {
  1351. errorstream << "generateImage(): baseimg == NULL "
  1352. << "for part_of_name=\"" << part_of_name
  1353. << "\", cancelling." << std::endl;
  1354. return false;
  1355. }
  1356. Strfnd sf(part_of_name);
  1357. sf.next(":");
  1358. std::string filename = unescape_string(sf.next_esc(":", escape), escape);
  1359. video::IImage *img = generateImage(filename);
  1360. if (img) {
  1361. apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
  1362. img->getDimension());
  1363. img->drop();
  1364. } else {
  1365. errorstream << "generateImage(): Failed to load \""
  1366. << filename << "\".";
  1367. }
  1368. }
  1369. /*
  1370. [multiply:color
  1371. multiplys a given color to any pixel of an image
  1372. color = color as ColorString
  1373. */
  1374. else if (str_starts_with(part_of_name, "[multiply:")) {
  1375. Strfnd sf(part_of_name);
  1376. sf.next(":");
  1377. std::string color_str = sf.next(":");
  1378. if (baseimg == NULL) {
  1379. errorstream << "generateImagePart(): baseimg != NULL "
  1380. << "for part_of_name=\"" << part_of_name
  1381. << "\", cancelling." << std::endl;
  1382. return false;
  1383. }
  1384. video::SColor color;
  1385. if (!parseColorString(color_str, color, false))
  1386. return false;
  1387. apply_multiplication(baseimg, v2u32(0, 0), baseimg->getDimension(), color);
  1388. }
  1389. /*
  1390. [colorize:color
  1391. Overlays image with given color
  1392. color = color as ColorString
  1393. */
  1394. else if (str_starts_with(part_of_name, "[colorize:"))
  1395. {
  1396. Strfnd sf(part_of_name);
  1397. sf.next(":");
  1398. std::string color_str = sf.next(":");
  1399. std::string ratio_str = sf.next(":");
  1400. if (baseimg == NULL) {
  1401. errorstream << "generateImagePart(): baseimg != NULL "
  1402. << "for part_of_name=\"" << part_of_name
  1403. << "\", cancelling." << std::endl;
  1404. return false;
  1405. }
  1406. video::SColor color;
  1407. int ratio = -1;
  1408. bool keep_alpha = false;
  1409. if (!parseColorString(color_str, color, false))
  1410. return false;
  1411. if (is_number(ratio_str))
  1412. ratio = mystoi(ratio_str, 0, 255);
  1413. else if (ratio_str == "alpha")
  1414. keep_alpha = true;
  1415. apply_colorize(baseimg, v2u32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha);
  1416. }
  1417. /*
  1418. [applyfiltersformesh
  1419. Internal modifier
  1420. */
  1421. else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
  1422. {
  1423. /* IMPORTANT: When changing this, getTextureForMesh() needs to be
  1424. * updated too. */
  1425. if (!baseimg) {
  1426. errorstream << "generateImagePart(): baseimg == NULL "
  1427. << "for part_of_name=\"" << part_of_name
  1428. << "\", cancelling." << std::endl;
  1429. return false;
  1430. }
  1431. // Apply the "clean transparent" filter, if configured.
  1432. if (g_settings->getBool("texture_clean_transparent"))
  1433. imageCleanTransparent(baseimg, 127);
  1434. /* Upscale textures to user's requested minimum size. This is a trick to make
  1435. * filters look as good on low-res textures as on high-res ones, by making
  1436. * low-res textures BECOME high-res ones. This is helpful for worlds that
  1437. * mix high- and low-res textures, or for mods with least-common-denominator
  1438. * textures that don't have the resources to offer high-res alternatives.
  1439. */
  1440. const bool filter = m_setting_trilinear_filter || m_setting_bilinear_filter;
  1441. const s32 scaleto = filter ? g_settings->getS32("texture_min_size") : 1;
  1442. if (scaleto > 1) {
  1443. const core::dimension2d<u32> dim = baseimg->getDimension();
  1444. /* Calculate scaling needed to make the shortest texture dimension
  1445. * equal to the target minimum. If e.g. this is a vertical frames
  1446. * animation, the short dimension will be the real size.
  1447. */
  1448. if ((dim.Width == 0) || (dim.Height == 0)) {
  1449. errorstream << "generateImagePart(): Illegal 0 dimension "
  1450. << "for part_of_name=\""<< part_of_name
  1451. << "\", cancelling." << std::endl;
  1452. return false;
  1453. }
  1454. u32 xscale = scaleto / dim.Width;
  1455. u32 yscale = scaleto / dim.Height;
  1456. u32 scale = (xscale > yscale) ? xscale : yscale;
  1457. // Never downscale; only scale up by 2x or more.
  1458. if (scale > 1) {
  1459. u32 w = scale * dim.Width;
  1460. u32 h = scale * dim.Height;
  1461. const core::dimension2d<u32> newdim = core::dimension2d<u32>(w, h);
  1462. video::IImage *newimg = driver->createImage(
  1463. baseimg->getColorFormat(), newdim);
  1464. baseimg->copyToScaling(newimg);
  1465. baseimg->drop();
  1466. baseimg = newimg;
  1467. }
  1468. }
  1469. }
  1470. /*
  1471. [resize:WxH
  1472. Resizes the base image to the given dimensions
  1473. */
  1474. else if (str_starts_with(part_of_name, "[resize"))
  1475. {
  1476. if (baseimg == NULL) {
  1477. errorstream << "generateImagePart(): baseimg == NULL "
  1478. << "for part_of_name=\""<< part_of_name
  1479. << "\", cancelling." << std::endl;
  1480. return false;
  1481. }
  1482. Strfnd sf(part_of_name);
  1483. sf.next(":");
  1484. u32 width = stoi(sf.next("x"));
  1485. u32 height = stoi(sf.next(""));
  1486. core::dimension2d<u32> dim(width, height);
  1487. video::IImage *image = RenderingEngine::get_video_driver()->
  1488. createImage(video::ECF_A8R8G8B8, dim);
  1489. baseimg->copyToScaling(image);
  1490. baseimg->drop();
  1491. baseimg = image;
  1492. }
  1493. /*
  1494. [opacity:R
  1495. Makes the base image transparent according to the given ratio.
  1496. R must be between 0 and 255.
  1497. 0 means totally transparent.
  1498. 255 means totally opaque.
  1499. */
  1500. else if (str_starts_with(part_of_name, "[opacity:")) {
  1501. if (baseimg == NULL) {
  1502. errorstream << "generateImagePart(): baseimg == NULL "
  1503. << "for part_of_name=\"" << part_of_name
  1504. << "\", cancelling." << std::endl;
  1505. return false;
  1506. }
  1507. Strfnd sf(part_of_name);
  1508. sf.next(":");
  1509. u32 ratio = mystoi(sf.next(""), 0, 255);
  1510. core::dimension2d<u32> dim = baseimg->getDimension();
  1511. for (u32 y = 0; y < dim.Height; y++)
  1512. for (u32 x = 0; x < dim.Width; x++)
  1513. {
  1514. video::SColor c = baseimg->getPixel(x, y);
  1515. c.setAlpha(floor((c.getAlpha() * ratio) / 255 + 0.5));
  1516. baseimg->setPixel(x, y, c);
  1517. }
  1518. }
  1519. /*
  1520. [invert:mode
  1521. Inverts the given channels of the base image.
  1522. Mode may contain the characters "r", "g", "b", "a".
  1523. Only the channels that are mentioned in the mode string
  1524. will be inverted.
  1525. */
  1526. else if (str_starts_with(part_of_name, "[invert:")) {
  1527. if (baseimg == NULL) {
  1528. errorstream << "generateImagePart(): baseimg == NULL "
  1529. << "for part_of_name=\"" << part_of_name
  1530. << "\", cancelling." << std::endl;
  1531. return false;
  1532. }
  1533. Strfnd sf(part_of_name);
  1534. sf.next(":");
  1535. std::string mode = sf.next("");
  1536. u32 mask = 0;
  1537. if (mode.find('a') != std::string::npos)
  1538. mask |= 0xff000000UL;
  1539. if (mode.find('r') != std::string::npos)
  1540. mask |= 0x00ff0000UL;
  1541. if (mode.find('g') != std::string::npos)
  1542. mask |= 0x0000ff00UL;
  1543. if (mode.find('b') != std::string::npos)
  1544. mask |= 0x000000ffUL;
  1545. core::dimension2d<u32> dim = baseimg->getDimension();
  1546. for (u32 y = 0; y < dim.Height; y++)
  1547. for (u32 x = 0; x < dim.Width; x++)
  1548. {
  1549. video::SColor c = baseimg->getPixel(x, y);
  1550. c.color ^= mask;
  1551. baseimg->setPixel(x, y, c);
  1552. }
  1553. }
  1554. /*
  1555. [sheet:WxH:X,Y
  1556. Retrieves a tile at position X,Y (in tiles)
  1557. from the base image it assumes to be a
  1558. tilesheet with dimensions W,H (in tiles).
  1559. */
  1560. else if (part_of_name.substr(0,7) == "[sheet:") {
  1561. if (baseimg == NULL) {
  1562. errorstream << "generateImagePart(): baseimg != NULL "
  1563. << "for part_of_name=\"" << part_of_name
  1564. << "\", cancelling." << std::endl;
  1565. return false;
  1566. }
  1567. Strfnd sf(part_of_name);
  1568. sf.next(":");
  1569. u32 w0 = stoi(sf.next("x"));
  1570. u32 h0 = stoi(sf.next(":"));
  1571. u32 x0 = stoi(sf.next(","));
  1572. u32 y0 = stoi(sf.next(":"));
  1573. core::dimension2d<u32> img_dim = baseimg->getDimension();
  1574. core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
  1575. video::IImage *img = driver->createImage(
  1576. video::ECF_A8R8G8B8, tile_dim);
  1577. if (!img) {
  1578. errorstream << "generateImagePart(): Could not create image "
  1579. << "for part_of_name=\"" << part_of_name
  1580. << "\", cancelling." << std::endl;
  1581. return false;
  1582. }
  1583. img->fill(video::SColor(0,0,0,0));
  1584. v2u32 vdim(tile_dim);
  1585. core::rect<s32> rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim);
  1586. baseimg->copyToWithAlpha(img, v2s32(0), rect,
  1587. video::SColor(255,255,255,255), NULL);
  1588. // Replace baseimg
  1589. baseimg->drop();
  1590. baseimg = img;
  1591. }
  1592. else
  1593. {
  1594. errorstream << "generateImagePart(): Invalid "
  1595. " modification: \"" << part_of_name << "\"" << std::endl;
  1596. }
  1597. }
  1598. return true;
  1599. }
  1600. /*
  1601. Calculate the color of a single pixel drawn on top of another pixel.
  1602. This is a little more complicated than just video::SColor::getInterpolated
  1603. because getInterpolated does not handle alpha correctly. For example, a
  1604. pixel with alpha=64 drawn atop a pixel with alpha=128 should yield a
  1605. pixel with alpha=160, while getInterpolated would yield alpha=96.
  1606. */
  1607. static inline video::SColor blitPixel(const video::SColor &src_c, const video::SColor &dst_c, u32 ratio)
  1608. {
  1609. if (dst_c.getAlpha() == 0)
  1610. return src_c;
  1611. video::SColor out_c = src_c.getInterpolated(dst_c, (float)ratio / 255.0f);
  1612. out_c.setAlpha(dst_c.getAlpha() + (255 - dst_c.getAlpha()) *
  1613. src_c.getAlpha() * ratio / (255 * 255));
  1614. return out_c;
  1615. }
  1616. /*
  1617. Draw an image on top of an another one, using the alpha channel of the
  1618. source image
  1619. This exists because IImage::copyToWithAlpha() doesn't seem to always
  1620. work.
  1621. */
  1622. static void blit_with_alpha(video::IImage *src, video::IImage *dst,
  1623. v2s32 src_pos, v2s32 dst_pos, v2u32 size)
  1624. {
  1625. for (u32 y0=0; y0<size.Y; y0++)
  1626. for (u32 x0=0; x0<size.X; x0++)
  1627. {
  1628. s32 src_x = src_pos.X + x0;
  1629. s32 src_y = src_pos.Y + y0;
  1630. s32 dst_x = dst_pos.X + x0;
  1631. s32 dst_y = dst_pos.Y + y0;
  1632. video::SColor src_c = src->getPixel(src_x, src_y);
  1633. video::SColor dst_c = dst->getPixel(dst_x, dst_y);
  1634. dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
  1635. dst->setPixel(dst_x, dst_y, dst_c);
  1636. }
  1637. }
  1638. /*
  1639. Draw an image on top of an another one, using the alpha channel of the
  1640. source image; only modify fully opaque pixels in destinaion
  1641. */
  1642. static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
  1643. v2s32 src_pos, v2s32 dst_pos, v2u32 size)
  1644. {
  1645. for (u32 y0=0; y0<size.Y; y0++)
  1646. for (u32 x0=0; x0<size.X; x0++)
  1647. {
  1648. s32 src_x = src_pos.X + x0;
  1649. s32 src_y = src_pos.Y + y0;
  1650. s32 dst_x = dst_pos.X + x0;
  1651. s32 dst_y = dst_pos.Y + y0;
  1652. video::SColor src_c = src->getPixel(src_x, src_y);
  1653. video::SColor dst_c = dst->getPixel(dst_x, dst_y);
  1654. if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
  1655. {
  1656. dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
  1657. dst->setPixel(dst_x, dst_y, dst_c);
  1658. }
  1659. }
  1660. }
  1661. // This function has been disabled because it is currently unused.
  1662. // Feel free to re-enable if you find it handy.
  1663. #if 0
  1664. /*
  1665. Draw an image on top of an another one, using the specified ratio
  1666. modify all partially-opaque pixels in the destination.
  1667. */
  1668. static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
  1669. v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio)
  1670. {
  1671. for (u32 y0 = 0; y0 < size.Y; y0++)
  1672. for (u32 x0 = 0; x0 < size.X; x0++)
  1673. {
  1674. s32 src_x = src_pos.X + x0;
  1675. s32 src_y = src_pos.Y + y0;
  1676. s32 dst_x = dst_pos.X + x0;
  1677. s32 dst_y = dst_pos.Y + y0;
  1678. video::SColor src_c = src->getPixel(src_x, src_y);
  1679. video::SColor dst_c = dst->getPixel(dst_x, dst_y);
  1680. if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0)
  1681. {
  1682. if (ratio == -1)
  1683. dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
  1684. else
  1685. dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f);
  1686. dst->setPixel(dst_x, dst_y, dst_c);
  1687. }
  1688. }
  1689. }
  1690. #endif
  1691. /*
  1692. Apply color to destination
  1693. */
  1694. static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
  1695. const video::SColor &color, int ratio, bool keep_alpha)
  1696. {
  1697. u32 alpha = color.getAlpha();
  1698. video::SColor dst_c;
  1699. if ((ratio == -1 && alpha == 255) || ratio == 255) { // full replacement of color
  1700. if (keep_alpha) { // replace the color with alpha = dest alpha * color alpha
  1701. dst_c = color;
  1702. for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
  1703. for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
  1704. u32 dst_alpha = dst->getPixel(x, y).getAlpha();
  1705. if (dst_alpha > 0) {
  1706. dst_c.setAlpha(dst_alpha * alpha / 255);
  1707. dst->setPixel(x, y, dst_c);
  1708. }
  1709. }
  1710. } else { // replace the color including the alpha
  1711. for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
  1712. for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++)
  1713. if (dst->getPixel(x, y).getAlpha() > 0)
  1714. dst->setPixel(x, y, color);
  1715. }
  1716. } else { // interpolate between the color and destination
  1717. float interp = (ratio == -1 ? color.getAlpha() / 255.0f : ratio / 255.0f);
  1718. for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
  1719. for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
  1720. dst_c = dst->getPixel(x, y);
  1721. if (dst_c.getAlpha() > 0) {
  1722. dst_c = color.getInterpolated(dst_c, interp);
  1723. dst->setPixel(x, y, dst_c);
  1724. }
  1725. }
  1726. }
  1727. }
  1728. /*
  1729. Apply color to destination
  1730. */
  1731. static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size,
  1732. const video::SColor &color)
  1733. {
  1734. video::SColor dst_c;
  1735. for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
  1736. for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
  1737. dst_c = dst->getPixel(x, y);
  1738. dst_c.set(
  1739. dst_c.getAlpha(),
  1740. (dst_c.getRed() * color.getRed()) / 255,
  1741. (dst_c.getGreen() * color.getGreen()) / 255,
  1742. (dst_c.getBlue() * color.getBlue()) / 255
  1743. );
  1744. dst->setPixel(x, y, dst_c);
  1745. }
  1746. }
  1747. /*
  1748. Apply mask to destination
  1749. */
  1750. static void apply_mask(video::IImage *mask, video::IImage *dst,
  1751. v2s32 mask_pos, v2s32 dst_pos, v2u32 size)
  1752. {
  1753. for (u32 y0 = 0; y0 < size.Y; y0++) {
  1754. for (u32 x0 = 0; x0 < size.X; x0++) {
  1755. s32 mask_x = x0 + mask_pos.X;
  1756. s32 mask_y = y0 + mask_pos.Y;
  1757. s32 dst_x = x0 + dst_pos.X;
  1758. s32 dst_y = y0 + dst_pos.Y;
  1759. video::SColor mask_c = mask->getPixel(mask_x, mask_y);
  1760. video::SColor dst_c = dst->getPixel(dst_x, dst_y);
  1761. dst_c.color &= mask_c.color;
  1762. dst->setPixel(dst_x, dst_y, dst_c);
  1763. }
  1764. }
  1765. }
  1766. video::IImage *create_crack_image(video::IImage *crack, s32 frame_index,
  1767. core::dimension2d<u32> size, u8 tiles, video::IVideoDriver *driver)
  1768. {
  1769. core::dimension2d<u32> strip_size = crack->getDimension();
  1770. core::dimension2d<u32> frame_size(strip_size.Width, strip_size.Width);
  1771. core::dimension2d<u32> tile_size(size / tiles);
  1772. s32 frame_count = strip_size.Height / strip_size.Width;
  1773. if (frame_index >= frame_count)
  1774. frame_index = frame_count - 1;
  1775. core::rect<s32> frame(v2s32(0, frame_index * frame_size.Height), frame_size);
  1776. video::IImage *result = nullptr;
  1777. // extract crack frame
  1778. video::IImage *crack_tile = driver->createImage(video::ECF_A8R8G8B8, tile_size);
  1779. if (!crack_tile)
  1780. return nullptr;
  1781. if (tile_size == frame_size) {
  1782. crack->copyTo(crack_tile, v2s32(0, 0), frame);
  1783. } else {
  1784. video::IImage *crack_frame = driver->createImage(video::ECF_A8R8G8B8, frame_size);
  1785. if (!crack_frame)
  1786. goto exit__has_tile;
  1787. crack->copyTo(crack_frame, v2s32(0, 0), frame);
  1788. crack_frame->copyToScaling(crack_tile);
  1789. crack_frame->drop();
  1790. }
  1791. if (tiles == 1)
  1792. return crack_tile;
  1793. // tile it
  1794. result = driver->createImage(video::ECF_A8R8G8B8, size);
  1795. if (!result)
  1796. goto exit__has_tile;
  1797. result->fill({});
  1798. for (u8 i = 0; i < tiles; i++)
  1799. for (u8 j = 0; j < tiles; j++)
  1800. crack_tile->copyTo(result, v2s32(i * tile_size.Width, j * tile_size.Height));
  1801. exit__has_tile:
  1802. crack_tile->drop();
  1803. return result;
  1804. }
  1805. static void draw_crack(video::IImage *crack, video::IImage *dst,
  1806. bool use_overlay, s32 frame_count, s32 progression,
  1807. video::IVideoDriver *driver, u8 tiles)
  1808. {
  1809. // Dimension of destination image
  1810. core::dimension2d<u32> dim_dst = dst->getDimension();
  1811. // Limit frame_count
  1812. if (frame_count > (s32) dim_dst.Height)
  1813. frame_count = dim_dst.Height;
  1814. if (frame_count < 1)
  1815. frame_count = 1;
  1816. // Dimension of the scaled crack stage,
  1817. // which is the same as the dimension of a single destination frame
  1818. core::dimension2d<u32> frame_size(
  1819. dim_dst.Width,
  1820. dim_dst.Height / frame_count
  1821. );
  1822. video::IImage *crack_scaled = create_crack_image(crack, progression,
  1823. frame_size, tiles, driver);
  1824. if (!crack_scaled)
  1825. return;
  1826. auto blit = use_overlay ? blit_with_alpha_overlay : blit_with_alpha;
  1827. for (s32 i = 0; i < frame_count; ++i) {
  1828. v2s32 dst_pos(0, frame_size.Height * i);
  1829. blit(crack_scaled, dst, v2s32(0,0), dst_pos, frame_size);
  1830. }
  1831. crack_scaled->drop();
  1832. }
  1833. void brighten(video::IImage *image)
  1834. {
  1835. if (image == NULL)
  1836. return;
  1837. core::dimension2d<u32> dim = image->getDimension();
  1838. for (u32 y=0; y<dim.Height; y++)
  1839. for (u32 x=0; x<dim.Width; x++)
  1840. {
  1841. video::SColor c = image->getPixel(x,y);
  1842. c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
  1843. c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
  1844. c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
  1845. image->setPixel(x,y,c);
  1846. }
  1847. }
  1848. u32 parseImageTransform(const std::string& s)
  1849. {
  1850. int total_transform = 0;
  1851. std::string transform_names[8];
  1852. transform_names[0] = "i";
  1853. transform_names[1] = "r90";
  1854. transform_names[2] = "r180";
  1855. transform_names[3] = "r270";
  1856. transform_names[4] = "fx";
  1857. transform_names[6] = "fy";
  1858. std::size_t pos = 0;
  1859. while(pos < s.size())
  1860. {
  1861. int transform = -1;
  1862. for (int i = 0; i <= 7; ++i)
  1863. {
  1864. const std::string &name_i = transform_names[i];
  1865. if (s[pos] == ('0' + i))
  1866. {
  1867. transform = i;
  1868. pos++;
  1869. break;
  1870. }
  1871. if (!(name_i.empty()) && lowercase(s.substr(pos, name_i.size())) == name_i) {
  1872. transform = i;
  1873. pos += name_i.size();
  1874. break;
  1875. }
  1876. }
  1877. if (transform < 0)
  1878. break;
  1879. // Multiply total_transform and transform in the group D4
  1880. int new_total = 0;
  1881. if (transform < 4)
  1882. new_total = (transform + total_transform) % 4;
  1883. else
  1884. new_total = (transform - total_transform + 8) % 4;
  1885. if ((transform >= 4) ^ (total_transform >= 4))
  1886. new_total += 4;
  1887. total_transform = new_total;
  1888. }
  1889. return total_transform;
  1890. }
  1891. core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
  1892. {
  1893. if (transform % 2 == 0)
  1894. return dim;
  1895. return core::dimension2d<u32>(dim.Height, dim.Width);
  1896. }
  1897. void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
  1898. {
  1899. if (src == NULL || dst == NULL)
  1900. return;
  1901. core::dimension2d<u32> dstdim = dst->getDimension();
  1902. // Pre-conditions
  1903. assert(dstdim == imageTransformDimension(transform, src->getDimension()));
  1904. assert(transform <= 7);
  1905. /*
  1906. Compute the transformation from source coordinates (sx,sy)
  1907. to destination coordinates (dx,dy).
  1908. */
  1909. int sxn = 0;
  1910. int syn = 2;
  1911. if (transform == 0) // identity
  1912. sxn = 0, syn = 2; // sx = dx, sy = dy
  1913. else if (transform == 1) // rotate by 90 degrees ccw
  1914. sxn = 3, syn = 0; // sx = (H-1) - dy, sy = dx
  1915. else if (transform == 2) // rotate by 180 degrees
  1916. sxn = 1, syn = 3; // sx = (W-1) - dx, sy = (H-1) - dy
  1917. else if (transform == 3) // rotate by 270 degrees ccw
  1918. sxn = 2, syn = 1; // sx = dy, sy = (W-1) - dx
  1919. else if (transform == 4) // flip x
  1920. sxn = 1, syn = 2; // sx = (W-1) - dx, sy = dy
  1921. else if (transform == 5) // flip x then rotate by 90 degrees ccw
  1922. sxn = 2, syn = 0; // sx = dy, sy = dx
  1923. else if (transform == 6) // flip y
  1924. sxn = 0, syn = 3; // sx = dx, sy = (H-1) - dy
  1925. else if (transform == 7) // flip y then rotate by 90 degrees ccw
  1926. sxn = 3, syn = 1; // sx = (H-1) - dy, sy = (W-1) - dx
  1927. for (u32 dy=0; dy<dstdim.Height; dy++)
  1928. for (u32 dx=0; dx<dstdim.Width; dx++)
  1929. {
  1930. u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
  1931. u32 sx = entries[sxn];
  1932. u32 sy = entries[syn];
  1933. video::SColor c = src->getPixel(sx,sy);
  1934. dst->setPixel(dx,dy,c);
  1935. }
  1936. }
  1937. video::ITexture* TextureSource::getNormalTexture(const std::string &name)
  1938. {
  1939. if (isKnownSourceImage("override_normal.png"))
  1940. return getTexture("override_normal.png");
  1941. std::string fname_base = name;
  1942. static const char *normal_ext = "_normal.png";
  1943. static const u32 normal_ext_size = strlen(normal_ext);
  1944. size_t pos = fname_base.find('.');
  1945. std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
  1946. if (isKnownSourceImage(fname_normal)) {
  1947. // look for image extension and replace it
  1948. size_t i = 0;
  1949. while ((i = fname_base.find('.', i)) != std::string::npos) {
  1950. fname_base.replace(i, 4, normal_ext);
  1951. i += normal_ext_size;
  1952. }
  1953. return getTexture(fname_base);
  1954. }
  1955. return NULL;
  1956. }
  1957. video::SColor TextureSource::getTextureAverageColor(const std::string &name)
  1958. {
  1959. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  1960. video::SColor c(0, 0, 0, 0);
  1961. video::ITexture *texture = getTexture(name);
  1962. video::IImage *image = driver->createImage(texture,
  1963. core::position2d<s32>(0, 0),
  1964. texture->getOriginalSize());
  1965. u32 total = 0;
  1966. u32 tR = 0;
  1967. u32 tG = 0;
  1968. u32 tB = 0;
  1969. core::dimension2d<u32> dim = image->getDimension();
  1970. u16 step = 1;
  1971. if (dim.Width > 16)
  1972. step = dim.Width / 16;
  1973. for (u16 x = 0; x < dim.Width; x += step) {
  1974. for (u16 y = 0; y < dim.Width; y += step) {
  1975. c = image->getPixel(x,y);
  1976. if (c.getAlpha() > 0) {
  1977. total++;
  1978. tR += c.getRed();
  1979. tG += c.getGreen();
  1980. tB += c.getBlue();
  1981. }
  1982. }
  1983. }
  1984. image->drop();
  1985. if (total > 0) {
  1986. c.setRed(tR / total);
  1987. c.setGreen(tG / total);
  1988. c.setBlue(tB / total);
  1989. }
  1990. c.setAlpha(255);
  1991. return c;
  1992. }
  1993. video::ITexture *TextureSource::getShaderFlagsTexture(bool normalmap_present)
  1994. {
  1995. std::string tname = "__shaderFlagsTexture";
  1996. tname += normalmap_present ? "1" : "0";
  1997. if (isKnownSourceImage(tname)) {
  1998. return getTexture(tname);
  1999. }
  2000. video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  2001. video::IImage *flags_image = driver->createImage(
  2002. video::ECF_A8R8G8B8, core::dimension2d<u32>(1, 1));
  2003. sanity_check(flags_image != NULL);
  2004. video::SColor c(255, normalmap_present ? 255 : 0, 0, 0);
  2005. flags_image->setPixel(0, 0, c);
  2006. insertSourceImage(tname, flags_image);
  2007. flags_image->drop();
  2008. return getTexture(tname);
  2009. }
  2010. std::vector<std::string> getTextureDirs()
  2011. {
  2012. return fs::GetRecursiveDirs(g_settings->get("texture_path"));
  2013. }