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