Fix my name.
[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_textureinfo_cache_mutex.Init();
437         
438         m_main_thread = get_current_thread_id();
439         
440         // Add a NULL TextureInfo as the first index, named ""
441         m_textureinfo_cache.push_back(TextureInfo(""));
442         m_name_to_id[""] = 0;
443         
444         // Cache some settings
445         // Note: Since this is only done once, the game must be restarted
446         // for these settings to take effect
447         m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
448         m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
449         m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
450 }
451
452 TextureSource::~TextureSource()
453 {
454         video::IVideoDriver* driver = m_device->getVideoDriver();
455
456         unsigned int textures_before = driver->getTextureCount();
457
458         for (std::vector<TextureInfo>::iterator iter =
459                         m_textureinfo_cache.begin();
460                         iter != m_textureinfo_cache.end(); iter++)
461         {
462                 //cleanup texture
463                 if (iter->texture)
464                         driver->removeTexture(iter->texture);
465
466                 //cleanup source image
467                 if (iter->img)
468                         iter->img->drop();
469         }
470         m_textureinfo_cache.clear();
471
472         for (std::list<video::ITexture*>::iterator iter =
473                         m_texture_trash.begin(); iter != m_texture_trash.end();
474                         iter++)
475         {
476                 video::ITexture *t = *iter;
477
478                 //cleanup trashed texture
479                 driver->removeTexture(t);
480         }
481
482         infostream << "~TextureSource() "<< textures_before << "/"
483                         << driver->getTextureCount() << std::endl;
484 }
485
486 u32 TextureSource::getTextureId(const std::string &name)
487 {
488         //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
489
490         {
491                 /*
492                         See if texture already exists
493                 */
494                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
495                 std::map<std::string, u32>::iterator n;
496                 n = m_name_to_id.find(name);
497                 if(n != m_name_to_id.end())
498                 {
499                         return n->second;
500                 }
501         }
502         
503         /*
504                 Get texture
505         */
506         if(get_current_thread_id() == m_main_thread)
507         {
508                 return getTextureIdDirect(name);
509         }
510         else
511         {
512                 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
513
514                 // We're gonna ask the result to be put into here
515                 ResultQueue<std::string, u32, u8, u8> result_queue;
516                 
517                 // Throw a request in
518                 m_get_texture_queue.add(name, 0, 0, &result_queue);
519                 
520                 infostream<<"Waiting for texture from main thread, name=\""
521                                 <<name<<"\""<<std::endl;
522                 
523                 try
524                 {
525                         // Wait result for a second
526                         GetResult<std::string, u32, u8, u8>
527                                         result = result_queue.pop_front(1000);
528                 
529                         // Check that at least something worked OK
530                         assert(result.key == name);
531
532                         return result.item;
533                 }
534                 catch(ItemNotFoundException &e)
535                 {
536                         infostream<<"Waiting for texture timed out."<<std::endl;
537                         return 0;
538                 }
539         }
540         
541         infostream<<"getTextureId(): Failed"<<std::endl;
542
543         return 0;
544 }
545
546 // Draw an image on top of an another one, using the alpha channel of the
547 // source image
548 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
549                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
550
551 // Like blit_with_alpha, but only modifies destination pixels that
552 // are fully opaque
553 static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
554                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
555
556 // Draw or overlay a crack
557 static void draw_crack(video::IImage *crack, video::IImage *dst,
558                 bool use_overlay, s32 frame_count, s32 progression,
559                 video::IVideoDriver *driver);
560
561 // Brighten image
562 void brighten(video::IImage *image);
563 // Parse a transform name
564 u32 parseImageTransform(const std::string& s);
565 // Apply transform to image dimension
566 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
567 // Apply transform to image data
568 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
569
570 /*
571         This method generates all the textures
572 */
573 u32 TextureSource::getTextureIdDirect(const std::string &name)
574 {
575         //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
576
577         // Empty name means texture 0
578         if(name == "")
579         {
580                 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
581                 return 0;
582         }
583         
584         /*
585                 Calling only allowed from main thread
586         */
587         if(get_current_thread_id() != m_main_thread)
588         {
589                 errorstream<<"TextureSource::getTextureIdDirect() "
590                                 "called not from main thread"<<std::endl;
591                 return 0;
592         }
593
594         /*
595                 See if texture already exists
596         */
597         {
598                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
599
600                 std::map<std::string, u32>::iterator n;
601                 n = m_name_to_id.find(name);
602                 if(n != m_name_to_id.end())
603                 {
604                         /*infostream<<"getTextureIdDirect(): \""<<name
605                                         <<"\" found in cache"<<std::endl;*/
606                         return n->second;
607                 }
608         }
609
610         /*infostream<<"getTextureIdDirect(): \""<<name
611                         <<"\" NOT found in cache. Creating it."<<std::endl;*/
612         
613         /*
614                 Get the base image
615         */
616
617         char separator = '^';
618
619         /*
620                 This is set to the id of the base image.
621                 If left 0, there is no base image and a completely new image
622                 is made.
623         */
624         u32 base_image_id = 0;
625         
626         // Find last meta separator in name
627         s32 last_separator_position = -1;
628         for(s32 i=name.size()-1; i>=0; i--)
629         {
630                 if(name[i] == separator)
631                 {
632                         last_separator_position = i;
633                         break;
634                 }
635         }
636         /*
637                 If separator was found, construct the base name and make the
638                 base image using a recursive call
639         */
640         std::string base_image_name;
641         if(last_separator_position != -1)
642         {
643                 // Construct base name
644                 base_image_name = name.substr(0, last_separator_position);
645                 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
646                                 " to get base image of \""<<name<<"\" = \""
647                 <<base_image_name<<"\""<<std::endl;*/
648                 base_image_id = getTextureIdDirect(base_image_name);
649         }
650         
651         //infostream<<"base_image_id="<<base_image_id<<std::endl;
652         
653         video::IVideoDriver* driver = m_device->getVideoDriver();
654         assert(driver);
655
656         video::ITexture *t = NULL;
657
658         /*
659                 An image will be built from files and then converted into a texture.
660         */
661         video::IImage *baseimg = NULL;
662         
663         // If a base image was found, copy it to baseimg
664         if(base_image_id != 0)
665         {
666                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
667
668                 TextureInfo *ti = &m_textureinfo_cache[base_image_id];
669                 
670                 if(ti->img == NULL)
671                 {
672                         infostream<<"getTextureIdDirect(): WARNING: NULL image in "
673                                         <<"cache: \""<<base_image_name<<"\""
674                                         <<std::endl;
675                 }
676                 else
677                 {
678                         core::dimension2d<u32> dim = ti->img->getDimension();
679
680                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
681
682                         ti->img->copyTo(
683                                         baseimg, // target
684                                         v2s32(0,0), // position in target
685                                         core::rect<s32>(v2s32(0,0), dim) // from
686                         );
687
688                         /*infostream<<"getTextureIdDirect(): Loaded \""
689                                         <<base_image_name<<"\" from image cache"
690                                         <<std::endl;*/
691                 }
692         }
693         
694         /*
695                 Parse out the last part of the name of the image and act
696                 according to it
697         */
698
699         std::string last_part_of_name = name.substr(last_separator_position+1);
700         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
701
702         // Generate image according to part of name
703         if(!generateImage(last_part_of_name, baseimg))
704         {
705                 errorstream<<"getTextureIdDirect(): "
706                                 "failed to generate \""<<last_part_of_name<<"\""
707                                 <<std::endl;
708         }
709
710         // If no resulting image, print a warning
711         if(baseimg == NULL)
712         {
713                 errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
714                                 " create texture \""<<name<<"\""<<std::endl;
715         }
716         
717         if(baseimg != NULL)
718         {
719                 // Create texture from resulting image
720                 t = driver->addTexture(name.c_str(), baseimg);
721         }
722         
723         /*
724                 Add texture to caches (add NULL textures too)
725         */
726
727         JMutexAutoLock lock(m_textureinfo_cache_mutex);
728         
729         u32 id = m_textureinfo_cache.size();
730         TextureInfo ti(name, t, baseimg);
731         m_textureinfo_cache.push_back(ti);
732         m_name_to_id[name] = id;
733
734         /*infostream<<"getTextureIdDirect(): "
735                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
736         
737         return id;
738 }
739
740 std::string TextureSource::getTextureName(u32 id)
741 {
742         JMutexAutoLock lock(m_textureinfo_cache_mutex);
743
744         if(id >= m_textureinfo_cache.size())
745         {
746                 errorstream<<"TextureSource::getTextureName(): id="<<id
747                                 <<" >= m_textureinfo_cache.size()="
748                                 <<m_textureinfo_cache.size()<<std::endl;
749                 return "";
750         }
751         
752         return m_textureinfo_cache[id].name;
753 }
754
755 video::ITexture* TextureSource::getTexture(u32 id)
756 {
757         JMutexAutoLock lock(m_textureinfo_cache_mutex);
758
759         if(id >= m_textureinfo_cache.size())
760                 return NULL;
761
762         return m_textureinfo_cache[id].texture;
763 }
764
765 video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
766 {
767         u32 actual_id = getTextureId(name);
768         if(id){
769                 *id = actual_id;
770         }
771         return getTexture(actual_id);
772 }
773
774 void TextureSource::processQueue()
775 {
776         /*
777                 Fetch textures
778         */
779         if(!m_get_texture_queue.empty())
780         {
781                 GetRequest<std::string, u32, u8, u8>
782                                 request = m_get_texture_queue.pop();
783
784                 /*infostream<<"TextureSource::processQueue(): "
785                                 <<"got texture request with "
786                                 <<"name=\""<<request.key<<"\""
787                                 <<std::endl;*/
788
789                 GetResult<std::string, u32, u8, u8>
790                                 result;
791                 result.key = request.key;
792                 result.callers = request.callers;
793                 result.item = getTextureIdDirect(request.key);
794
795                 request.dest->push_back(result);
796         }
797 }
798
799 void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
800 {
801         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
802         
803         assert(get_current_thread_id() == m_main_thread);
804         
805         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
806         m_source_image_existence.set(name, true);
807 }
808
809 void TextureSource::rebuildImagesAndTextures()
810 {
811         JMutexAutoLock lock(m_textureinfo_cache_mutex);
812
813         video::IVideoDriver* driver = m_device->getVideoDriver();
814
815         // Recreate textures
816         for(u32 i=0; i<m_textureinfo_cache.size(); i++){
817                 TextureInfo *ti = &m_textureinfo_cache[i];
818                 video::IImage *img = generateImageFromScratch(ti->name);
819                 // Create texture from resulting image
820                 video::ITexture *t = NULL;
821                 if(img)
822                         t = driver->addTexture(ti->name.c_str(), img);
823                 video::ITexture *t_old = ti->texture;
824                 // Replace texture
825                 ti->texture = t;
826                 ti->img = img;
827
828                 if (t_old != 0)
829                         m_texture_trash.push_back(t_old);
830         }
831 }
832
833 video::ITexture* TextureSource::generateTextureFromMesh(
834                 const TextureFromMeshParams &params)
835 {
836         video::IVideoDriver *driver = m_device->getVideoDriver();
837         assert(driver);
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         driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));
864
865         // Get a scene manager
866         scene::ISceneManager *smgr_main = m_device->getSceneManager();
867         assert(smgr_main);
868         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
869         assert(smgr);
870
871         scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
872         meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
873         meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
874         meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
875         meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
876         meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
877
878         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
879                         params.camera_position, params.camera_lookat);
880         // second parameter of setProjectionMatrix (isOrthogonal) is ignored
881         camera->setProjectionMatrix(params.camera_projection_matrix, false);
882
883         smgr->setAmbientLight(params.ambient_light);
884         smgr->addLightSceneNode(0,
885                         params.light_position,
886                         params.light_color,
887                         params.light_radius);
888
889         // Render scene
890         driver->beginScene(true, true, video::SColor(0,0,0,0));
891         smgr->drawAll();
892         driver->endScene();
893
894         // NOTE: The scene nodes should not be dropped, otherwise
895         //       smgr->drop() segfaults
896         /*cube->drop();
897         camera->drop();
898         light->drop();*/
899         // Drop scene manager
900         smgr->drop();
901
902         // Unset render target
903         driver->setRenderTarget(0, false, true, 0);
904
905         if(params.delete_texture_on_shutdown)
906                 m_texture_trash.push_back(rtt);
907
908         return rtt;
909 }
910
911 video::IImage* TextureSource::generateImageFromScratch(std::string name)
912 {
913         /*infostream<<"generateImageFromScratch(): "
914                         "\""<<name<<"\""<<std::endl;*/
915
916         video::IVideoDriver *driver = m_device->getVideoDriver();
917         assert(driver);
918
919         /*
920                 Get the base image
921         */
922
923         video::IImage *baseimg = NULL;
924
925         char separator = '^';
926
927         // Find last meta separator in name
928         s32 last_separator_position = name.find_last_of(separator);
929
930         /*
931                 If separator was found, construct the base name and make the
932                 base image using a recursive call
933         */
934         std::string base_image_name;
935         if(last_separator_position != -1)
936         {
937                 // Construct base name
938                 base_image_name = name.substr(0, last_separator_position);
939                 baseimg = generateImageFromScratch(base_image_name);
940         }
941         
942         /*
943                 Parse out the last part of the name of the image and act
944                 according to it
945         */
946
947         std::string last_part_of_name = name.substr(last_separator_position+1);
948         
949         // Generate image according to part of name
950         if(!generateImage(last_part_of_name, baseimg))
951         {
952                 errorstream<<"generateImageFromScratch(): "
953                                 "failed to generate \""<<last_part_of_name<<"\""
954                                 <<std::endl;
955                 return NULL;
956         }
957         
958         return baseimg;
959 }
960
961 bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg)
962 {
963         video::IVideoDriver* driver = m_device->getVideoDriver();
964         assert(driver);
965
966         // Stuff starting with [ are special commands
967         if(part_of_name.size() == 0 || part_of_name[0] != '[')
968         {
969                 video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
970
971                 if(image == NULL)
972                 {
973                         if(part_of_name != ""){
974                                 errorstream<<"generateImage(): Could not load image \""
975                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
976                                 errorstream<<"generateImage(): Creating a dummy"
977                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
978                         }
979
980                         // Just create a dummy image
981                         //core::dimension2d<u32> dim(2,2);
982                         core::dimension2d<u32> dim(1,1);
983                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
984                         assert(image);
985                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
986                         image->setPixel(1,0, video::SColor(255,0,255,0));
987                         image->setPixel(0,1, video::SColor(255,0,0,255));
988                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
989                         image->setPixel(0,0, video::SColor(255,myrand()%256,
990                                         myrand()%256,myrand()%256));
991                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
992                                         myrand()%256,myrand()%256));
993                         image->setPixel(0,1, video::SColor(255,myrand()%256,
994                                         myrand()%256,myrand()%256));
995                         image->setPixel(1,1, video::SColor(255,myrand()%256,
996                                         myrand()%256,myrand()%256));*/
997                 }
998
999                 // If base image is NULL, load as base.
1000                 if(baseimg == NULL)
1001                 {
1002                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
1003                         /*
1004                                 Copy it this way to get an alpha channel.
1005                                 Otherwise images with alpha cannot be blitted on 
1006                                 images that don't have alpha in the original file.
1007                         */
1008                         core::dimension2d<u32> dim = image->getDimension();
1009                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1010                         image->copyTo(baseimg);
1011                 }
1012                 // Else blit on base.
1013                 else
1014                 {
1015                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1016                         // Size of the copied area
1017                         core::dimension2d<u32> dim = image->getDimension();
1018                         //core::dimension2d<u32> dim(16,16);
1019                         // Position to copy the blitted to in the base image
1020                         core::position2d<s32> pos_to(0,0);
1021                         // Position to copy the blitted from in the blitted image
1022                         core::position2d<s32> pos_from(0,0);
1023                         // Blit
1024                         /*image->copyToWithAlpha(baseimg, pos_to,
1025                                         core::rect<s32>(pos_from, dim),
1026                                         video::SColor(255,255,255,255),
1027                                         NULL);*/
1028                         blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
1029                 }
1030                 //cleanup
1031                 image->drop();
1032         }
1033         else
1034         {
1035                 // A special texture modification
1036
1037                 /*infostream<<"generateImage(): generating special "
1038                                 <<"modification \""<<part_of_name<<"\""
1039                                 <<std::endl;*/
1040                 
1041                 /*
1042                         [crack:N:P
1043                         [cracko:N:P
1044                         Adds a cracking texture
1045                         N = animation frame count, P = crack progression
1046                 */
1047                 if(part_of_name.substr(0,6) == "[crack")
1048                 {
1049                         if(baseimg == NULL)
1050                         {
1051                                 errorstream<<"generateImage(): baseimg==NULL "
1052                                                 <<"for part_of_name=\""<<part_of_name
1053                                                 <<"\", cancelling."<<std::endl;
1054                                 return false;
1055                         }
1056
1057                         // Crack image number and overlay option
1058                         bool use_overlay = (part_of_name[6] == 'o');
1059                         Strfnd sf(part_of_name);
1060                         sf.next(":");
1061                         s32 frame_count = stoi(sf.next(":"));
1062                         s32 progression = stoi(sf.next(":"));
1063
1064                         /*
1065                                 Load crack image.
1066
1067                                 It is an image with a number of cracking stages
1068                                 horizontally tiled.
1069                         */
1070                         video::IImage *img_crack = m_sourcecache.getOrLoad(
1071                                         "crack_anylength.png", m_device);
1072
1073                         if(img_crack && progression >= 0)
1074                         {
1075                                 draw_crack(img_crack, baseimg,
1076                                                 use_overlay, frame_count,
1077                                                 progression, driver);
1078                                 img_crack->drop();
1079                         }
1080                 }
1081                 /*
1082                         [combine:WxH:X,Y=filename:X,Y=filename2
1083                         Creates a bigger texture from an amount of smaller ones
1084                 */
1085                 else if(part_of_name.substr(0,8) == "[combine")
1086                 {
1087                         Strfnd sf(part_of_name);
1088                         sf.next(":");
1089                         u32 w0 = stoi(sf.next("x"));
1090                         u32 h0 = stoi(sf.next(":"));
1091                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1092                         core::dimension2d<u32> dim(w0,h0);
1093                         if(baseimg == NULL)
1094                         {
1095                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1096                                 baseimg->fill(video::SColor(0,0,0,0));
1097                         }
1098                         while(sf.atend() == false)
1099                         {
1100                                 u32 x = stoi(sf.next(","));
1101                                 u32 y = stoi(sf.next("="));
1102                                 std::string filename = sf.next(":");
1103                                 infostream<<"Adding \""<<filename
1104                                                 <<"\" to combined ("<<x<<","<<y<<")"
1105                                                 <<std::endl;
1106                                 video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1107                                 if(img)
1108                                 {
1109                                         core::dimension2d<u32> dim = img->getDimension();
1110                                         infostream<<"Size "<<dim.Width
1111                                                         <<"x"<<dim.Height<<std::endl;
1112                                         core::position2d<s32> pos_base(x, y);
1113                                         video::IImage *img2 =
1114                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1115                                         img->copyTo(img2);
1116                                         img->drop();
1117                                         /*img2->copyToWithAlpha(baseimg, pos_base,
1118                                                         core::rect<s32>(v2s32(0,0), dim),
1119                                                         video::SColor(255,255,255,255),
1120                                                         NULL);*/
1121                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
1122                                         img2->drop();
1123                                 }
1124                                 else
1125                                 {
1126                                         infostream<<"img==NULL"<<std::endl;
1127                                 }
1128                         }
1129                 }
1130                 /*
1131                         "[brighten"
1132                 */
1133                 else if(part_of_name.substr(0,9) == "[brighten")
1134                 {
1135                         if(baseimg == NULL)
1136                         {
1137                                 errorstream<<"generateImage(): baseimg==NULL "
1138                                                 <<"for part_of_name=\""<<part_of_name
1139                                                 <<"\", cancelling."<<std::endl;
1140                                 return false;
1141                         }
1142
1143                         brighten(baseimg);
1144                 }
1145                 /*
1146                         "[noalpha"
1147                         Make image completely opaque.
1148                         Used for the leaves texture when in old leaves mode, so
1149                         that the transparent parts don't look completely black 
1150                         when simple alpha channel is used for rendering.
1151                 */
1152                 else if(part_of_name.substr(0,8) == "[noalpha")
1153                 {
1154                         if(baseimg == NULL)
1155                         {
1156                                 errorstream<<"generateImage(): baseimg==NULL "
1157                                                 <<"for part_of_name=\""<<part_of_name
1158                                                 <<"\", cancelling."<<std::endl;
1159                                 return false;
1160                         }
1161
1162                         core::dimension2d<u32> dim = baseimg->getDimension();
1163                         
1164                         // Set alpha to full
1165                         for(u32 y=0; y<dim.Height; y++)
1166                         for(u32 x=0; x<dim.Width; x++)
1167                         {
1168                                 video::SColor c = baseimg->getPixel(x,y);
1169                                 c.setAlpha(255);
1170                                 baseimg->setPixel(x,y,c);
1171                         }
1172                 }
1173                 /*
1174                         "[makealpha:R,G,B"
1175                         Convert one color to transparent.
1176                 */
1177                 else if(part_of_name.substr(0,11) == "[makealpha:")
1178                 {
1179                         if(baseimg == NULL)
1180                         {
1181                                 errorstream<<"generateImage(): baseimg==NULL "
1182                                                 <<"for part_of_name=\""<<part_of_name
1183                                                 <<"\", cancelling."<<std::endl;
1184                                 return false;
1185                         }
1186
1187                         Strfnd sf(part_of_name.substr(11));
1188                         u32 r1 = stoi(sf.next(","));
1189                         u32 g1 = stoi(sf.next(","));
1190                         u32 b1 = stoi(sf.next(""));
1191                         std::string filename = sf.next("");
1192
1193                         core::dimension2d<u32> dim = baseimg->getDimension();
1194                         
1195                         /*video::IImage *oldbaseimg = baseimg;
1196                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1197                         oldbaseimg->copyTo(baseimg);
1198                         oldbaseimg->drop();*/
1199
1200                         // Set alpha to full
1201                         for(u32 y=0; y<dim.Height; y++)
1202                         for(u32 x=0; x<dim.Width; x++)
1203                         {
1204                                 video::SColor c = baseimg->getPixel(x,y);
1205                                 u32 r = c.getRed();
1206                                 u32 g = c.getGreen();
1207                                 u32 b = c.getBlue();
1208                                 if(!(r == r1 && g == g1 && b == b1))
1209                                         continue;
1210                                 c.setAlpha(0);
1211                                 baseimg->setPixel(x,y,c);
1212                         }
1213                 }
1214                 /*
1215                         "[transformN"
1216                         Rotates and/or flips the image.
1217
1218                         N can be a number (between 0 and 7) or a transform name.
1219                         Rotations are counter-clockwise.
1220                         0  I      identity
1221                         1  R90    rotate by 90 degrees
1222                         2  R180   rotate by 180 degrees
1223                         3  R270   rotate by 270 degrees
1224                         4  FX     flip X
1225                         5  FXR90  flip X then rotate by 90 degrees
1226                         6  FY     flip Y
1227                         7  FYR90  flip Y then rotate by 90 degrees
1228
1229                         Note: Transform names can be concatenated to produce
1230                         their product (applies the first then the second).
1231                         The resulting transform will be equivalent to one of the
1232                         eight existing ones, though (see: dihedral group).
1233                 */
1234                 else if(part_of_name.substr(0,10) == "[transform")
1235                 {
1236                         if(baseimg == NULL)
1237                         {
1238                                 errorstream<<"generateImage(): baseimg==NULL "
1239                                                 <<"for part_of_name=\""<<part_of_name
1240                                                 <<"\", cancelling."<<std::endl;
1241                                 return false;
1242                         }
1243
1244                         u32 transform = parseImageTransform(part_of_name.substr(10));
1245                         core::dimension2d<u32> dim = imageTransformDimension(
1246                                         transform, baseimg->getDimension());
1247                         video::IImage *image = driver->createImage(
1248                                         baseimg->getColorFormat(), dim);
1249                         assert(image);
1250                         imageTransform(transform, baseimg, image);
1251                         baseimg->drop();
1252                         baseimg = image;
1253                 }
1254                 /*
1255                         [inventorycube{topimage{leftimage{rightimage
1256                         In every subimage, replace ^ with &.
1257                         Create an "inventory cube".
1258                         NOTE: This should be used only on its own.
1259                         Example (a grass block (not actually used in game):
1260                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1261                 */
1262                 else if(part_of_name.substr(0,14) == "[inventorycube")
1263                 {
1264                         if(baseimg != NULL)
1265                         {
1266                                 errorstream<<"generateImage(): baseimg!=NULL "
1267                                                 <<"for part_of_name=\""<<part_of_name
1268                                                 <<"\", cancelling."<<std::endl;
1269                                 return false;
1270                         }
1271
1272                         str_replace_char(part_of_name, '&', '^');
1273                         Strfnd sf(part_of_name);
1274                         sf.next("{");
1275                         std::string imagename_top = sf.next("{");
1276                         std::string imagename_left = sf.next("{");
1277                         std::string imagename_right = sf.next("{");
1278
1279                         // Generate images for the faces of the cube
1280                         video::IImage *img_top =
1281                                 generateImageFromScratch(imagename_top);
1282                         video::IImage *img_left =
1283                                 generateImageFromScratch(imagename_left);
1284                         video::IImage *img_right =
1285                                 generateImageFromScratch(imagename_right);
1286                         assert(img_top && img_left && img_right);
1287
1288                         // Create textures from images
1289                         video::ITexture *texture_top = driver->addTexture(
1290                                         (imagename_top + "__temp__").c_str(), img_top);
1291                         video::ITexture *texture_left = driver->addTexture(
1292                                         (imagename_left + "__temp__").c_str(), img_left);
1293                         video::ITexture *texture_right = driver->addTexture(
1294                                         (imagename_right + "__temp__").c_str(), img_right);
1295                         assert(texture_top && texture_left && texture_right);
1296
1297                         // Drop images
1298                         img_top->drop();
1299                         img_left->drop();
1300                         img_right->drop();
1301                         
1302                         /*
1303                                 Draw a cube mesh into a render target texture
1304                         */
1305                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1306                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1307                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1308                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1309                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1310                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1311                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1312                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1313
1314                         TextureFromMeshParams params;
1315                         params.mesh = cube;
1316                         params.dim.set(64, 64);
1317                         params.rtt_texture_name = part_of_name + "_RTT";
1318                         // We will delete the rtt texture ourselves
1319                         params.delete_texture_on_shutdown = false;
1320                         params.camera_position.set(0, 1.0, -1.5);
1321                         params.camera_position.rotateXZBy(45);
1322                         params.camera_lookat.set(0, 0, 0);
1323                         // Set orthogonal projection
1324                         params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
1325                                         1.65, 1.65, 0, 100);
1326
1327                         params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
1328                         params.light_position.set(10, 100, -50);
1329                         params.light_color.set(1.0, 0.5, 0.5, 0.5);
1330                         params.light_radius = 1000;
1331                         
1332                         video::ITexture *rtt = generateTextureFromMesh(params);
1333                         
1334                         // Drop mesh
1335                         cube->drop();
1336
1337                         // Free textures of images
1338                         driver->removeTexture(texture_top);
1339                         driver->removeTexture(texture_left);
1340                         driver->removeTexture(texture_right);
1341                         
1342                         if(rtt == NULL)
1343                         {
1344                                 baseimg = generateImageFromScratch(imagename_top);
1345                                 return true;
1346                         }
1347
1348                         // Create image of render target
1349                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim);
1350                         assert(image);
1351
1352                         // Cleanup texture
1353                         driver->removeTexture(rtt);
1354
1355                         baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
1356
1357                         if(image)
1358                         {
1359                                 image->copyTo(baseimg);
1360                                 image->drop();
1361                         }
1362                 }
1363                 /*
1364                         [lowpart:percent:filename
1365                         Adds the lower part of a texture
1366                 */
1367                 else if(part_of_name.substr(0,9) == "[lowpart:")
1368                 {
1369                         Strfnd sf(part_of_name);
1370                         sf.next(":");
1371                         u32 percent = stoi(sf.next(":"));
1372                         std::string filename = sf.next(":");
1373                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1374
1375                         if(baseimg == NULL)
1376                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1377                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1378                         if(img)
1379                         {
1380                                 core::dimension2d<u32> dim = img->getDimension();
1381                                 core::position2d<s32> pos_base(0, 0);
1382                                 video::IImage *img2 =
1383                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1384                                 img->copyTo(img2);
1385                                 img->drop();
1386                                 core::position2d<s32> clippos(0, 0);
1387                                 clippos.Y = dim.Height * (100-percent) / 100;
1388                                 core::dimension2d<u32> clipdim = dim;
1389                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1390                                 core::rect<s32> cliprect(clippos, clipdim);
1391                                 img2->copyToWithAlpha(baseimg, pos_base,
1392                                                 core::rect<s32>(v2s32(0,0), dim),
1393                                                 video::SColor(255,255,255,255),
1394                                                 &cliprect);
1395                                 img2->drop();
1396                         }
1397                 }
1398                 /*
1399                         [verticalframe:N:I
1400                         Crops a frame of a vertical animation.
1401                         N = frame count, I = frame index
1402                 */
1403                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1404                 {
1405                         Strfnd sf(part_of_name);
1406                         sf.next(":");
1407                         u32 frame_count = stoi(sf.next(":"));
1408                         u32 frame_index = stoi(sf.next(":"));
1409
1410                         if(baseimg == NULL){
1411                                 errorstream<<"generateImage(): baseimg!=NULL "
1412                                                 <<"for part_of_name=\""<<part_of_name
1413                                                 <<"\", cancelling."<<std::endl;
1414                                 return false;
1415                         }
1416                         
1417                         v2u32 frame_size = baseimg->getDimension();
1418                         frame_size.Y /= frame_count;
1419
1420                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1421                                         frame_size);
1422                         if(!img){
1423                                 errorstream<<"generateImage(): Could not create image "
1424                                                 <<"for part_of_name=\""<<part_of_name
1425                                                 <<"\", cancelling."<<std::endl;
1426                                 return false;
1427                         }
1428
1429                         // Fill target image with transparency
1430                         img->fill(video::SColor(0,0,0,0));
1431
1432                         core::dimension2d<u32> dim = frame_size;
1433                         core::position2d<s32> pos_dst(0, 0);
1434                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1435                         baseimg->copyToWithAlpha(img, pos_dst,
1436                                         core::rect<s32>(pos_src, dim),
1437                                         video::SColor(255,255,255,255),
1438                                         NULL);
1439                         // Replace baseimg
1440                         baseimg->drop();
1441                         baseimg = img;
1442                 }
1443                 else
1444                 {
1445                         errorstream<<"generateImage(): Invalid "
1446                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1447                 }
1448         }
1449
1450         return true;
1451 }
1452
1453 /*
1454         Draw an image on top of an another one, using the alpha channel of the
1455         source image
1456
1457         This exists because IImage::copyToWithAlpha() doesn't seem to always
1458         work.
1459 */
1460 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1461                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1462 {
1463         for(u32 y0=0; y0<size.Y; y0++)
1464         for(u32 x0=0; x0<size.X; x0++)
1465         {
1466                 s32 src_x = src_pos.X + x0;
1467                 s32 src_y = src_pos.Y + y0;
1468                 s32 dst_x = dst_pos.X + x0;
1469                 s32 dst_y = dst_pos.Y + y0;
1470                 video::SColor src_c = src->getPixel(src_x, src_y);
1471                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1472                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1473                 dst->setPixel(dst_x, dst_y, dst_c);
1474         }
1475 }
1476
1477 /*
1478         Draw an image on top of an another one, using the alpha channel of the
1479         source image; only modify fully opaque pixels in destinaion
1480 */
1481 static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
1482                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1483 {
1484         for(u32 y0=0; y0<size.Y; y0++)
1485         for(u32 x0=0; x0<size.X; x0++)
1486         {
1487                 s32 src_x = src_pos.X + x0;
1488                 s32 src_y = src_pos.Y + y0;
1489                 s32 dst_x = dst_pos.X + x0;
1490                 s32 dst_y = dst_pos.Y + y0;
1491                 video::SColor src_c = src->getPixel(src_x, src_y);
1492                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1493                 if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
1494                 {
1495                         dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1496                         dst->setPixel(dst_x, dst_y, dst_c);
1497                 }
1498         }
1499 }
1500
1501 static void draw_crack(video::IImage *crack, video::IImage *dst,
1502                 bool use_overlay, s32 frame_count, s32 progression,
1503                 video::IVideoDriver *driver)
1504 {
1505         // Dimension of destination image
1506         core::dimension2d<u32> dim_dst = dst->getDimension();
1507         // Dimension of original image
1508         core::dimension2d<u32> dim_crack = crack->getDimension();
1509         // Count of crack stages
1510         s32 crack_count = dim_crack.Height / dim_crack.Width;
1511         // Limit frame_count
1512         if(frame_count > (s32) dim_dst.Height)
1513                 frame_count = dim_dst.Height;
1514         if(frame_count < 1)
1515                 frame_count = 1;
1516         // Limit progression
1517         if(progression > crack_count-1)
1518                 progression = crack_count-1;
1519         // Dimension of a single crack stage
1520         core::dimension2d<u32> dim_crack_cropped(
1521                 dim_crack.Width,
1522                 dim_crack.Width
1523         );
1524         // Dimension of the scaled crack stage,
1525         // which is the same as the dimension of a single destination frame
1526         core::dimension2d<u32> dim_crack_scaled(
1527                 dim_dst.Width,
1528                 dim_dst.Height / frame_count
1529         );
1530         // Create cropped and scaled crack images
1531         video::IImage *crack_cropped = driver->createImage(
1532                         video::ECF_A8R8G8B8, dim_crack_cropped);
1533         video::IImage *crack_scaled = driver->createImage(
1534                         video::ECF_A8R8G8B8, dim_crack_scaled);
1535
1536         if(crack_cropped && crack_scaled)
1537         {
1538                 // Crop crack image
1539                 v2s32 pos_crack(0, progression*dim_crack.Width);
1540                 crack->copyTo(crack_cropped,
1541                                 v2s32(0,0),
1542                                 core::rect<s32>(pos_crack, dim_crack_cropped));
1543                 // Scale crack image by copying
1544                 crack_cropped->copyToScaling(crack_scaled);
1545                 // Copy or overlay crack image onto each frame
1546                 for(s32 i = 0; i < frame_count; ++i)
1547                 {
1548                         v2s32 dst_pos(0, dim_crack_scaled.Height * i);
1549                         if(use_overlay)
1550                         {
1551                                 blit_with_alpha_overlay(crack_scaled, dst,
1552                                                 v2s32(0,0), dst_pos,
1553                                                 dim_crack_scaled);
1554                         }
1555                         else
1556                         {
1557                                 blit_with_alpha(crack_scaled, dst,
1558                                                 v2s32(0,0), dst_pos,
1559                                                 dim_crack_scaled);
1560                         }
1561                 }
1562         }
1563
1564         if(crack_scaled)
1565                 crack_scaled->drop();
1566
1567         if(crack_cropped)
1568                 crack_cropped->drop();
1569 }
1570
1571 void brighten(video::IImage *image)
1572 {
1573         if(image == NULL)
1574                 return;
1575         
1576         core::dimension2d<u32> dim = image->getDimension();
1577
1578         for(u32 y=0; y<dim.Height; y++)
1579         for(u32 x=0; x<dim.Width; x++)
1580         {
1581                 video::SColor c = image->getPixel(x,y);
1582                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1583                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1584                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1585                 image->setPixel(x,y,c);
1586         }
1587 }
1588
1589 u32 parseImageTransform(const std::string& s)
1590 {
1591         int total_transform = 0;
1592
1593         std::string transform_names[8];
1594         transform_names[0] = "i";
1595         transform_names[1] = "r90";
1596         transform_names[2] = "r180";
1597         transform_names[3] = "r270";
1598         transform_names[4] = "fx";
1599         transform_names[6] = "fy";
1600
1601         std::size_t pos = 0;
1602         while(pos < s.size())
1603         {
1604                 int transform = -1;
1605                 for(int i = 0; i <= 7; ++i)
1606                 {
1607                         const std::string &name_i = transform_names[i];
1608
1609                         if(s[pos] == ('0' + i))
1610                         {
1611                                 transform = i;
1612                                 pos++;
1613                                 break;
1614                         }
1615                         else if(!(name_i.empty()) &&
1616                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1617                         {
1618                                 transform = i;
1619                                 pos += name_i.size();
1620                                 break;
1621                         }
1622                 }
1623                 if(transform < 0)
1624                         break;
1625
1626                 // Multiply total_transform and transform in the group D4
1627                 int new_total = 0;
1628                 if(transform < 4)
1629                         new_total = (transform + total_transform) % 4;
1630                 else
1631                         new_total = (transform - total_transform + 8) % 4;
1632                 if((transform >= 4) ^ (total_transform >= 4))
1633                         new_total += 4;
1634
1635                 total_transform = new_total;
1636         }
1637         return total_transform;
1638 }
1639
1640 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1641 {
1642         if(transform % 2 == 0)
1643                 return dim;
1644         else
1645                 return core::dimension2d<u32>(dim.Height, dim.Width);
1646 }
1647
1648 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1649 {
1650         if(src == NULL || dst == NULL)
1651                 return;
1652         
1653         core::dimension2d<u32> srcdim = src->getDimension();
1654         core::dimension2d<u32> dstdim = dst->getDimension();
1655
1656         assert(dstdim == imageTransformDimension(transform, srcdim));
1657         assert(transform >= 0 && transform <= 7);
1658
1659         /*
1660                 Compute the transformation from source coordinates (sx,sy)
1661                 to destination coordinates (dx,dy).
1662         */
1663         int sxn = 0;
1664         int syn = 2;
1665         if(transform == 0)         // identity
1666                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1667         else if(transform == 1)    // rotate by 90 degrees ccw
1668                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1669         else if(transform == 2)    // rotate by 180 degrees
1670                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1671         else if(transform == 3)    // rotate by 270 degrees ccw
1672                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1673         else if(transform == 4)    // flip x
1674                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1675         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1676                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1677         else if(transform == 6)    // flip y
1678                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1679         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1680                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1681
1682         for(u32 dy=0; dy<dstdim.Height; dy++)
1683         for(u32 dx=0; dx<dstdim.Width; dx++)
1684         {
1685                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1686                 u32 sx = entries[sxn];
1687                 u32 sy = entries[syn];
1688                 video::SColor c = src->getPixel(sx,sy);
1689                 dst->setPixel(dx,dy,c);
1690         }
1691 }