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