]> git.lizzy.rs Git - dragonfireclient.git/blob - src/tile.cpp
Log unhandled exceptions in connectionthreads to errorstream
[dragonfireclient.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 // Apply a mask to an image
541 static void apply_mask(video::IImage *mask, video::IImage *dst,
542                 v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
543
544 // Draw or overlay a crack
545 static void draw_crack(video::IImage *crack, video::IImage *dst,
546                 bool use_overlay, s32 frame_count, s32 progression,
547                 video::IVideoDriver *driver);
548
549 // Brighten image
550 void brighten(video::IImage *image);
551 // Parse a transform name
552 u32 parseImageTransform(const std::string& s);
553 // Apply transform to image dimension
554 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
555 // Apply transform to image data
556 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
557
558 /*
559         This method generates all the textures
560 */
561 u32 TextureSource::generateTexture(const std::string &name)
562 {
563         //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl;
564
565         // Empty name means texture 0
566         if (name == "") {
567                 infostream<<"generateTexture(): name is empty"<<std::endl;
568                 return 0;
569         }
570
571         {
572                 /*
573                         See if texture already exists
574                 */
575                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
576                 std::map<std::string, u32>::iterator n;
577                 n = m_name_to_id.find(name);
578                 if (n != m_name_to_id.end()) {
579                         return n->second;
580                 }
581         }
582
583         /*
584                 Calling only allowed from main thread
585         */
586         if (get_current_thread_id() != m_main_thread) {
587                 errorstream<<"TextureSource::generateTexture() "
588                                 "called not from main thread"<<std::endl;
589                 return 0;
590         }
591
592         video::IVideoDriver *driver = m_device->getVideoDriver();
593         assert(driver);
594
595         video::IImage *img = generateImage(name);
596
597         video::ITexture *tex = NULL;
598
599         if (img != NULL) {
600 #ifdef __ANDROID__
601                 img = Align2Npot2(img, driver);
602 #endif
603                 // Create texture from resulting image
604                 tex = driver->addTexture(name.c_str(), img);
605                 img->drop();
606         }
607
608         /*
609                 Add texture to caches (add NULL textures too)
610         */
611
612         JMutexAutoLock lock(m_textureinfo_cache_mutex);
613
614         u32 id = m_textureinfo_cache.size();
615         TextureInfo ti(name, tex);
616         m_textureinfo_cache.push_back(ti);
617         m_name_to_id[name] = id;
618
619         return id;
620 }
621
622 std::string TextureSource::getTextureName(u32 id)
623 {
624         JMutexAutoLock lock(m_textureinfo_cache_mutex);
625
626         if(id >= m_textureinfo_cache.size())
627         {
628                 errorstream<<"TextureSource::getTextureName(): id="<<id
629                                 <<" >= m_textureinfo_cache.size()="
630                                 <<m_textureinfo_cache.size()<<std::endl;
631                 return "";
632         }
633
634         return m_textureinfo_cache[id].name;
635 }
636
637 video::ITexture* TextureSource::getTexture(u32 id)
638 {
639         JMutexAutoLock lock(m_textureinfo_cache_mutex);
640
641         if(id >= m_textureinfo_cache.size())
642                 return NULL;
643
644         return m_textureinfo_cache[id].texture;
645 }
646
647 video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
648 {
649         u32 actual_id = getTextureId(name);
650         if(id){
651                 *id = actual_id;
652         }
653         return getTexture(actual_id);
654 }
655
656 void TextureSource::processQueue()
657 {
658         /*
659                 Fetch textures
660         */
661         //NOTE this is only thread safe for ONE consumer thread!
662         if(!m_get_texture_queue.empty())
663         {
664                 GetRequest<std::string, u32, u8, u8>
665                                 request = m_get_texture_queue.pop();
666
667                 /*infostream<<"TextureSource::processQueue(): "
668                                 <<"got texture request with "
669                                 <<"name=\""<<request.key<<"\""
670                                 <<std::endl;*/
671
672                 m_get_texture_queue.pushResult(request, generateTexture(request.key));
673         }
674 }
675
676 void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
677 {
678         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
679
680         assert(get_current_thread_id() == m_main_thread);
681
682         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
683         m_source_image_existence.set(name, true);
684 }
685
686 void TextureSource::rebuildImagesAndTextures()
687 {
688         JMutexAutoLock lock(m_textureinfo_cache_mutex);
689
690         video::IVideoDriver* driver = m_device->getVideoDriver();
691         assert(driver != 0);
692
693         // Recreate textures
694         for(u32 i=0; i<m_textureinfo_cache.size(); i++){
695                 TextureInfo *ti = &m_textureinfo_cache[i];
696                 video::IImage *img = generateImage(ti->name);
697 #ifdef __ANDROID__
698                 img = Align2Npot2(img, driver);
699                 assert(img->getDimension().Height == npot2(img->getDimension().Height));
700                 assert(img->getDimension().Width == npot2(img->getDimension().Width));
701 #endif
702                 // Create texture from resulting image
703                 video::ITexture *t = NULL;
704                 if (img) {
705                         t = driver->addTexture(ti->name.c_str(), img);
706                         img->drop();
707                 }
708                 video::ITexture *t_old = ti->texture;
709                 // Replace texture
710                 ti->texture = t;
711
712                 if (t_old)
713                         m_texture_trash.push_back(t_old);
714         }
715 }
716
717 video::ITexture* TextureSource::generateTextureFromMesh(
718                 const TextureFromMeshParams &params)
719 {
720         video::IVideoDriver *driver = m_device->getVideoDriver();
721         assert(driver);
722
723 #ifdef __ANDROID__
724         const GLubyte* renderstr = glGetString(GL_RENDERER);
725         std::string renderer((char*) renderstr);
726
727         // use no render to texture hack
728         if (
729                 (renderer.find("Adreno") != std::string::npos) ||
730                 (renderer.find("Mali") != std::string::npos) ||
731                 (renderer.find("Immersion") != std::string::npos) ||
732                 (renderer.find("Tegra") != std::string::npos) ||
733                 g_settings->getBool("inventory_image_hack")
734                 ) {
735                 // Get a scene manager
736                 scene::ISceneManager *smgr_main = m_device->getSceneManager();
737                 assert(smgr_main);
738                 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
739                 assert(smgr);
740
741                 const float scaling = 0.2;
742
743                 scene::IMeshSceneNode* meshnode =
744                                 smgr->addMeshSceneNode(params.mesh, NULL,
745                                                 -1, v3f(0,0,0), v3f(0,0,0),
746                                                 v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true);
747                 meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
748                 meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
749                 meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
750                 meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
751                 meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
752
753                 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
754                                 params.camera_position, params.camera_lookat);
755                 // second parameter of setProjectionMatrix (isOrthogonal) is ignored
756                 camera->setProjectionMatrix(params.camera_projection_matrix, false);
757
758                 smgr->setAmbientLight(params.ambient_light);
759                 smgr->addLightSceneNode(0,
760                                 params.light_position,
761                                 params.light_color,
762                                 params.light_radius*scaling);
763
764                 core::dimension2d<u32> screen = driver->getScreenSize();
765
766                 // Render scene
767                 driver->beginScene(true, true, video::SColor(0,0,0,0));
768                 driver->clearZBuffer();
769                 smgr->drawAll();
770
771                 core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
772
773                 irr::video::IImage* rawImage =
774                                 driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
775
776                 u8* pixels = static_cast<u8*>(rawImage->lock());
777                 if (!pixels)
778                 {
779                         rawImage->drop();
780                         return NULL;
781                 }
782
783                 core::rect<s32> source(
784                                 screen.Width /2 - (screen.Width  * (scaling / 2)),
785                                 screen.Height/2 - (screen.Height * (scaling / 2)),
786                                 screen.Width /2 + (screen.Width  * (scaling / 2)),
787                                 screen.Height/2 + (screen.Height * (scaling / 2))
788                         );
789
790                 glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
791                                 partsize.Width, partsize.Height, GL_RGBA,
792                                 GL_UNSIGNED_BYTE, pixels);
793
794                 driver->endScene();
795
796                 // Drop scene manager
797                 smgr->drop();
798
799                 unsigned int pixelcount = partsize.Width*partsize.Height;
800
801                 u8* runptr = pixels;
802                 for (unsigned int i=0; i < pixelcount; i++) {
803
804                         u8 B = *runptr;
805                         u8 G = *(runptr+1);
806                         u8 R = *(runptr+2);
807                         u8 A = *(runptr+3);
808
809                         //BGRA -> RGBA
810                         *runptr = R;
811                         runptr ++;
812                         *runptr = G;
813                         runptr ++;
814                         *runptr = B;
815                         runptr ++;
816                         *runptr = A;
817                         runptr ++;
818                 }
819
820                 video::IImage* inventory_image =
821                                 driver->createImage(irr::video::ECF_A8R8G8B8, params.dim);
822
823                 rawImage->copyToScaling(inventory_image);
824                 rawImage->drop();
825
826                 video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
827                 inventory_image->drop();
828
829                 if (rtt == NULL) {
830                         errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
831                         return NULL;
832                 }
833
834                 driver->makeColorKeyTexture(rtt, v2s32(0,0));
835
836                 if(params.delete_texture_on_shutdown)
837                         m_texture_trash.push_back(rtt);
838
839                 return rtt;
840         }
841 #endif
842
843         if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
844         {
845                 static bool warned = false;
846                 if(!warned)
847                 {
848                         errorstream<<"TextureSource::generateTextureFromMesh(): "
849                                 <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
850                         warned = true;
851                 }
852                 return NULL;
853         }
854
855         // Create render target texture
856         video::ITexture *rtt = driver->addRenderTargetTexture(
857                         params.dim, params.rtt_texture_name.c_str(),
858                         video::ECF_A8R8G8B8);
859         if(rtt == NULL)
860         {
861                 errorstream<<"TextureSource::generateTextureFromMesh(): "
862                         <<"addRenderTargetTexture returned NULL."<<std::endl;
863                 return NULL;
864         }
865
866         // Set render target
867         if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
868                 driver->removeTexture(rtt);
869                 errorstream<<"TextureSource::generateTextureFromMesh(): "
870                         <<"failed to set render target"<<std::endl;
871                 return NULL;
872         }
873
874         // Get a scene manager
875         scene::ISceneManager *smgr_main = m_device->getSceneManager();
876         assert(smgr_main);
877         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
878         assert(smgr);
879
880         scene::IMeshSceneNode* meshnode =
881                         smgr->addMeshSceneNode(params.mesh, NULL,
882                                         -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
883         meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
884         meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
885         meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
886         meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
887         meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
888
889         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
890                         params.camera_position, params.camera_lookat);
891         // second parameter of setProjectionMatrix (isOrthogonal) is ignored
892         camera->setProjectionMatrix(params.camera_projection_matrix, false);
893
894         smgr->setAmbientLight(params.ambient_light);
895         smgr->addLightSceneNode(0,
896                         params.light_position,
897                         params.light_color,
898                         params.light_radius);
899
900         // Render scene
901         driver->beginScene(true, true, video::SColor(0,0,0,0));
902         smgr->drawAll();
903         driver->endScene();
904
905         // Drop scene manager
906         smgr->drop();
907
908         // Unset render target
909         driver->setRenderTarget(0, false, true, 0);
910
911         if(params.delete_texture_on_shutdown)
912                 m_texture_trash.push_back(rtt);
913
914         return rtt;
915 }
916
917 video::IImage* TextureSource::generateImage(const std::string &name)
918 {
919         /*
920                 Get the base image
921         */
922
923         const char separator = '^';
924         const char paren_open = '(';
925         const char paren_close = ')';
926
927         // Find last separator in the name
928         s32 last_separator_pos = -1;
929         u8 paren_bal = 0;
930         for(s32 i = name.size() - 1; i >= 0; i--) {
931                 switch(name[i]) {
932                 case separator:
933                         if (paren_bal == 0) {
934                                 last_separator_pos = i;
935                                 i = -1; // break out of loop
936                         }
937                         break;
938                 case paren_open:
939                         if (paren_bal == 0) {
940                                 errorstream << "generateImage(): unbalanced parentheses"
941                                                 << "(extranous '(') while generating texture \""
942                                                 << name << "\"" << std::endl;
943                                 return NULL;
944                         }
945                         paren_bal--;
946                         break;
947                 case paren_close:
948                         paren_bal++;
949                         break;
950                 default:
951                         break;
952                 }
953         }
954         if (paren_bal > 0) {
955                 errorstream << "generateImage(): unbalanced parentheses"
956                                 << "(missing matching '(') while generating texture \""
957                                 << name << "\"" << std::endl;
958                 return NULL;
959         }
960
961
962         video::IImage *baseimg = NULL;
963
964         /*
965                 If separator was found, make the base image
966                 using a recursive call.
967         */
968         if (last_separator_pos != -1) {
969                 baseimg = generateImage(name.substr(0, last_separator_pos));
970         }
971
972
973         video::IVideoDriver* driver = m_device->getVideoDriver();
974         assert(driver);
975
976         /*
977                 Parse out the last part of the name of the image and act
978                 according to it
979         */
980
981         std::string last_part_of_name = name.substr(last_separator_pos + 1);
982
983         /* 
984                 If this name is enclosed in parentheses, generate it
985                 and blit it onto the base image
986         */
987         if (last_part_of_name[0] == paren_open
988                         && last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
989                 std::string name2 = last_part_of_name.substr(1,
990                                 last_part_of_name.size() - 2);
991                 video::IImage *tmp = generateImage(name2);
992                 if (!tmp) {
993                         errorstream << "generateImage(): "
994                                 "Failed to generate \"" << name2 << "\""
995                                 << std::endl;
996                         return NULL;
997                 }
998                 core::dimension2d<u32> dim = tmp->getDimension();
999                 if (!baseimg)
1000                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1001                 blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
1002                 tmp->drop();
1003         } else if (!generateImagePart(last_part_of_name, baseimg)) {
1004                 // Generate image according to part of name
1005                 errorstream << "generateImage(): "
1006                                 "Failed to generate \"" << last_part_of_name << "\""
1007                                 << std::endl;
1008         }
1009
1010         // If no resulting image, print a warning
1011         if (baseimg == NULL) {
1012                 errorstream << "generateImage(): baseimg is NULL (attempted to"
1013                                 " create texture \"" << name << "\")" << std::endl;
1014         }
1015
1016         return baseimg;
1017 }
1018
1019 #ifdef __ANDROID__
1020 #include <GLES/gl.h>
1021 /**
1022  * Check and align image to npot2 if required by hardware
1023  * @param image image to check for npot2 alignment
1024  * @param driver driver to use for image operations
1025  * @return image or copy of image aligned to npot2
1026  */
1027 video::IImage * Align2Npot2(video::IImage * image,
1028                 video::IVideoDriver* driver)
1029 {
1030         if(image == NULL) {
1031                 return image;
1032         }
1033
1034         core::dimension2d<u32> dim = image->getDimension();
1035
1036         std::string extensions = (char*) glGetString(GL_EXTENSIONS);
1037         if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
1038                 return image;
1039         }
1040
1041         unsigned int height = npot2(dim.Height);
1042         unsigned int width  = npot2(dim.Width);
1043
1044         if ((dim.Height == height) &&
1045                         (dim.Width == width)) {
1046                 return image;
1047         }
1048
1049         if (dim.Height > height) {
1050                 height *= 2;
1051         }
1052
1053         if (dim.Width > width) {
1054                 width *= 2;
1055         }
1056
1057         video::IImage *targetimage =
1058                         driver->createImage(video::ECF_A8R8G8B8,
1059                                         core::dimension2d<u32>(width, height));
1060
1061         if (targetimage != NULL) {
1062                 image->copyToScaling(targetimage);
1063         }
1064         image->drop();
1065         return targetimage;
1066 }
1067
1068 #endif
1069
1070 bool TextureSource::generateImagePart(std::string part_of_name,
1071                 video::IImage *& baseimg)
1072 {
1073         video::IVideoDriver* driver = m_device->getVideoDriver();
1074         assert(driver);
1075
1076         // Stuff starting with [ are special commands
1077         if(part_of_name.size() == 0 || part_of_name[0] != '[')
1078         {
1079                 video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
1080 #ifdef __ANDROID__
1081                 image = Align2Npot2(image, driver);
1082 #endif
1083                 if (image == NULL) {
1084                         if (part_of_name != "") {
1085                                 if (part_of_name.find("_normal.png") == std::string::npos){
1086                                         errorstream<<"generateImage(): Could not load image \""
1087                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
1088                                         errorstream<<"generateImage(): Creating a dummy"
1089                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
1090                                 } else {
1091                                         infostream<<"generateImage(): Could not load normal map \""
1092                                                 <<part_of_name<<"\""<<std::endl;
1093                                         infostream<<"generateImage(): Creating a dummy"
1094                                                 <<" normal map for \""<<part_of_name<<"\""<<std::endl;
1095                                 }
1096                         }
1097
1098                         // Just create a dummy image
1099                         //core::dimension2d<u32> dim(2,2);
1100                         core::dimension2d<u32> dim(1,1);
1101                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
1102                         assert(image);
1103                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
1104                         image->setPixel(1,0, video::SColor(255,0,255,0));
1105                         image->setPixel(0,1, video::SColor(255,0,0,255));
1106                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
1107                         image->setPixel(0,0, video::SColor(255,myrand()%256,
1108                                         myrand()%256,myrand()%256));
1109                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
1110                                         myrand()%256,myrand()%256));
1111                         image->setPixel(0,1, video::SColor(255,myrand()%256,
1112                                         myrand()%256,myrand()%256));
1113                         image->setPixel(1,1, video::SColor(255,myrand()%256,
1114                                         myrand()%256,myrand()%256));*/
1115                 }
1116
1117                 // If base image is NULL, load as base.
1118                 if(baseimg == NULL)
1119                 {
1120                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
1121                         /*
1122                                 Copy it this way to get an alpha channel.
1123                                 Otherwise images with alpha cannot be blitted on
1124                                 images that don't have alpha in the original file.
1125                         */
1126                         core::dimension2d<u32> dim = image->getDimension();
1127                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1128                         image->copyTo(baseimg);
1129                 }
1130                 // Else blit on base.
1131                 else
1132                 {
1133                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1134                         // Size of the copied area
1135                         core::dimension2d<u32> dim = image->getDimension();
1136                         //core::dimension2d<u32> dim(16,16);
1137                         // Position to copy the blitted to in the base image
1138                         core::position2d<s32> pos_to(0,0);
1139                         // Position to copy the blitted from in the blitted image
1140                         core::position2d<s32> pos_from(0,0);
1141                         // Blit
1142                         /*image->copyToWithAlpha(baseimg, pos_to,
1143                                         core::rect<s32>(pos_from, dim),
1144                                         video::SColor(255,255,255,255),
1145                                         NULL);*/
1146                         blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
1147                 }
1148                 //cleanup
1149                 image->drop();
1150         }
1151         else
1152         {
1153                 // A special texture modification
1154
1155                 /*infostream<<"generateImage(): generating special "
1156                                 <<"modification \""<<part_of_name<<"\""
1157                                 <<std::endl;*/
1158
1159                 /*
1160                         [crack:N:P
1161                         [cracko:N:P
1162                         Adds a cracking texture
1163                         N = animation frame count, P = crack progression
1164                 */
1165                 if(part_of_name.substr(0,6) == "[crack")
1166                 {
1167                         if (baseimg == NULL) {
1168                                 errorstream<<"generateImagePart(): baseimg == NULL "
1169                                                 <<"for part_of_name=\""<<part_of_name
1170                                                 <<"\", cancelling."<<std::endl;
1171                                 return false;
1172                         }
1173
1174                         // Crack image number and overlay option
1175                         bool use_overlay = (part_of_name[6] == 'o');
1176                         Strfnd sf(part_of_name);
1177                         sf.next(":");
1178                         s32 frame_count = stoi(sf.next(":"));
1179                         s32 progression = stoi(sf.next(":"));
1180
1181                         /*
1182                                 Load crack image.
1183
1184                                 It is an image with a number of cracking stages
1185                                 horizontally tiled.
1186                         */
1187                         video::IImage *img_crack = m_sourcecache.getOrLoad(
1188                                         "crack_anylength.png", m_device);
1189
1190                         if(img_crack && progression >= 0)
1191                         {
1192                                 draw_crack(img_crack, baseimg,
1193                                                 use_overlay, frame_count,
1194                                                 progression, driver);
1195                                 img_crack->drop();
1196                         }
1197                 }
1198                 /*
1199                         [combine:WxH:X,Y=filename:X,Y=filename2
1200                         Creates a bigger texture from an amount of smaller ones
1201                 */
1202                 else if(part_of_name.substr(0,8) == "[combine")
1203                 {
1204                         Strfnd sf(part_of_name);
1205                         sf.next(":");
1206                         u32 w0 = stoi(sf.next("x"));
1207                         u32 h0 = stoi(sf.next(":"));
1208                         //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1209                         core::dimension2d<u32> dim(w0,h0);
1210                         if (baseimg == NULL) {
1211                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1212                                 baseimg->fill(video::SColor(0,0,0,0));
1213                         }
1214                         while (sf.atend() == false) {
1215                                 u32 x = stoi(sf.next(","));
1216                                 u32 y = stoi(sf.next("="));
1217                                 std::string filename = sf.next(":");
1218                                 infostream<<"Adding \""<<filename
1219                                                 <<"\" to combined ("<<x<<","<<y<<")"
1220                                                 <<std::endl;
1221                                 video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1222                                 if (img) {
1223                                         core::dimension2d<u32> dim = img->getDimension();
1224                                         infostream<<"Size "<<dim.Width
1225                                                         <<"x"<<dim.Height<<std::endl;
1226                                         core::position2d<s32> pos_base(x, y);
1227                                         video::IImage *img2 =
1228                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1229                                         img->copyTo(img2);
1230                                         img->drop();
1231                                         /*img2->copyToWithAlpha(baseimg, pos_base,
1232                                                         core::rect<s32>(v2s32(0,0), dim),
1233                                                         video::SColor(255,255,255,255),
1234                                                         NULL);*/
1235                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
1236                                         img2->drop();
1237                                 } else {
1238                                         errorstream << "generateImagePart(): Failed to load image \""
1239                                                 << filename << "\" for [combine" << std::endl;
1240                                 }
1241                         }
1242                 }
1243                 /*
1244                         "[brighten"
1245                 */
1246                 else if(part_of_name.substr(0,9) == "[brighten")
1247                 {
1248                         if (baseimg == NULL) {
1249                                 errorstream<<"generateImagePart(): baseimg==NULL "
1250                                                 <<"for part_of_name=\""<<part_of_name
1251                                                 <<"\", cancelling."<<std::endl;
1252                                 return false;
1253                         }
1254
1255                         brighten(baseimg);
1256                 }
1257                 /*
1258                         "[noalpha"
1259                         Make image completely opaque.
1260                         Used for the leaves texture when in old leaves mode, so
1261                         that the transparent parts don't look completely black
1262                         when simple alpha channel is used for rendering.
1263                 */
1264                 else if(part_of_name.substr(0,8) == "[noalpha")
1265                 {
1266                         if (baseimg == NULL){
1267                                 errorstream<<"generateImagePart(): baseimg==NULL "
1268                                                 <<"for part_of_name=\""<<part_of_name
1269                                                 <<"\", cancelling."<<std::endl;
1270                                 return false;
1271                         }
1272
1273                         core::dimension2d<u32> dim = baseimg->getDimension();
1274
1275                         // Set alpha to full
1276                         for(u32 y=0; y<dim.Height; y++)
1277                         for(u32 x=0; x<dim.Width; x++)
1278                         {
1279                                 video::SColor c = baseimg->getPixel(x,y);
1280                                 c.setAlpha(255);
1281                                 baseimg->setPixel(x,y,c);
1282                         }
1283                 }
1284                 /*
1285                         "[makealpha:R,G,B"
1286                         Convert one color to transparent.
1287                 */
1288                 else if(part_of_name.substr(0,11) == "[makealpha:")
1289                 {
1290                         if (baseimg == NULL) {
1291                                 errorstream<<"generateImagePart(): baseimg == NULL "
1292                                                 <<"for part_of_name=\""<<part_of_name
1293                                                 <<"\", cancelling."<<std::endl;
1294                                 return false;
1295                         }
1296
1297                         Strfnd sf(part_of_name.substr(11));
1298                         u32 r1 = stoi(sf.next(","));
1299                         u32 g1 = stoi(sf.next(","));
1300                         u32 b1 = stoi(sf.next(""));
1301                         std::string filename = sf.next("");
1302
1303                         core::dimension2d<u32> dim = baseimg->getDimension();
1304
1305                         /*video::IImage *oldbaseimg = baseimg;
1306                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1307                         oldbaseimg->copyTo(baseimg);
1308                         oldbaseimg->drop();*/
1309
1310                         // Set alpha to full
1311                         for(u32 y=0; y<dim.Height; y++)
1312                         for(u32 x=0; x<dim.Width; x++)
1313                         {
1314                                 video::SColor c = baseimg->getPixel(x,y);
1315                                 u32 r = c.getRed();
1316                                 u32 g = c.getGreen();
1317                                 u32 b = c.getBlue();
1318                                 if(!(r == r1 && g == g1 && b == b1))
1319                                         continue;
1320                                 c.setAlpha(0);
1321                                 baseimg->setPixel(x,y,c);
1322                         }
1323                 }
1324                 /*
1325                         "[transformN"
1326                         Rotates and/or flips the image.
1327
1328                         N can be a number (between 0 and 7) or a transform name.
1329                         Rotations are counter-clockwise.
1330                         0  I      identity
1331                         1  R90    rotate by 90 degrees
1332                         2  R180   rotate by 180 degrees
1333                         3  R270   rotate by 270 degrees
1334                         4  FX     flip X
1335                         5  FXR90  flip X then rotate by 90 degrees
1336                         6  FY     flip Y
1337                         7  FYR90  flip Y then rotate by 90 degrees
1338
1339                         Note: Transform names can be concatenated to produce
1340                         their product (applies the first then the second).
1341                         The resulting transform will be equivalent to one of the
1342                         eight existing ones, though (see: dihedral group).
1343                 */
1344                 else if(part_of_name.substr(0,10) == "[transform")
1345                 {
1346                         if (baseimg == NULL) {
1347                                 errorstream<<"generateImagePart(): baseimg == NULL "
1348                                                 <<"for part_of_name=\""<<part_of_name
1349                                                 <<"\", cancelling."<<std::endl;
1350                                 return false;
1351                         }
1352
1353                         u32 transform = parseImageTransform(part_of_name.substr(10));
1354                         core::dimension2d<u32> dim = imageTransformDimension(
1355                                         transform, baseimg->getDimension());
1356                         video::IImage *image = driver->createImage(
1357                                         baseimg->getColorFormat(), dim);
1358                         assert(image);
1359                         imageTransform(transform, baseimg, image);
1360                         baseimg->drop();
1361                         baseimg = image;
1362                 }
1363                 /*
1364                         [inventorycube{topimage{leftimage{rightimage
1365                         In every subimage, replace ^ with &.
1366                         Create an "inventory cube".
1367                         NOTE: This should be used only on its own.
1368                         Example (a grass block (not actually used in game):
1369                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1370                 */
1371                 else if(part_of_name.substr(0,14) == "[inventorycube")
1372                 {
1373                         if (baseimg != NULL){
1374                                 errorstream<<"generateImagePart(): baseimg != NULL "
1375                                                 <<"for part_of_name=\""<<part_of_name
1376                                                 <<"\", cancelling."<<std::endl;
1377                                 return false;
1378                         }
1379
1380                         str_replace_char(part_of_name, '&', '^');
1381                         Strfnd sf(part_of_name);
1382                         sf.next("{");
1383                         std::string imagename_top = sf.next("{");
1384                         std::string imagename_left = sf.next("{");
1385                         std::string imagename_right = sf.next("{");
1386
1387                         // Generate images for the faces of the cube
1388                         video::IImage *img_top = generateImage(imagename_top);
1389                         video::IImage *img_left = generateImage(imagename_left);
1390                         video::IImage *img_right = generateImage(imagename_right);
1391
1392                         if (img_top == NULL || img_left == NULL || img_right == NULL) {
1393                                 errorstream << "generateImagePart(): Failed to create textures"
1394                                                 << " for inventorycube \"" << part_of_name << "\""
1395                                                 << std::endl;
1396                                 baseimg = generateImage(imagename_top);
1397                                 return true;
1398                         }
1399
1400 #ifdef __ANDROID__
1401                         assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height));
1402                         assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width));
1403
1404                         assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height));
1405                         assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width));
1406
1407                         assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height));
1408                         assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width));
1409 #endif
1410
1411                         // Create textures from images
1412                         video::ITexture *texture_top = driver->addTexture(
1413                                         (imagename_top + "__temp__").c_str(), img_top);
1414                         video::ITexture *texture_left = driver->addTexture(
1415                                         (imagename_left + "__temp__").c_str(), img_left);
1416                         video::ITexture *texture_right = driver->addTexture(
1417                                         (imagename_right + "__temp__").c_str(), img_right);
1418                         assert(texture_top && texture_left && texture_right);
1419
1420                         // Drop images
1421                         img_top->drop();
1422                         img_left->drop();
1423                         img_right->drop();
1424
1425                         /*
1426                                 Draw a cube mesh into a render target texture
1427                         */
1428                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1429                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1430                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1431                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1432                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1433                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1434                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1435                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1436
1437                         TextureFromMeshParams params;
1438                         params.mesh = cube;
1439                         params.dim.set(64, 64);
1440                         params.rtt_texture_name = part_of_name + "_RTT";
1441                         // We will delete the rtt texture ourselves
1442                         params.delete_texture_on_shutdown = false;
1443                         params.camera_position.set(0, 1.0, -1.5);
1444                         params.camera_position.rotateXZBy(45);
1445                         params.camera_lookat.set(0, 0, 0);
1446                         // Set orthogonal projection
1447                         params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
1448                                         1.65, 1.65, 0, 100);
1449
1450                         params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
1451                         params.light_position.set(10, 100, -50);
1452                         params.light_color.set(1.0, 0.5, 0.5, 0.5);
1453                         params.light_radius = 1000;
1454
1455                         video::ITexture *rtt = generateTextureFromMesh(params);
1456
1457                         // Drop mesh
1458                         cube->drop();
1459
1460                         // Free textures
1461                         driver->removeTexture(texture_top);
1462                         driver->removeTexture(texture_left);
1463                         driver->removeTexture(texture_right);
1464
1465                         if (rtt == NULL) {
1466                                 baseimg = generateImage(imagename_top);
1467                                 return true;
1468                         }
1469
1470                         // Create image of render target
1471                         video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
1472                         assert(image);
1473
1474                         // Cleanup texture
1475                         driver->removeTexture(rtt);
1476
1477                         baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
1478
1479                         if (image) {
1480                                 image->copyTo(baseimg);
1481                                 image->drop();
1482                         }
1483                 }
1484                 /*
1485                         [lowpart:percent:filename
1486                         Adds the lower part of a texture
1487                 */
1488                 else if(part_of_name.substr(0,9) == "[lowpart:")
1489                 {
1490                         Strfnd sf(part_of_name);
1491                         sf.next(":");
1492                         u32 percent = stoi(sf.next(":"));
1493                         std::string filename = sf.next(":");
1494                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1495
1496                         if(baseimg == NULL)
1497                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1498                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1499                         if(img)
1500                         {
1501                                 core::dimension2d<u32> dim = img->getDimension();
1502                                 core::position2d<s32> pos_base(0, 0);
1503                                 video::IImage *img2 =
1504                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1505                                 img->copyTo(img2);
1506                                 img->drop();
1507                                 core::position2d<s32> clippos(0, 0);
1508                                 clippos.Y = dim.Height * (100-percent) / 100;
1509                                 core::dimension2d<u32> clipdim = dim;
1510                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1511                                 core::rect<s32> cliprect(clippos, clipdim);
1512                                 img2->copyToWithAlpha(baseimg, pos_base,
1513                                                 core::rect<s32>(v2s32(0,0), dim),
1514                                                 video::SColor(255,255,255,255),
1515                                                 &cliprect);
1516                                 img2->drop();
1517                         }
1518                 }
1519                 /*
1520                         [verticalframe:N:I
1521                         Crops a frame of a vertical animation.
1522                         N = frame count, I = frame index
1523                 */
1524                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1525                 {
1526                         Strfnd sf(part_of_name);
1527                         sf.next(":");
1528                         u32 frame_count = stoi(sf.next(":"));
1529                         u32 frame_index = stoi(sf.next(":"));
1530
1531                         if(baseimg == NULL){
1532                                 errorstream<<"generateImagePart(): baseimg != NULL "
1533                                                 <<"for part_of_name=\""<<part_of_name
1534                                                 <<"\", cancelling."<<std::endl;
1535                                 return false;
1536                         }
1537
1538                         v2u32 frame_size = baseimg->getDimension();
1539                         frame_size.Y /= frame_count;
1540
1541                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1542                                         frame_size);
1543                         if(!img){
1544                                 errorstream<<"generateImagePart(): Could not create image "
1545                                                 <<"for part_of_name=\""<<part_of_name
1546                                                 <<"\", cancelling."<<std::endl;
1547                                 return false;
1548                         }
1549
1550                         // Fill target image with transparency
1551                         img->fill(video::SColor(0,0,0,0));
1552
1553                         core::dimension2d<u32> dim = frame_size;
1554                         core::position2d<s32> pos_dst(0, 0);
1555                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1556                         baseimg->copyToWithAlpha(img, pos_dst,
1557                                         core::rect<s32>(pos_src, dim),
1558                                         video::SColor(255,255,255,255),
1559                                         NULL);
1560                         // Replace baseimg
1561                         baseimg->drop();
1562                         baseimg = img;
1563                 }
1564                 /*
1565                         [mask:filename
1566                         Applies a mask to an image
1567                 */
1568                 else if(part_of_name.substr(0,6) == "[mask:")
1569                 {
1570                         if (baseimg == NULL) {
1571                                 errorstream << "generateImage(): baseimg == NULL "
1572                                                 << "for part_of_name=\"" << part_of_name
1573                                                 << "\", cancelling." << std::endl;
1574                                 return false;
1575                         }
1576                         Strfnd sf(part_of_name);
1577                         sf.next(":");
1578                         std::string filename = sf.next(":");
1579
1580                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1581                         if (img) {
1582                                 apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
1583                                                 img->getDimension());
1584                         } else {
1585                                 errorstream << "generateImage(): Failed to load \""
1586                                                 << filename << "\".";
1587                         }
1588                 }
1589                 else
1590                 {
1591                         errorstream<<"generateImagePart(): Invalid "
1592                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1593                 }
1594         }
1595
1596         return true;
1597 }
1598
1599 /*
1600         Draw an image on top of an another one, using the alpha channel of the
1601         source image
1602
1603         This exists because IImage::copyToWithAlpha() doesn't seem to always
1604         work.
1605 */
1606 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1607                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1608 {
1609         for(u32 y0=0; y0<size.Y; y0++)
1610         for(u32 x0=0; x0<size.X; x0++)
1611         {
1612                 s32 src_x = src_pos.X + x0;
1613                 s32 src_y = src_pos.Y + y0;
1614                 s32 dst_x = dst_pos.X + x0;
1615                 s32 dst_y = dst_pos.Y + y0;
1616                 video::SColor src_c = src->getPixel(src_x, src_y);
1617                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1618                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1619                 dst->setPixel(dst_x, dst_y, dst_c);
1620         }
1621 }
1622
1623 /*
1624         Draw an image on top of an another one, using the alpha channel of the
1625         source image; only modify fully opaque pixels in destinaion
1626 */
1627 static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
1628                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1629 {
1630         for(u32 y0=0; y0<size.Y; y0++)
1631         for(u32 x0=0; x0<size.X; x0++)
1632         {
1633                 s32 src_x = src_pos.X + x0;
1634                 s32 src_y = src_pos.Y + y0;
1635                 s32 dst_x = dst_pos.X + x0;
1636                 s32 dst_y = dst_pos.Y + y0;
1637                 video::SColor src_c = src->getPixel(src_x, src_y);
1638                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1639                 if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
1640                 {
1641                         dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1642                         dst->setPixel(dst_x, dst_y, dst_c);
1643                 }
1644         }
1645 }
1646
1647 /*
1648         Apply mask to destination
1649 */
1650 static void apply_mask(video::IImage *mask, video::IImage *dst,
1651                 v2s32 mask_pos, v2s32 dst_pos, v2u32 size)
1652 {
1653         for(u32 y0 = 0; y0 < size.Y; y0++) {
1654                 for(u32 x0 = 0; x0 < size.X; x0++) {
1655                         s32 mask_x = x0 + mask_pos.X;
1656                         s32 mask_y = y0 + mask_pos.Y;
1657                         s32 dst_x = x0 + dst_pos.X;
1658                         s32 dst_y = y0 + dst_pos.Y;
1659                         video::SColor mask_c = mask->getPixel(mask_x, mask_y);
1660                         video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1661                         dst_c.color &= mask_c.color;
1662                         dst->setPixel(dst_x, dst_y, dst_c);
1663                 }
1664         }
1665 }
1666
1667 static void draw_crack(video::IImage *crack, video::IImage *dst,
1668                 bool use_overlay, s32 frame_count, s32 progression,
1669                 video::IVideoDriver *driver)
1670 {
1671         // Dimension of destination image
1672         core::dimension2d<u32> dim_dst = dst->getDimension();
1673         // Dimension of original image
1674         core::dimension2d<u32> dim_crack = crack->getDimension();
1675         // Count of crack stages
1676         s32 crack_count = dim_crack.Height / dim_crack.Width;
1677         // Limit frame_count
1678         if(frame_count > (s32) dim_dst.Height)
1679                 frame_count = dim_dst.Height;
1680         if(frame_count < 1)
1681                 frame_count = 1;
1682         // Limit progression
1683         if(progression > crack_count-1)
1684                 progression = crack_count-1;
1685         // Dimension of a single crack stage
1686         core::dimension2d<u32> dim_crack_cropped(
1687                 dim_crack.Width,
1688                 dim_crack.Width
1689         );
1690         // Dimension of the scaled crack stage,
1691         // which is the same as the dimension of a single destination frame
1692         core::dimension2d<u32> dim_crack_scaled(
1693                 dim_dst.Width,
1694                 dim_dst.Height / frame_count
1695         );
1696         // Create cropped and scaled crack images
1697         video::IImage *crack_cropped = driver->createImage(
1698                         video::ECF_A8R8G8B8, dim_crack_cropped);
1699         video::IImage *crack_scaled = driver->createImage(
1700                         video::ECF_A8R8G8B8, dim_crack_scaled);
1701
1702         if(crack_cropped && crack_scaled)
1703         {
1704                 // Crop crack image
1705                 v2s32 pos_crack(0, progression*dim_crack.Width);
1706                 crack->copyTo(crack_cropped,
1707                                 v2s32(0,0),
1708                                 core::rect<s32>(pos_crack, dim_crack_cropped));
1709                 // Scale crack image by copying
1710                 crack_cropped->copyToScaling(crack_scaled);
1711                 // Copy or overlay crack image onto each frame
1712                 for(s32 i = 0; i < frame_count; ++i)
1713                 {
1714                         v2s32 dst_pos(0, dim_crack_scaled.Height * i);
1715                         if(use_overlay)
1716                         {
1717                                 blit_with_alpha_overlay(crack_scaled, dst,
1718                                                 v2s32(0,0), dst_pos,
1719                                                 dim_crack_scaled);
1720                         }
1721                         else
1722                         {
1723                                 blit_with_alpha(crack_scaled, dst,
1724                                                 v2s32(0,0), dst_pos,
1725                                                 dim_crack_scaled);
1726                         }
1727                 }
1728         }
1729
1730         if(crack_scaled)
1731                 crack_scaled->drop();
1732
1733         if(crack_cropped)
1734                 crack_cropped->drop();
1735 }
1736
1737 void brighten(video::IImage *image)
1738 {
1739         if(image == NULL)
1740                 return;
1741
1742         core::dimension2d<u32> dim = image->getDimension();
1743
1744         for(u32 y=0; y<dim.Height; y++)
1745         for(u32 x=0; x<dim.Width; x++)
1746         {
1747                 video::SColor c = image->getPixel(x,y);
1748                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1749                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1750                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1751                 image->setPixel(x,y,c);
1752         }
1753 }
1754
1755 u32 parseImageTransform(const std::string& s)
1756 {
1757         int total_transform = 0;
1758
1759         std::string transform_names[8];
1760         transform_names[0] = "i";
1761         transform_names[1] = "r90";
1762         transform_names[2] = "r180";
1763         transform_names[3] = "r270";
1764         transform_names[4] = "fx";
1765         transform_names[6] = "fy";
1766
1767         std::size_t pos = 0;
1768         while(pos < s.size())
1769         {
1770                 int transform = -1;
1771                 for(int i = 0; i <= 7; ++i)
1772                 {
1773                         const std::string &name_i = transform_names[i];
1774
1775                         if(s[pos] == ('0' + i))
1776                         {
1777                                 transform = i;
1778                                 pos++;
1779                                 break;
1780                         }
1781                         else if(!(name_i.empty()) &&
1782                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1783                         {
1784                                 transform = i;
1785                                 pos += name_i.size();
1786                                 break;
1787                         }
1788                 }
1789                 if(transform < 0)
1790                         break;
1791
1792                 // Multiply total_transform and transform in the group D4
1793                 int new_total = 0;
1794                 if(transform < 4)
1795                         new_total = (transform + total_transform) % 4;
1796                 else
1797                         new_total = (transform - total_transform + 8) % 4;
1798                 if((transform >= 4) ^ (total_transform >= 4))
1799                         new_total += 4;
1800
1801                 total_transform = new_total;
1802         }
1803         return total_transform;
1804 }
1805
1806 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1807 {
1808         if(transform % 2 == 0)
1809                 return dim;
1810         else
1811                 return core::dimension2d<u32>(dim.Height, dim.Width);
1812 }
1813
1814 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1815 {
1816         if(src == NULL || dst == NULL)
1817                 return;
1818
1819         core::dimension2d<u32> srcdim = src->getDimension();
1820         core::dimension2d<u32> dstdim = dst->getDimension();
1821
1822         assert(dstdim == imageTransformDimension(transform, srcdim));
1823         assert(transform >= 0 && transform <= 7);
1824
1825         /*
1826                 Compute the transformation from source coordinates (sx,sy)
1827                 to destination coordinates (dx,dy).
1828         */
1829         int sxn = 0;
1830         int syn = 2;
1831         if(transform == 0)         // identity
1832                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1833         else if(transform == 1)    // rotate by 90 degrees ccw
1834                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1835         else if(transform == 2)    // rotate by 180 degrees
1836                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1837         else if(transform == 3)    // rotate by 270 degrees ccw
1838                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1839         else if(transform == 4)    // flip x
1840                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1841         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1842                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1843         else if(transform == 6)    // flip y
1844                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1845         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1846                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1847
1848         for(u32 dy=0; dy<dstdim.Height; dy++)
1849         for(u32 dx=0; dx<dstdim.Width; dx++)
1850         {
1851                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1852                 u32 sx = entries[sxn];
1853                 u32 sy = entries[syn];
1854                 video::SColor c = src->getPixel(sx,sy);
1855                 dst->setPixel(dx,dy,c);
1856         }
1857 }
1858
1859 video::ITexture* TextureSource::getNormalTexture(const std::string &name)
1860 {
1861         u32 id;
1862         if (isKnownSourceImage("override_normal.png"))
1863                 return getTexture("override_normal.png", &id);
1864         std::string fname_base = name;
1865         std::string normal_ext = "_normal.png";
1866         size_t pos = fname_base.find(".");
1867         std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1868         if (isKnownSourceImage(fname_normal)) {
1869                 // look for image extension and replace it
1870                 size_t i = 0;
1871                 while ((i = fname_base.find(".", i)) != std::string::npos) {
1872                         fname_base.replace(i, 4, normal_ext);
1873                         i += normal_ext.length();
1874                 }
1875                 return getTexture(fname_base, &id);
1876                 }
1877         return NULL;
1878 }