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