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