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