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