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