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