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