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