]> git.lizzy.rs Git - minetest.git/blob - src/tile.cpp
Delay deleting replaced textures (m_texture_trash)
[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 "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 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         // Textures that have been overwritten with other ones
445         // but can't be deleted because the ITexture* might still be used
446         std::list<video::ITexture*> m_texture_trash;
447 };
448
449 IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
450 {
451         return new TextureSource(device);
452 }
453
454 TextureSource::TextureSource(IrrlichtDevice *device):
455                 m_device(device),
456                 m_main_atlas_image(NULL),
457                 m_main_atlas_texture(NULL)
458 {
459         assert(m_device);
460         
461         m_atlaspointer_cache_mutex.Init();
462         
463         m_main_thread = get_current_thread_id();
464         
465         // Add a NULL AtlasPointer as the first index, named ""
466         m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
467         m_name_to_id[""] = 0;
468 }
469
470 TextureSource::~TextureSource()
471 {
472         video::IVideoDriver* driver = m_device->getVideoDriver();
473
474         unsigned int textures_before = driver->getTextureCount();
475
476         for (std::vector<SourceAtlasPointer>::iterator iter =
477                         m_atlaspointer_cache.begin();  iter != m_atlaspointer_cache.end();
478                         iter++)
479         {
480                 video::ITexture *t = driver->getTexture(iter->name.c_str());
481
482                 //cleanup texture
483                 if (t)
484                         driver->removeTexture(t);
485
486                 //cleanup source image
487                 if (iter->atlas_img)
488                         iter->atlas_img->drop();
489         }
490         m_atlaspointer_cache.clear();
491
492         for (std::list<video::ITexture*>::iterator iter =
493                         m_texture_trash.begin(); iter != m_texture_trash.end();
494                         iter++)
495         {
496                 video::ITexture *t = *iter;
497
498                 //cleanup trashed texture
499                 driver->removeTexture(t);
500         }
501
502         infostream << "~TextureSource() "<< textures_before << "/"
503                         << driver->getTextureCount() << std::endl;
504 }
505
506 u32 TextureSource::getTextureId(const std::string &name)
507 {
508         //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
509
510         {
511                 /*
512                         See if texture already exists
513                 */
514                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
515                 std::map<std::string, u32>::iterator n;
516                 n = m_name_to_id.find(name);
517                 if(n != m_name_to_id.end())
518                 {
519                         return n->second;
520                 }
521         }
522         
523         /*
524                 Get texture
525         */
526         if(get_current_thread_id() == m_main_thread)
527         {
528                 return getTextureIdDirect(name);
529         }
530         else
531         {
532                 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
533
534                 // We're gonna ask the result to be put into here
535                 ResultQueue<std::string, u32, u8, u8> result_queue;
536                 
537                 // Throw a request in
538                 m_get_texture_queue.add(name, 0, 0, &result_queue);
539                 
540                 infostream<<"Waiting for texture from main thread, name=\""
541                                 <<name<<"\""<<std::endl;
542                 
543                 try
544                 {
545                         // Wait result for a second
546                         GetResult<std::string, u32, u8, u8>
547                                         result = result_queue.pop_front(1000);
548                 
549                         // Check that at least something worked OK
550                         assert(result.key == name);
551
552                         return result.item;
553                 }
554                 catch(ItemNotFoundException &e)
555                 {
556                         infostream<<"Waiting for texture timed out."<<std::endl;
557                         return 0;
558                 }
559         }
560         
561         infostream<<"getTextureId(): Failed"<<std::endl;
562
563         return 0;
564 }
565
566 // Overlay image on top of another image (used for cracks)
567 void overlay(video::IImage *image, video::IImage *overlay);
568
569 // Draw an image on top of an another one, using the alpha channel of the
570 // source image
571 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
572                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
573
574 // Brighten image
575 void brighten(video::IImage *image);
576 // Parse a transform name
577 u32 parseImageTransform(const std::string& s);
578 // Apply transform to image dimension
579 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
580 // Apply transform to image data
581 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
582
583 /*
584         Generate image based on a string like "stone.png" or "[crack0".
585         if baseimg is NULL, it is created. Otherwise stuff is made on it.
586 */
587 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
588                 IrrlichtDevice *device, SourceImageCache *sourcecache);
589
590 /*
591         Generates an image from a full string like
592         "stone.png^mineral_coal.png^[crack0".
593
594         This is used by buildMainAtlas().
595 */
596 video::IImage* generate_image_from_scratch(std::string name,
597                 IrrlichtDevice *device, SourceImageCache *sourcecache);
598
599 /*
600         This method generates all the textures
601 */
602 u32 TextureSource::getTextureIdDirect(const std::string &name)
603 {
604         //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
605
606         // Empty name means texture 0
607         if(name == "")
608         {
609                 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
610                 return 0;
611         }
612         
613         /*
614                 Calling only allowed from main thread
615         */
616         if(get_current_thread_id() != m_main_thread)
617         {
618                 errorstream<<"TextureSource::getTextureIdDirect() "
619                                 "called not from main thread"<<std::endl;
620                 return 0;
621         }
622
623         /*
624                 See if texture already exists
625         */
626         {
627                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
628
629                 std::map<std::string, u32>::iterator n;
630                 n = m_name_to_id.find(name);
631                 if(n != m_name_to_id.end())
632                 {
633                         /*infostream<<"getTextureIdDirect(): \""<<name
634                                         <<"\" found in cache"<<std::endl;*/
635                         return n->second;
636                 }
637         }
638
639         /*infostream<<"getTextureIdDirect(): \""<<name
640                         <<"\" NOT found in cache. Creating it."<<std::endl;*/
641         
642         /*
643                 Get the base image
644         */
645
646         char separator = '^';
647
648         /*
649                 This is set to the id of the base image.
650                 If left 0, there is no base image and a completely new image
651                 is made.
652         */
653         u32 base_image_id = 0;
654         
655         // Find last meta separator in name
656         s32 last_separator_position = -1;
657         for(s32 i=name.size()-1; i>=0; i--)
658         {
659                 if(name[i] == separator)
660                 {
661                         last_separator_position = i;
662                         break;
663                 }
664         }
665         /*
666                 If separator was found, construct the base name and make the
667                 base image using a recursive call
668         */
669         std::string base_image_name;
670         if(last_separator_position != -1)
671         {
672                 // Construct base name
673                 base_image_name = name.substr(0, last_separator_position);
674                 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
675                                 " to get base image of \""<<name<<"\" = \""
676                 <<base_image_name<<"\""<<std::endl;*/
677                 base_image_id = getTextureIdDirect(base_image_name);
678         }
679         
680         //infostream<<"base_image_id="<<base_image_id<<std::endl;
681         
682         video::IVideoDriver* driver = m_device->getVideoDriver();
683         assert(driver);
684
685         video::ITexture *t = NULL;
686
687         /*
688                 An image will be built from files and then converted into a texture.
689         */
690         video::IImage *baseimg = NULL;
691         
692         // If a base image was found, copy it to baseimg
693         if(base_image_id != 0)
694         {
695                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
696
697                 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
698
699                 video::IImage *image = ap.atlas_img;
700                 
701                 if(image == NULL)
702                 {
703                         infostream<<"getTextureIdDirect(): WARNING: NULL image in "
704                                         <<"cache: \""<<base_image_name<<"\""
705                                         <<std::endl;
706                 }
707                 else
708                 {
709                         core::dimension2d<u32> dim = ap.intsize;
710
711                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
712
713                         core::position2d<s32> pos_to(0,0);
714                         core::position2d<s32> pos_from = ap.intpos;
715                         
716                         image->copyTo(
717                                         baseimg, // target
718                                         v2s32(0,0), // position in target
719                                         core::rect<s32>(pos_from, dim) // from
720                         );
721
722                         /*infostream<<"getTextureIdDirect(): Loaded \""
723                                         <<base_image_name<<"\" from image cache"
724                                         <<std::endl;*/
725                 }
726         }
727         
728         /*
729                 Parse out the last part of the name of the image and act
730                 according to it
731         */
732
733         std::string last_part_of_name = name.substr(last_separator_position+1);
734         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
735
736         // Generate image according to part of name
737         if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
738         {
739                 errorstream<<"getTextureIdDirect(): "
740                                 "failed to generate \""<<last_part_of_name<<"\""
741                                 <<std::endl;
742         }
743
744         // If no resulting image, print a warning
745         if(baseimg == NULL)
746         {
747                 errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
748                                 " create texture \""<<name<<"\""<<std::endl;
749         }
750         
751         if(baseimg != NULL)
752         {
753                 // Create texture from resulting image
754                 t = driver->addTexture(name.c_str(), baseimg);
755         }
756         
757         /*
758                 Add texture to caches (add NULL textures too)
759         */
760
761         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
762         
763         u32 id = m_atlaspointer_cache.size();
764         AtlasPointer ap(id);
765         ap.atlas = t;
766         ap.pos = v2f(0,0);
767         ap.size = v2f(1,1);
768         ap.tiled = 0;
769         core::dimension2d<u32> baseimg_dim(0,0);
770         if(baseimg)
771                 baseimg_dim = baseimg->getDimension();
772         SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
773         m_atlaspointer_cache.push_back(nap);
774         m_name_to_id[name] = id;
775
776         /*infostream<<"getTextureIdDirect(): "
777                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
778         
779         return id;
780 }
781
782 std::string TextureSource::getTextureName(u32 id)
783 {
784         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
785
786         if(id >= m_atlaspointer_cache.size())
787         {
788                 errorstream<<"TextureSource::getTextureName(): id="<<id
789                                 <<" >= m_atlaspointer_cache.size()="
790                                 <<m_atlaspointer_cache.size()<<std::endl;
791                 return "";
792         }
793         
794         return m_atlaspointer_cache[id].name;
795 }
796
797
798 AtlasPointer TextureSource::getTexture(u32 id)
799 {
800         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
801
802         if(id >= m_atlaspointer_cache.size())
803                 return AtlasPointer(0, NULL);
804         
805         return m_atlaspointer_cache[id].a;
806 }
807
808 void TextureSource::updateAP(AtlasPointer &ap)
809 {
810         AtlasPointer ap2 = getTexture(ap.id);
811         ap = ap2;
812 }
813
814 void TextureSource::processQueue()
815 {
816         /*
817                 Fetch textures
818         */
819         if(!m_get_texture_queue.empty())
820         {
821                 GetRequest<std::string, u32, u8, u8>
822                                 request = m_get_texture_queue.pop();
823
824                 /*infostream<<"TextureSource::processQueue(): "
825                                 <<"got texture request with "
826                                 <<"name=\""<<request.key<<"\""
827                                 <<std::endl;*/
828
829                 GetResult<std::string, u32, u8, u8>
830                                 result;
831                 result.key = request.key;
832                 result.callers = request.callers;
833                 result.item = getTextureIdDirect(request.key);
834
835                 request.dest->push_back(result);
836         }
837 }
838
839 void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
840 {
841         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
842         
843         assert(get_current_thread_id() == m_main_thread);
844         
845         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
846         m_source_image_existence.set(name, true);
847 }
848         
849 void TextureSource::rebuildImagesAndTextures()
850 {
851         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
852
853         /*// Oh well... just clear everything, they'll load sometime.
854         m_atlaspointer_cache.clear();
855         m_name_to_id.clear();*/
856
857         video::IVideoDriver* driver = m_device->getVideoDriver();
858         
859         // Remove source images from textures to disable inheriting textures
860         // from existing textures
861         /*for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
862                 SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
863                 sap->atlas_img->drop();
864                 sap->atlas_img = NULL;
865         }*/
866         
867         // Recreate textures
868         for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
869                 SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
870                 video::IImage *img =
871                         generate_image_from_scratch(sap->name, m_device, &m_sourcecache);
872                 // Create texture from resulting image
873                 video::ITexture *t = NULL;
874                 if(img)
875                         t = driver->addTexture(sap->name.c_str(), img);
876                 video::ITexture *t_old = sap->a.atlas;
877                 // Replace texture
878                 sap->a.atlas = t;
879                 sap->a.pos = v2f(0,0);
880                 sap->a.size = v2f(1,1);
881                 sap->a.tiled = 0;
882                 sap->atlas_img = img;
883                 sap->intpos = v2s32(0,0);
884                 sap->intsize = img->getDimension();
885
886                 if (t_old != 0)
887                         m_texture_trash.push_back(t_old);
888         }
889 }
890
891 void TextureSource::buildMainAtlas(class IGameDef *gamedef) 
892 {
893         assert(gamedef->tsrc() == this);
894         INodeDefManager *ndef = gamedef->ndef();
895
896         infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
897
898         //return; // Disable (for testing)
899         
900         video::IVideoDriver* driver = m_device->getVideoDriver();
901         assert(driver);
902
903         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
904
905         // Create an image of the right size
906         core::dimension2d<u32> max_dim = driver->getMaxTextureSize();
907         core::dimension2d<u32> atlas_dim(2048,2048);
908         atlas_dim.Width  = MYMIN(atlas_dim.Width,  max_dim.Width);
909         atlas_dim.Height = MYMIN(atlas_dim.Height, max_dim.Height);
910         video::IImage *atlas_img =
911                         driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
912         //assert(atlas_img);
913         if(atlas_img == NULL)
914         {
915                 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
916                                 "image; not building texture atlas."<<std::endl;
917                 return;
918         }
919
920         /*
921                 Grab list of stuff to include in the texture atlas from the
922                 main content features
923         */
924
925         std::set<std::string> sourcelist;
926
927         for(u16 j=0; j<MAX_CONTENT+1; j++)
928         {
929                 if(j == CONTENT_IGNORE || j == CONTENT_AIR)
930                         continue;
931                 const ContentFeatures &f = ndef->get(j);
932                 for(u32 i=0; i<6; i++)
933                 {
934                         std::string name = f.tiledef[i].name;
935                         sourcelist.insert(name);
936                 }
937         }
938         
939         infostream<<"Creating texture atlas out of textures: ";
940         for(std::set<std::string>::iterator
941                         i = sourcelist.begin();
942                         i != sourcelist.end(); ++i)
943         {
944                 std::string name = *i;
945                 infostream<<"\""<<name<<"\" ";
946         }
947         infostream<<std::endl;
948
949         // Padding to disallow texture bleeding
950         // (16 needed if mipmapping is used; otherwise less will work too)
951         s32 padding = 16;
952         s32 column_padding = 16;
953         s32 column_width = 256; // Space for 16 pieces of 16x16 textures
954
955         /*
956                 First pass: generate almost everything
957         */
958         core::position2d<s32> pos_in_atlas(0,0);
959         
960         pos_in_atlas.X = column_padding;
961         pos_in_atlas.Y = padding;
962
963         for(std::set<std::string>::iterator
964                         i = sourcelist.begin();
965                         i != sourcelist.end(); ++i)
966         {
967                 std::string name = *i;
968
969                 // Generate image by name
970                 video::IImage *img2 = generate_image_from_scratch(name, m_device,
971                                 &m_sourcecache);
972                 if(img2 == NULL)
973                 {
974                         errorstream<<"TextureSource::buildMainAtlas(): "
975                                         <<"Couldn't generate image \""<<name<<"\""<<std::endl;
976                         continue;
977                 }
978
979                 core::dimension2d<u32> dim = img2->getDimension();
980
981                 // Don't add to atlas if image is too large
982                 core::dimension2d<u32> max_size_in_atlas(64,64);
983                 if(dim.Width > max_size_in_atlas.Width
984                 || dim.Height > max_size_in_atlas.Height)
985                 {
986                         infostream<<"TextureSource::buildMainAtlas(): Not adding "
987                                         <<"\""<<name<<"\" because image is large"<<std::endl;
988                         continue;
989                 }
990
991                 // Wrap columns and stop making atlas if atlas is full
992                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
993                 {
994                         if(pos_in_atlas.X > (s32)atlas_dim.Width - column_width - column_padding){
995                                 errorstream<<"TextureSource::buildMainAtlas(): "
996                                                 <<"Atlas is full, not adding more textures."
997                                                 <<std::endl;
998                                 break;
999                         }
1000                         pos_in_atlas.Y = padding;
1001                         pos_in_atlas.X += column_width + column_padding*2;
1002                 }
1003                 
1004                 /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
1005                                 <<"\" to texture atlas"<<std::endl;*/
1006
1007                 // Tile it a few times in the X direction
1008                 u16 xwise_tiling = column_width / dim.Width;
1009                 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
1010                         xwise_tiling = 16;
1011                 for(u32 j=0; j<xwise_tiling; j++)
1012                 {
1013                         // Copy the copy to the atlas
1014                         /*img2->copyToWithAlpha(atlas_img,
1015                                         pos_in_atlas + v2s32(j*dim.Width,0),
1016                                         core::rect<s32>(v2s32(0,0), dim),
1017                                         video::SColor(255,255,255,255),
1018                                         NULL);*/
1019                         img2->copyTo(atlas_img,
1020                                         pos_in_atlas + v2s32(j*dim.Width,0),
1021                                         core::rect<s32>(v2s32(0,0), dim),
1022                                         NULL);
1023                 }
1024
1025                 // Copy the borders a few times to disallow texture bleeding
1026                 for(u32 side=0; side<2; side++) // top and bottom
1027                 for(s32 y0=0; y0<padding; y0++)
1028                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
1029                 {
1030                         s32 dst_y;
1031                         s32 src_y;
1032                         if(side==0)
1033                         {
1034                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
1035                                 src_y = pos_in_atlas.Y + dim.Height - 1;
1036                         }
1037                         else
1038                         {
1039                                 dst_y = -y0 + pos_in_atlas.Y-1;
1040                                 src_y = pos_in_atlas.Y;
1041                         }
1042                         s32 x = x0 + pos_in_atlas.X;
1043                         video::SColor c = atlas_img->getPixel(x, src_y);
1044                         atlas_img->setPixel(x,dst_y,c);
1045                 }
1046
1047                 for(u32 side=0; side<2; side++) // left and right
1048                 for(s32 x0=0; x0<column_padding; x0++)
1049                 for(s32 y0=-padding; y0<(s32)dim.Height+padding; y0++)
1050                 {
1051                         s32 dst_x;
1052                         s32 src_x;
1053                         if(side==0)
1054                         {
1055                                 dst_x = x0 + pos_in_atlas.X + dim.Width*xwise_tiling;
1056                                 src_x = pos_in_atlas.X + dim.Width*xwise_tiling - 1;
1057                         }
1058                         else
1059                         {
1060                                 dst_x = -x0 + pos_in_atlas.X-1;
1061                                 src_x = pos_in_atlas.X;
1062                         }
1063                         s32 y = y0 + pos_in_atlas.Y;
1064                         s32 src_y = MYMAX((int)pos_in_atlas.Y, MYMIN((int)pos_in_atlas.Y + (int)dim.Height - 1, y));
1065                         s32 dst_y = y;
1066                         video::SColor c = atlas_img->getPixel(src_x, src_y);
1067                         atlas_img->setPixel(dst_x,dst_y,c);
1068                 }
1069
1070                 img2->drop();
1071
1072                 /*
1073                         Add texture to caches
1074                 */
1075                 
1076                 bool reuse_old_id = false;
1077                 u32 id = m_atlaspointer_cache.size();
1078                 // Check old id without fetching a texture
1079                 std::map<std::string, u32>::iterator n;
1080                 n = m_name_to_id.find(name);
1081                 // If it exists, we will replace the old definition
1082                 if(n != m_name_to_id.end()){
1083                         id = n->second;
1084                         reuse_old_id = true;
1085                         /*infostream<<"TextureSource::buildMainAtlas(): "
1086                                         <<"Replacing old AtlasPointer"<<std::endl;*/
1087                 }
1088
1089                 // Create AtlasPointer
1090                 AtlasPointer ap(id);
1091                 ap.atlas = NULL; // Set on the second pass
1092                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
1093                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
1094                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
1095                                 (float)dim.Width/(float)atlas_dim.Height);
1096                 ap.tiled = xwise_tiling;
1097
1098                 // Create SourceAtlasPointer and add to containers
1099                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
1100                 if(reuse_old_id)
1101                         m_atlaspointer_cache[id] = nap;
1102                 else
1103                         m_atlaspointer_cache.push_back(nap);
1104                 m_name_to_id[name] = id;
1105                         
1106                 // Increment position
1107                 pos_in_atlas.Y += dim.Height + padding * 2;
1108         }
1109
1110         /*
1111                 Make texture
1112         */
1113         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
1114         assert(t);
1115
1116         /*
1117                 Second pass: set texture pointer in generated AtlasPointers
1118         */
1119         for(std::set<std::string>::iterator
1120                         i = sourcelist.begin();
1121                         i != sourcelist.end(); ++i)
1122         {
1123                 std::string name = *i;
1124                 if(m_name_to_id.find(name) == m_name_to_id.end())
1125                         continue;
1126                 u32 id = m_name_to_id[name];
1127                 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
1128                 m_atlaspointer_cache[id].a.atlas = t;
1129         }
1130
1131         /*
1132                 Write image to file so that it can be inspected
1133         */
1134         /*std::string atlaspath = porting::path_user
1135                         + DIR_DELIM + "generated_texture_atlas.png";
1136         infostream<<"Removing and writing texture atlas for inspection to "
1137                         <<atlaspath<<std::endl;
1138         fs::RecursiveDelete(atlaspath);
1139         driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
1140 }
1141
1142 video::IImage* generate_image_from_scratch(std::string name,
1143                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1144 {
1145         /*infostream<<"generate_image_from_scratch(): "
1146                         "\""<<name<<"\""<<std::endl;*/
1147         
1148         video::IVideoDriver* driver = device->getVideoDriver();
1149         assert(driver);
1150
1151         /*
1152                 Get the base image
1153         */
1154
1155         video::IImage *baseimg = NULL;
1156
1157         char separator = '^';
1158
1159         // Find last meta separator in name
1160         s32 last_separator_position = name.find_last_of(separator);
1161         //if(last_separator_position == std::npos)
1162         //      last_separator_position = -1;
1163
1164         /*infostream<<"generate_image_from_scratch(): "
1165                         <<"last_separator_position="<<last_separator_position
1166                         <<std::endl;*/
1167
1168         /*
1169                 If separator was found, construct the base name and make the
1170                 base image using a recursive call
1171         */
1172         std::string base_image_name;
1173         if(last_separator_position != -1)
1174         {
1175                 // Construct base name
1176                 base_image_name = name.substr(0, last_separator_position);
1177                 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
1178                                 " to get base image of \""<<name<<"\" = \""
1179                 <<base_image_name<<"\""<<std::endl;*/
1180                 baseimg = generate_image_from_scratch(base_image_name, device,
1181                                 sourcecache);
1182         }
1183         
1184         /*
1185                 Parse out the last part of the name of the image and act
1186                 according to it
1187         */
1188
1189         std::string last_part_of_name = name.substr(last_separator_position+1);
1190         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
1191         
1192         // Generate image according to part of name
1193         if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
1194         {
1195                 errorstream<<"generate_image_from_scratch(): "
1196                                 "failed to generate \""<<last_part_of_name<<"\""
1197                                 <<std::endl;
1198                 return NULL;
1199         }
1200         
1201         return baseimg;
1202 }
1203
1204 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
1205                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1206 {
1207         video::IVideoDriver* driver = device->getVideoDriver();
1208         assert(driver);
1209
1210         // Stuff starting with [ are special commands
1211         if(part_of_name.size() == 0 || part_of_name[0] != '[')
1212         {
1213                 video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
1214
1215                 if(image == NULL)
1216                 {
1217                         if(part_of_name != ""){
1218                                 errorstream<<"generate_image(): Could not load image \""
1219                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
1220                                 errorstream<<"generate_image(): Creating a dummy"
1221                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
1222                         }
1223
1224                         // Just create a dummy image
1225                         //core::dimension2d<u32> dim(2,2);
1226                         core::dimension2d<u32> dim(1,1);
1227                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
1228                         assert(image);
1229                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
1230                         image->setPixel(1,0, video::SColor(255,0,255,0));
1231                         image->setPixel(0,1, video::SColor(255,0,0,255));
1232                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
1233                         image->setPixel(0,0, video::SColor(255,myrand()%256,
1234                                         myrand()%256,myrand()%256));
1235                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
1236                                         myrand()%256,myrand()%256));
1237                         image->setPixel(0,1, video::SColor(255,myrand()%256,
1238                                         myrand()%256,myrand()%256));
1239                         image->setPixel(1,1, video::SColor(255,myrand()%256,
1240                                         myrand()%256,myrand()%256));*/
1241                 }
1242
1243                 // If base image is NULL, load as base.
1244                 if(baseimg == NULL)
1245                 {
1246                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
1247                         /*
1248                                 Copy it this way to get an alpha channel.
1249                                 Otherwise images with alpha cannot be blitted on 
1250                                 images that don't have alpha in the original file.
1251                         */
1252                         core::dimension2d<u32> dim = image->getDimension();
1253                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1254                         image->copyTo(baseimg);
1255                 }
1256                 // Else blit on base.
1257                 else
1258                 {
1259                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1260                         // Size of the copied area
1261                         core::dimension2d<u32> dim = image->getDimension();
1262                         //core::dimension2d<u32> dim(16,16);
1263                         // Position to copy the blitted to in the base image
1264                         core::position2d<s32> pos_to(0,0);
1265                         // Position to copy the blitted from in the blitted image
1266                         core::position2d<s32> pos_from(0,0);
1267                         // Blit
1268                         /*image->copyToWithAlpha(baseimg, pos_to,
1269                                         core::rect<s32>(pos_from, dim),
1270                                         video::SColor(255,255,255,255),
1271                                         NULL);*/
1272                         blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
1273                 }
1274                 //cleanup
1275                 image->drop();
1276         }
1277         else
1278         {
1279                 // A special texture modification
1280
1281                 /*infostream<<"generate_image(): generating special "
1282                                 <<"modification \""<<part_of_name<<"\""
1283                                 <<std::endl;*/
1284                 
1285                 /*
1286                         This is the simplest of all; it just adds stuff to the
1287                         name so that a separate texture is created.
1288
1289                         It is used to make textures for stuff that doesn't want
1290                         to implement getting the texture from a bigger texture
1291                         atlas.
1292                 */
1293                 if(part_of_name == "[forcesingle")
1294                 {
1295                         // If base image is NULL, create a random color
1296                         if(baseimg == NULL)
1297                         {
1298                                 core::dimension2d<u32> dim(1,1);
1299                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1300                                 assert(baseimg);
1301                                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
1302                                                 myrand()%256,myrand()%256));
1303                         }
1304                 }
1305                 /*
1306                         [crackN
1307                         Adds a cracking texture
1308                 */
1309                 else if(part_of_name.substr(0,6) == "[crack")
1310                 {
1311                         if(baseimg == NULL)
1312                         {
1313                                 errorstream<<"generate_image(): baseimg==NULL "
1314                                                 <<"for part_of_name=\""<<part_of_name
1315                                                 <<"\", cancelling."<<std::endl;
1316                                 return false;
1317                         }
1318                         
1319                         // Crack image number and overlay option
1320                         s32 progression = 0;
1321                         bool use_overlay = false;
1322                         if(part_of_name.substr(6,1) == "o")
1323                         {
1324                                 progression = stoi(part_of_name.substr(7));
1325                                 use_overlay = true;
1326                         }
1327                         else
1328                         {
1329                                 progression = stoi(part_of_name.substr(6));
1330                                 use_overlay = false;
1331                         }
1332
1333                         // Size of the base image
1334                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1335                         
1336                         /*
1337                                 Load crack image.
1338
1339                                 It is an image with a number of cracking stages
1340                                 horizontally tiled.
1341                         */
1342                         video::IImage *img_crack = sourcecache->getOrLoad(
1343                                         "crack_anylength.png", device);
1344                 
1345                         if(img_crack && progression >= 0)
1346                         {
1347                                 // Dimension of original image
1348                                 core::dimension2d<u32> dim_crack
1349                                                 = img_crack->getDimension();
1350                                 // Count of crack stages
1351                                 s32 crack_count = dim_crack.Height / dim_crack.Width;
1352                                 // Limit progression
1353                                 if(progression > crack_count-1)
1354                                         progression = crack_count-1;
1355                                 // Dimension of a single crack stage
1356                                 core::dimension2d<u32> dim_crack_cropped(
1357                                         dim_crack.Width,
1358                                         dim_crack.Width
1359                                 );
1360                                 // Create cropped and scaled crack images
1361                                 video::IImage *img_crack_cropped = driver->createImage(
1362                                                 video::ECF_A8R8G8B8, dim_crack_cropped);
1363                                 video::IImage *img_crack_scaled = driver->createImage(
1364                                                 video::ECF_A8R8G8B8, dim_base);
1365
1366                                 if(img_crack_cropped && img_crack_scaled)
1367                                 {
1368                                         // Crop crack image
1369                                         v2s32 pos_crack(0, progression*dim_crack.Width);
1370                                         img_crack->copyTo(img_crack_cropped,
1371                                                         v2s32(0,0),
1372                                                         core::rect<s32>(pos_crack, dim_crack_cropped));
1373                                         // Scale crack image by copying
1374                                         img_crack_cropped->copyToScaling(img_crack_scaled);
1375                                         // Copy or overlay crack image
1376                                         if(use_overlay)
1377                                         {
1378                                                 overlay(baseimg, img_crack_scaled);
1379                                         }
1380                                         else
1381                                         {
1382                                                 /*img_crack_scaled->copyToWithAlpha(
1383                                                                 baseimg,
1384                                                                 v2s32(0,0),
1385                                                                 core::rect<s32>(v2s32(0,0), dim_base),
1386                                                                 video::SColor(255,255,255,255));*/
1387                                                 blit_with_alpha(img_crack_scaled, baseimg,
1388                                                                 v2s32(0,0), v2s32(0,0), dim_base);
1389                                         }
1390                                 }
1391
1392                                 if(img_crack_scaled)
1393                                         img_crack_scaled->drop();
1394
1395                                 if(img_crack_cropped)
1396                                         img_crack_cropped->drop();
1397                                 
1398                                 img_crack->drop();
1399                         }
1400                 }
1401                 /*
1402                         [combine:WxH:X,Y=filename:X,Y=filename2
1403                         Creates a bigger texture from an amount of smaller ones
1404                 */
1405                 else if(part_of_name.substr(0,8) == "[combine")
1406                 {
1407                         Strfnd sf(part_of_name);
1408                         sf.next(":");
1409                         u32 w0 = stoi(sf.next("x"));
1410                         u32 h0 = stoi(sf.next(":"));
1411                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1412                         core::dimension2d<u32> dim(w0,h0);
1413                         if(baseimg == NULL)
1414                         {
1415                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1416                                 baseimg->fill(video::SColor(0,0,0,0));
1417                         }
1418                         while(sf.atend() == false)
1419                         {
1420                                 u32 x = stoi(sf.next(","));
1421                                 u32 y = stoi(sf.next("="));
1422                                 std::string filename = sf.next(":");
1423                                 infostream<<"Adding \""<<filename
1424                                                 <<"\" to combined ("<<x<<","<<y<<")"
1425                                                 <<std::endl;
1426                                 video::IImage *img = sourcecache->getOrLoad(filename, device);
1427                                 if(img)
1428                                 {
1429                                         core::dimension2d<u32> dim = img->getDimension();
1430                                         infostream<<"Size "<<dim.Width
1431                                                         <<"x"<<dim.Height<<std::endl;
1432                                         core::position2d<s32> pos_base(x, y);
1433                                         video::IImage *img2 =
1434                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1435                                         img->copyTo(img2);
1436                                         img->drop();
1437                                         /*img2->copyToWithAlpha(baseimg, pos_base,
1438                                                         core::rect<s32>(v2s32(0,0), dim),
1439                                                         video::SColor(255,255,255,255),
1440                                                         NULL);*/
1441                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
1442                                         img2->drop();
1443                                 }
1444                                 else
1445                                 {
1446                                         infostream<<"img==NULL"<<std::endl;
1447                                 }
1448                         }
1449                 }
1450                 /*
1451                         "[brighten"
1452                 */
1453                 else if(part_of_name.substr(0,9) == "[brighten")
1454                 {
1455                         if(baseimg == NULL)
1456                         {
1457                                 errorstream<<"generate_image(): baseimg==NULL "
1458                                                 <<"for part_of_name=\""<<part_of_name
1459                                                 <<"\", cancelling."<<std::endl;
1460                                 return false;
1461                         }
1462
1463                         brighten(baseimg);
1464                 }
1465                 /*
1466                         "[noalpha"
1467                         Make image completely opaque.
1468                         Used for the leaves texture when in old leaves mode, so
1469                         that the transparent parts don't look completely black 
1470                         when simple alpha channel is used for rendering.
1471                 */
1472                 else if(part_of_name.substr(0,8) == "[noalpha")
1473                 {
1474                         if(baseimg == NULL)
1475                         {
1476                                 errorstream<<"generate_image(): baseimg==NULL "
1477                                                 <<"for part_of_name=\""<<part_of_name
1478                                                 <<"\", cancelling."<<std::endl;
1479                                 return false;
1480                         }
1481
1482                         core::dimension2d<u32> dim = baseimg->getDimension();
1483                         
1484                         // Set alpha to full
1485                         for(u32 y=0; y<dim.Height; y++)
1486                         for(u32 x=0; x<dim.Width; x++)
1487                         {
1488                                 video::SColor c = baseimg->getPixel(x,y);
1489                                 c.setAlpha(255);
1490                                 baseimg->setPixel(x,y,c);
1491                         }
1492                 }
1493                 /*
1494                         "[makealpha:R,G,B"
1495                         Convert one color to transparent.
1496                 */
1497                 else if(part_of_name.substr(0,11) == "[makealpha:")
1498                 {
1499                         if(baseimg == NULL)
1500                         {
1501                                 errorstream<<"generate_image(): baseimg==NULL "
1502                                                 <<"for part_of_name=\""<<part_of_name
1503                                                 <<"\", cancelling."<<std::endl;
1504                                 return false;
1505                         }
1506
1507                         Strfnd sf(part_of_name.substr(11));
1508                         u32 r1 = stoi(sf.next(","));
1509                         u32 g1 = stoi(sf.next(","));
1510                         u32 b1 = stoi(sf.next(""));
1511                         std::string filename = sf.next("");
1512
1513                         core::dimension2d<u32> dim = baseimg->getDimension();
1514                         
1515                         /*video::IImage *oldbaseimg = baseimg;
1516                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1517                         oldbaseimg->copyTo(baseimg);
1518                         oldbaseimg->drop();*/
1519
1520                         // Set alpha to full
1521                         for(u32 y=0; y<dim.Height; y++)
1522                         for(u32 x=0; x<dim.Width; x++)
1523                         {
1524                                 video::SColor c = baseimg->getPixel(x,y);
1525                                 u32 r = c.getRed();
1526                                 u32 g = c.getGreen();
1527                                 u32 b = c.getBlue();
1528                                 if(!(r == r1 && g == g1 && b == b1))
1529                                         continue;
1530                                 c.setAlpha(0);
1531                                 baseimg->setPixel(x,y,c);
1532                         }
1533                 }
1534                 /*
1535                         "[transformN"
1536                         Rotates and/or flips the image.
1537
1538                         N can be a number (between 0 and 7) or a transform name.
1539                         Rotations are counter-clockwise.
1540                         0  I      identity
1541                         1  R90    rotate by 90 degrees
1542                         2  R180   rotate by 180 degrees
1543                         3  R270   rotate by 270 degrees
1544                         4  FX     flip X
1545                         5  FXR90  flip X then rotate by 90 degrees
1546                         6  FY     flip Y
1547                         7  FYR90  flip Y then rotate by 90 degrees
1548
1549                         Note: Transform names can be concatenated to produce
1550                         their product (applies the first then the second).
1551                         The resulting transform will be equivalent to one of the
1552                         eight existing ones, though (see: dihedral group).
1553                 */
1554                 else if(part_of_name.substr(0,10) == "[transform")
1555                 {
1556                         if(baseimg == NULL)
1557                         {
1558                                 errorstream<<"generate_image(): baseimg==NULL "
1559                                                 <<"for part_of_name=\""<<part_of_name
1560                                                 <<"\", cancelling."<<std::endl;
1561                                 return false;
1562                         }
1563
1564                         u32 transform = parseImageTransform(part_of_name.substr(10));
1565                         core::dimension2d<u32> dim = imageTransformDimension(
1566                                         transform, baseimg->getDimension());
1567                         video::IImage *image = driver->createImage(
1568                                         baseimg->getColorFormat(), dim);
1569                         assert(image);
1570                         imageTransform(transform, baseimg, image);
1571                         baseimg->drop();
1572                         baseimg = image;
1573                 }
1574                 /*
1575                         [inventorycube{topimage{leftimage{rightimage
1576                         In every subimage, replace ^ with &.
1577                         Create an "inventory cube".
1578                         NOTE: This should be used only on its own.
1579                         Example (a grass block (not actually used in game):
1580                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1581                 */
1582                 else if(part_of_name.substr(0,14) == "[inventorycube")
1583                 {
1584                         if(baseimg != NULL)
1585                         {
1586                                 errorstream<<"generate_image(): baseimg!=NULL "
1587                                                 <<"for part_of_name=\""<<part_of_name
1588                                                 <<"\", cancelling."<<std::endl;
1589                                 return false;
1590                         }
1591
1592                         str_replace_char(part_of_name, '&', '^');
1593                         Strfnd sf(part_of_name);
1594                         sf.next("{");
1595                         std::string imagename_top = sf.next("{");
1596                         std::string imagename_left = sf.next("{");
1597                         std::string imagename_right = sf.next("{");
1598
1599                         // Generate images for the faces of the cube
1600                         video::IImage *img_top = generate_image_from_scratch(
1601                                         imagename_top, device, sourcecache);
1602                         video::IImage *img_left = generate_image_from_scratch(
1603                                         imagename_left, device, sourcecache);
1604                         video::IImage *img_right = generate_image_from_scratch(
1605                                         imagename_right, device, sourcecache);
1606                         assert(img_top && img_left && img_right);
1607
1608                         // Create textures from images
1609                         video::ITexture *texture_top = driver->addTexture(
1610                                         (imagename_top + "__temp__").c_str(), img_top);
1611                         video::ITexture *texture_left = driver->addTexture(
1612                                         (imagename_left + "__temp__").c_str(), img_left);
1613                         video::ITexture *texture_right = driver->addTexture(
1614                                         (imagename_right + "__temp__").c_str(), img_right);
1615                         assert(texture_top && texture_left && texture_right);
1616
1617                         // Drop images
1618                         img_top->drop();
1619                         img_left->drop();
1620                         img_right->drop();
1621                         
1622                         /*
1623                                 Draw a cube mesh into a render target texture
1624                         */
1625                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1626                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1627                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1628                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1629                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1630                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1631                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1632                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1633
1634                         core::dimension2d<u32> dim(64,64);
1635                         std::string rtt_texture_name = part_of_name + "_RTT";
1636
1637                         v3f camera_position(0, 1.0, -1.5);
1638                         camera_position.rotateXZBy(45);
1639                         v3f camera_lookat(0, 0, 0);
1640                         core::CMatrix4<f32> camera_projection_matrix;
1641                         // Set orthogonal projection
1642                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
1643                                         1.65, 1.65, 0, 100);
1644
1645                         video::SColorf ambient_light(0.2,0.2,0.2);
1646                         v3f light_position(10, 100, -50);
1647                         video::SColorf light_color(0.5,0.5,0.5);
1648                         f32 light_radius = 1000;
1649
1650                         video::ITexture *rtt = generateTextureFromMesh(
1651                                         cube, device, dim, rtt_texture_name,
1652                                         camera_position,
1653                                         camera_lookat,
1654                                         camera_projection_matrix,
1655                                         ambient_light,
1656                                         light_position,
1657                                         light_color,
1658                                         light_radius);
1659                         
1660                         // Drop mesh
1661                         cube->drop();
1662
1663                         // Free textures of images
1664                         driver->removeTexture(texture_top);
1665                         driver->removeTexture(texture_left);
1666                         driver->removeTexture(texture_right);
1667                         
1668                         if(rtt == NULL)
1669                         {
1670                                 baseimg = generate_image_from_scratch(
1671                                                 imagename_top, device, sourcecache);
1672                                 return true;
1673                         }
1674
1675                         // Create image of render target
1676                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1677                         assert(image);
1678
1679                         //cleanup texture
1680                         driver->removeTexture(rtt);
1681
1682                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1683
1684                         if(image)
1685                         {
1686                                 image->copyTo(baseimg);
1687                                 image->drop();
1688                         }
1689                 }
1690                 /*
1691                         [lowpart:percent:filename
1692                         Adds the lower part of a texture
1693                 */
1694                 else if(part_of_name.substr(0,9) == "[lowpart:")
1695                 {
1696                         Strfnd sf(part_of_name);
1697                         sf.next(":");
1698                         u32 percent = stoi(sf.next(":"));
1699                         std::string filename = sf.next(":");
1700                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1701
1702                         if(baseimg == NULL)
1703                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1704                         video::IImage *img = sourcecache->getOrLoad(filename, device);
1705                         if(img)
1706                         {
1707                                 core::dimension2d<u32> dim = img->getDimension();
1708                                 core::position2d<s32> pos_base(0, 0);
1709                                 video::IImage *img2 =
1710                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1711                                 img->copyTo(img2);
1712                                 img->drop();
1713                                 core::position2d<s32> clippos(0, 0);
1714                                 clippos.Y = dim.Height * (100-percent) / 100;
1715                                 core::dimension2d<u32> clipdim = dim;
1716                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1717                                 core::rect<s32> cliprect(clippos, clipdim);
1718                                 img2->copyToWithAlpha(baseimg, pos_base,
1719                                                 core::rect<s32>(v2s32(0,0), dim),
1720                                                 video::SColor(255,255,255,255),
1721                                                 &cliprect);
1722                                 img2->drop();
1723                         }
1724                 }
1725                 /*
1726                         [verticalframe:N:I
1727                         Crops a frame of a vertical animation.
1728                         N = frame count, I = frame index
1729                 */
1730                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1731                 {
1732                         Strfnd sf(part_of_name);
1733                         sf.next(":");
1734                         u32 frame_count = stoi(sf.next(":"));
1735                         u32 frame_index = stoi(sf.next(":"));
1736
1737                         if(baseimg == NULL){
1738                                 errorstream<<"generate_image(): baseimg!=NULL "
1739                                                 <<"for part_of_name=\""<<part_of_name
1740                                                 <<"\", cancelling."<<std::endl;
1741                                 return false;
1742                         }
1743                         
1744                         v2u32 frame_size = baseimg->getDimension();
1745                         frame_size.Y /= frame_count;
1746
1747                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1748                                         frame_size);
1749                         if(!img){
1750                                 errorstream<<"generate_image(): Could not create image "
1751                                                 <<"for part_of_name=\""<<part_of_name
1752                                                 <<"\", cancelling."<<std::endl;
1753                                 return false;
1754                         }
1755
1756                         // Fill target image with transparency
1757                         img->fill(video::SColor(0,0,0,0));
1758
1759                         core::dimension2d<u32> dim = frame_size;
1760                         core::position2d<s32> pos_dst(0, 0);
1761                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1762                         baseimg->copyToWithAlpha(img, pos_dst,
1763                                         core::rect<s32>(pos_src, dim),
1764                                         video::SColor(255,255,255,255),
1765                                         NULL);
1766                         // Replace baseimg
1767                         baseimg->drop();
1768                         baseimg = img;
1769                 }
1770                 else
1771                 {
1772                         errorstream<<"generate_image(): Invalid "
1773                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1774                 }
1775         }
1776
1777         return true;
1778 }
1779
1780 void overlay(video::IImage *image, video::IImage *overlay)
1781 {
1782         /*
1783                 Copy overlay to image, taking alpha into account.
1784                 Where image is transparent, don't copy from overlay.
1785                 Images sizes must be identical.
1786         */
1787         if(image == NULL || overlay == NULL)
1788                 return;
1789         
1790         core::dimension2d<u32> dim = image->getDimension();
1791         core::dimension2d<u32> dim_overlay = overlay->getDimension();
1792         assert(dim == dim_overlay);
1793
1794         for(u32 y=0; y<dim.Height; y++)
1795         for(u32 x=0; x<dim.Width; x++)
1796         {
1797                 video::SColor c1 = image->getPixel(x,y);
1798                 video::SColor c2 = overlay->getPixel(x,y);
1799                 u32 a1 = c1.getAlpha();
1800                 u32 a2 = c2.getAlpha();
1801                 if(a1 == 255 && a2 != 0)
1802                 {
1803                         c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
1804                         c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
1805                         c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
1806                 }
1807                 image->setPixel(x,y,c1);
1808         }
1809 }
1810
1811 /*
1812         Draw an image on top of an another one, using the alpha channel of the
1813         source image
1814
1815         This exists because IImage::copyToWithAlpha() doesn't seem to always
1816         work.
1817 */
1818 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1819                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1820 {
1821         for(u32 y0=0; y0<size.Y; y0++)
1822         for(u32 x0=0; x0<size.X; x0++)
1823         {
1824                 s32 src_x = src_pos.X + x0;
1825                 s32 src_y = src_pos.Y + y0;
1826                 s32 dst_x = dst_pos.X + x0;
1827                 s32 dst_y = dst_pos.Y + y0;
1828                 video::SColor src_c = src->getPixel(src_x, src_y);
1829                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1830                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1831                 dst->setPixel(dst_x, dst_y, dst_c);
1832         }
1833 }
1834
1835 void brighten(video::IImage *image)
1836 {
1837         if(image == NULL)
1838                 return;
1839         
1840         core::dimension2d<u32> dim = image->getDimension();
1841
1842         for(u32 y=0; y<dim.Height; y++)
1843         for(u32 x=0; x<dim.Width; x++)
1844         {
1845                 video::SColor c = image->getPixel(x,y);
1846                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1847                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1848                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1849                 image->setPixel(x,y,c);
1850         }
1851 }
1852
1853 u32 parseImageTransform(const std::string& s)
1854 {
1855         int total_transform = 0;
1856
1857         std::string transform_names[8];
1858         transform_names[0] = "i";
1859         transform_names[1] = "r90";
1860         transform_names[2] = "r180";
1861         transform_names[3] = "r270";
1862         transform_names[4] = "fx";
1863         transform_names[6] = "fy";
1864
1865         std::size_t pos = 0;
1866         while(pos < s.size())
1867         {
1868                 int transform = -1;
1869                 for(int i = 0; i <= 7; ++i)
1870                 {
1871                         const std::string &name_i = transform_names[i];
1872
1873                         if(s[pos] == ('0' + i))
1874                         {
1875                                 transform = i;
1876                                 pos++;
1877                                 break;
1878                         }
1879                         else if(!(name_i.empty()) &&
1880                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1881                         {
1882                                 transform = i;
1883                                 pos += name_i.size();
1884                                 break;
1885                         }
1886                 }
1887                 if(transform < 0)
1888                         break;
1889
1890                 // Multiply total_transform and transform in the group D4
1891                 int new_total = 0;
1892                 if(transform < 4)
1893                         new_total = (transform + total_transform) % 4;
1894                 else
1895                         new_total = (transform - total_transform + 8) % 4;
1896                 if((transform >= 4) ^ (total_transform >= 4))
1897                         new_total += 4;
1898
1899                 total_transform = new_total;
1900         }
1901         return total_transform;
1902 }
1903
1904 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1905 {
1906         if(transform % 2 == 0)
1907                 return dim;
1908         else
1909                 return core::dimension2d<u32>(dim.Height, dim.Width);
1910 }
1911
1912 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1913 {
1914         if(src == NULL || dst == NULL)
1915                 return;
1916         
1917         core::dimension2d<u32> srcdim = src->getDimension();
1918         core::dimension2d<u32> dstdim = dst->getDimension();
1919
1920         assert(dstdim == imageTransformDimension(transform, srcdim));
1921         assert(transform >= 0 && transform <= 7);
1922
1923         /*
1924                 Compute the transformation from source coordinates (sx,sy)
1925                 to destination coordinates (dx,dy).
1926         */
1927         int sxn = 0;
1928         int syn = 2;
1929         if(transform == 0)         // identity
1930                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1931         else if(transform == 1)    // rotate by 90 degrees ccw
1932                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1933         else if(transform == 2)    // rotate by 180 degrees
1934                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1935         else if(transform == 3)    // rotate by 270 degrees ccw
1936                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1937         else if(transform == 4)    // flip x
1938                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1939         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1940                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1941         else if(transform == 6)    // flip y
1942                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1943         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1944                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1945
1946         for(u32 dy=0; dy<dstdim.Height; dy++)
1947         for(u32 dx=0; dx<dstdim.Width; dx++)
1948         {
1949                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1950                 u32 sx = entries[sxn];
1951                 u32 sy = entries[syn];
1952                 video::SColor c = src->getPixel(sx,sy);
1953                 dst->setPixel(dx,dy,c);
1954         }
1955 }