3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU 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.
22 #include "main.h" // for g_settings
26 #include <ICameraSceneNode.h>
28 #include "mapnode.h" // For texture atlas making
29 #include "mineral.h" // For texture atlas making
32 A cache from texture name to texture path
34 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
37 Replaces the filename extension.
39 std::string image = "a/image.png"
40 replace_ext(image, "jpg")
41 -> image = "a/image.jpg"
42 Returns true on success.
44 static bool replace_ext(std::string &path, const char *ext)
48 // Find place of last dot, fail if \ or / found.
50 for(s32 i=path.size()-1; i>=0; i--)
58 if(path[i] == '\\' || path[i] == '/')
61 // If not found, return an empty string
64 // Else make the new path
65 path = path.substr(0, last_dot_i+1) + ext;
70 Find out the full path of an image by trying different filename
75 static std::string getImagePath(std::string path)
77 // A NULL-ended list of possible image extensions
78 const char *extensions[] = {
79 "png", "jpg", "bmp", "tga",
80 "pcx", "ppm", "psd", "wal", "rgb",
84 const char **ext = extensions;
86 bool r = replace_ext(path, *ext);
89 if(fs::PathExists(path))
92 while((++ext) != NULL);
98 Gets the path to a texture by first checking if the texture exists
99 in texture_path and if not, using the data path.
101 Checks all supported extensions by replacing the original extension.
103 If not found, returns "".
105 Utilizes a thread-safe cache.
107 std::string getTexturePath(const std::string &filename)
109 std::string fullpath = "";
113 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
118 Check from texture_path
120 std::string texture_path = g_settings->get("texture_path");
121 if(texture_path != "")
123 std::string testpath = texture_path + DIR_DELIM + filename;
124 // Check all filename extensions. Returns "" if not found.
125 fullpath = getImagePath(testpath);
129 Check from default data directory
133 std::string rel_path = std::string("textures")+DIR_DELIM+filename;
134 std::string testpath = porting::path_data + DIR_DELIM + rel_path;
135 // Check all filename extensions. Returns "" if not found.
136 fullpath = getImagePath(testpath);
139 // Add to cache (also an empty result is cached)
140 g_texturename_to_path_cache.set(filename, fullpath);
150 TextureSource::TextureSource(IrrlichtDevice *device):
152 m_main_atlas_image(NULL),
153 m_main_atlas_texture(NULL)
157 m_atlaspointer_cache_mutex.Init();
159 m_main_thread = get_current_thread_id();
161 // Add a NULL AtlasPointer as the first index, named ""
162 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
163 m_name_to_id[""] = 0;
165 // Build main texture atlas
166 if(g_settings->getBool("enable_texture_atlas"))
169 infostream<<"Not building texture atlas."<<std::endl;
172 TextureSource::~TextureSource()
176 void TextureSource::processQueue()
181 if(m_get_texture_queue.size() > 0)
183 GetRequest<std::string, u32, u8, u8>
184 request = m_get_texture_queue.pop();
186 infostream<<"TextureSource::processQueue(): "
187 <<"got texture request with "
188 <<"name=\""<<request.key<<"\""
191 GetResult<std::string, u32, u8, u8>
193 result.key = request.key;
194 result.callers = request.callers;
195 result.item = getTextureIdDirect(request.key);
197 request.dest->push_back(result);
201 u32 TextureSource::getTextureId(const std::string &name)
203 //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
207 See if texture already exists
209 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
210 core::map<std::string, u32>::Node *n;
211 n = m_name_to_id.find(name);
214 return n->getValue();
221 if(get_current_thread_id() == m_main_thread)
223 return getTextureIdDirect(name);
227 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
229 // We're gonna ask the result to be put into here
230 ResultQueue<std::string, u32, u8, u8> result_queue;
232 // Throw a request in
233 m_get_texture_queue.add(name, 0, 0, &result_queue);
235 infostream<<"Waiting for texture from main thread, name=\""
236 <<name<<"\""<<std::endl;
240 // Wait result for a second
241 GetResult<std::string, u32, u8, u8>
242 result = result_queue.pop_front(1000);
244 // Check that at least something worked OK
245 assert(result.key == name);
249 catch(ItemNotFoundException &e)
251 infostream<<"Waiting for texture timed out."<<std::endl;
256 infostream<<"getTextureId(): Failed"<<std::endl;
261 // Draw a progress bar on the image
262 void make_progressbar(float value, video::IImage *image);
265 Generate image based on a string like "stone.png" or "[crack0".
266 if baseimg is NULL, it is created. Otherwise stuff is made on it.
268 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
269 IrrlichtDevice *device);
272 Generates an image from a full string like
273 "stone.png^mineral_coal.png^[crack0".
275 This is used by buildMainAtlas().
277 video::IImage* generate_image_from_scratch(std::string name,
278 IrrlichtDevice *device);
281 This method generates all the textures
283 u32 TextureSource::getTextureIdDirect(const std::string &name)
285 //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
287 // Empty name means texture 0
290 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
295 Calling only allowed from main thread
297 if(get_current_thread_id() != m_main_thread)
299 errorstream<<"TextureSource::getTextureIdDirect() "
300 "called not from main thread"<<std::endl;
305 See if texture already exists
308 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
310 core::map<std::string, u32>::Node *n;
311 n = m_name_to_id.find(name);
314 infostream<<"getTextureIdDirect(): \""<<name
315 <<"\" found in cache"<<std::endl;
316 return n->getValue();
320 infostream<<"getTextureIdDirect(): \""<<name
321 <<"\" NOT found in cache. Creating it."<<std::endl;
327 char separator = '^';
330 This is set to the id of the base image.
331 If left 0, there is no base image and a completely new image
334 u32 base_image_id = 0;
336 // Find last meta separator in name
337 s32 last_separator_position = -1;
338 for(s32 i=name.size()-1; i>=0; i--)
340 if(name[i] == separator)
342 last_separator_position = i;
347 If separator was found, construct the base name and make the
348 base image using a recursive call
350 std::string base_image_name;
351 if(last_separator_position != -1)
353 // Construct base name
354 base_image_name = name.substr(0, last_separator_position);
355 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
356 " to get base image of \""<<name<<"\" = \""
357 <<base_image_name<<"\""<<std::endl;*/
358 base_image_id = getTextureIdDirect(base_image_name);
361 //infostream<<"base_image_id="<<base_image_id<<std::endl;
363 video::IVideoDriver* driver = m_device->getVideoDriver();
366 video::ITexture *t = NULL;
369 An image will be built from files and then converted into a texture.
371 video::IImage *baseimg = NULL;
373 // If a base image was found, copy it to baseimg
374 if(base_image_id != 0)
376 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
378 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
380 video::IImage *image = ap.atlas_img;
384 infostream<<"getTextureIdDirect(): NULL image in "
385 <<"cache: \""<<base_image_name<<"\""
390 core::dimension2d<u32> dim = ap.intsize;
392 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
394 core::position2d<s32> pos_to(0,0);
395 core::position2d<s32> pos_from = ap.intpos;
399 v2s32(0,0), // position in target
400 core::rect<s32>(pos_from, dim) // from
403 /*infostream<<"getTextureIdDirect(): Loaded \""
404 <<base_image_name<<"\" from image cache"
410 Parse out the last part of the name of the image and act
414 std::string last_part_of_name = name.substr(last_separator_position+1);
415 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
417 // Generate image according to part of name
418 if(generate_image(last_part_of_name, baseimg, m_device) == false)
420 infostream<<"getTextureIdDirect(): "
421 "failed to generate \""<<last_part_of_name<<"\""
425 // If no resulting image, print a warning
428 infostream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
429 " create texture \""<<name<<"\""<<std::endl;
434 // Create texture from resulting image
435 t = driver->addTexture(name.c_str(), baseimg);
439 Add texture to caches (add NULL textures too)
442 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
444 u32 id = m_atlaspointer_cache.size();
450 core::dimension2d<u32> baseimg_dim(0,0);
452 baseimg_dim = baseimg->getDimension();
453 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
454 m_atlaspointer_cache.push_back(nap);
455 m_name_to_id.insert(name, id);
457 /*infostream<<"getTextureIdDirect(): "
458 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
463 std::string TextureSource::getTextureName(u32 id)
465 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
467 if(id >= m_atlaspointer_cache.size())
469 infostream<<"TextureSource::getTextureName(): id="<<id
470 <<" >= m_atlaspointer_cache.size()="
471 <<m_atlaspointer_cache.size()<<std::endl;
475 return m_atlaspointer_cache[id].name;
479 AtlasPointer TextureSource::getTexture(u32 id)
481 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
483 if(id >= m_atlaspointer_cache.size())
484 return AtlasPointer(0, NULL);
486 return m_atlaspointer_cache[id].a;
489 void TextureSource::buildMainAtlas()
491 infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
493 //return; // Disable (for testing)
495 video::IVideoDriver* driver = m_device->getVideoDriver();
498 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
500 // Create an image of the right size
501 core::dimension2d<u32> atlas_dim(1024,1024);
502 video::IImage *atlas_img =
503 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
505 if(atlas_img == NULL)
507 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
508 "image; not building texture atlas."<<std::endl;
513 Grab list of stuff to include in the texture atlas from the
514 main content features
517 core::map<std::string, bool> sourcelist;
519 for(u16 j=0; j<MAX_CONTENT+1; j++)
521 if(j == CONTENT_IGNORE || j == CONTENT_AIR)
523 ContentFeatures *f = &content_features(j);
524 for(core::map<std::string, bool>::Iterator
525 i = f->used_texturenames.getIterator();
526 i.atEnd() == false; i++)
528 std::string name = i.getNode()->getKey();
529 sourcelist[name] = true;
531 if(f->often_contains_mineral){
532 for(int k=1; k<MINERAL_COUNT; k++){
533 std::string mineraltexture = mineral_block_texture(k);
534 std::string fulltexture = name + "^" + mineraltexture;
535 sourcelist[fulltexture] = true;
541 infostream<<"Creating texture atlas out of textures: ";
542 for(core::map<std::string, bool>::Iterator
543 i = sourcelist.getIterator();
544 i.atEnd() == false; i++)
546 std::string name = i.getNode()->getKey();
547 infostream<<"\""<<name<<"\" ";
549 infostream<<std::endl;
551 // Padding to disallow texture bleeding
554 s32 column_width = 256;
555 s32 column_padding = 16;
558 First pass: generate almost everything
560 core::position2d<s32> pos_in_atlas(0,0);
562 pos_in_atlas.Y = padding;
564 for(core::map<std::string, bool>::Iterator
565 i = sourcelist.getIterator();
566 i.atEnd() == false; i++)
568 std::string name = i.getNode()->getKey();
570 /*video::IImage *img = driver->createImageFromFile(
571 getTexturePath(name.c_str()).c_str());
575 core::dimension2d<u32> dim = img->getDimension();
576 // Make a copy with the right color format
577 video::IImage *img2 =
578 driver->createImage(video::ECF_A8R8G8B8, dim);
582 // Generate image by name
583 video::IImage *img2 = generate_image_from_scratch(name, m_device);
586 infostream<<"TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
590 core::dimension2d<u32> dim = img2->getDimension();
592 // Don't add to atlas if image is large
593 core::dimension2d<u32> max_size_in_atlas(32,32);
594 if(dim.Width > max_size_in_atlas.Width
595 || dim.Height > max_size_in_atlas.Height)
597 infostream<<"TextureSource::buildMainAtlas(): Not adding "
598 <<"\""<<name<<"\" because image is large"<<std::endl;
602 // Wrap columns and stop making atlas if atlas is full
603 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
605 if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
606 errorstream<<"TextureSource::buildMainAtlas(): "
607 <<"Atlas is full, not adding more textures."
611 pos_in_atlas.Y = padding;
612 pos_in_atlas.X += column_width + column_padding;
615 infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
616 <<"\" to texture atlas"<<std::endl;
618 // Tile it a few times in the X direction
619 u16 xwise_tiling = column_width / dim.Width;
620 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
622 for(u32 j=0; j<xwise_tiling; j++)
624 // Copy the copy to the atlas
625 img2->copyToWithAlpha(atlas_img,
626 pos_in_atlas + v2s32(j*dim.Width,0),
627 core::rect<s32>(v2s32(0,0), dim),
628 video::SColor(255,255,255,255),
632 // Copy the borders a few times to disallow texture bleeding
633 for(u32 side=0; side<2; side++) // top and bottom
634 for(s32 y0=0; y0<padding; y0++)
635 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
641 dst_y = y0 + pos_in_atlas.Y + dim.Height;
642 src_y = pos_in_atlas.Y + dim.Height - 1;
646 dst_y = -y0 + pos_in_atlas.Y-1;
647 src_y = pos_in_atlas.Y;
649 s32 x = x0 + pos_in_atlas.X;
650 video::SColor c = atlas_img->getPixel(x, src_y);
651 atlas_img->setPixel(x,dst_y,c);
657 Add texture to caches
661 u32 id = m_atlaspointer_cache.size();
663 // Create AtlasPointer
665 ap.atlas = NULL; // Set on the second pass
666 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
667 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
668 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
669 (float)dim.Width/(float)atlas_dim.Height);
670 ap.tiled = xwise_tiling;
672 // Create SourceAtlasPointer and add to containers
673 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
674 m_atlaspointer_cache.push_back(nap);
675 m_name_to_id.insert(name, id);
677 // Increment position
678 pos_in_atlas.Y += dim.Height + padding * 2;
684 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
688 Second pass: set texture pointer in generated AtlasPointers
690 for(core::map<std::string, bool>::Iterator
691 i = sourcelist.getIterator();
692 i.atEnd() == false; i++)
694 std::string name = i.getNode()->getKey();
695 if(m_name_to_id.find(name) == NULL)
697 u32 id = m_name_to_id[name];
698 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
699 m_atlaspointer_cache[id].a.atlas = t;
703 Write image to file so that it can be inspected
705 /*std::string atlaspath = porting::path_userdata
706 + DIR_DELIM + "generated_texture_atlas.png";
707 infostream<<"Removing and writing texture atlas for inspection to "
708 <<atlaspath<<std::endl;
709 fs::RecursiveDelete(atlaspath);
710 driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
713 video::IImage* generate_image_from_scratch(std::string name,
714 IrrlichtDevice *device)
716 /*infostream<<"generate_image_from_scratch(): "
717 "\""<<name<<"\""<<std::endl;*/
719 video::IVideoDriver* driver = device->getVideoDriver();
726 video::IImage *baseimg = NULL;
728 char separator = '^';
730 // Find last meta separator in name
731 s32 last_separator_position = -1;
732 for(s32 i=name.size()-1; i>=0; i--)
734 if(name[i] == separator)
736 last_separator_position = i;
741 /*infostream<<"generate_image_from_scratch(): "
742 <<"last_separator_position="<<last_separator_position
746 If separator was found, construct the base name and make the
747 base image using a recursive call
749 std::string base_image_name;
750 if(last_separator_position != -1)
752 // Construct base name
753 base_image_name = name.substr(0, last_separator_position);
754 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
755 " to get base image of \""<<name<<"\" = \""
756 <<base_image_name<<"\""<<std::endl;*/
757 baseimg = generate_image_from_scratch(base_image_name, device);
761 Parse out the last part of the name of the image and act
765 std::string last_part_of_name = name.substr(last_separator_position+1);
766 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
768 // Generate image according to part of name
769 if(generate_image(last_part_of_name, baseimg, device) == false)
771 infostream<<"generate_image_from_scratch(): "
772 "failed to generate \""<<last_part_of_name<<"\""
780 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
781 IrrlichtDevice *device)
783 video::IVideoDriver* driver = device->getVideoDriver();
786 // Stuff starting with [ are special commands
787 if(part_of_name[0] != '[')
789 // A normal texture; load it from a file
790 std::string path = getTexturePath(part_of_name.c_str());
791 /*infostream<<"generate_image(): Loading path \""<<path
794 video::IImage *image = driver->createImageFromFile(path.c_str());
798 infostream<<"generate_image(): Could not load image \""
799 <<part_of_name<<"\" from path \""<<path<<"\""
800 <<" while building texture"<<std::endl;
804 infostream<<"generate_image(): Creating a dummy"
805 <<" image for \""<<part_of_name<<"\""<<std::endl;
807 // Just create a dummy image
808 //core::dimension2d<u32> dim(2,2);
809 core::dimension2d<u32> dim(1,1);
810 image = driver->createImage(video::ECF_A8R8G8B8, dim);
812 /*image->setPixel(0,0, video::SColor(255,255,0,0));
813 image->setPixel(1,0, video::SColor(255,0,255,0));
814 image->setPixel(0,1, video::SColor(255,0,0,255));
815 image->setPixel(1,1, video::SColor(255,255,0,255));*/
816 image->setPixel(0,0, video::SColor(255,myrand()%256,
817 myrand()%256,myrand()%256));
818 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
819 myrand()%256,myrand()%256));
820 image->setPixel(0,1, video::SColor(255,myrand()%256,
821 myrand()%256,myrand()%256));
822 image->setPixel(1,1, video::SColor(255,myrand()%256,
823 myrand()%256,myrand()%256));*/
826 // If base image is NULL, load as base.
829 //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
831 Copy it this way to get an alpha channel.
832 Otherwise images with alpha cannot be blitted on
833 images that don't have alpha in the original file.
835 core::dimension2d<u32> dim = image->getDimension();
836 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
837 image->copyTo(baseimg);
840 // Else blit on base.
843 //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
844 // Size of the copied area
845 core::dimension2d<u32> dim = image->getDimension();
846 //core::dimension2d<u32> dim(16,16);
847 // Position to copy the blitted to in the base image
848 core::position2d<s32> pos_to(0,0);
849 // Position to copy the blitted from in the blitted image
850 core::position2d<s32> pos_from(0,0);
852 image->copyToWithAlpha(baseimg, pos_to,
853 core::rect<s32>(pos_from, dim),
854 video::SColor(255,255,255,255),
862 // A special texture modification
864 infostream<<"generate_image(): generating special "
865 <<"modification \""<<part_of_name<<"\""
869 This is the simplest of all; it just adds stuff to the
870 name so that a separate texture is created.
872 It is used to make textures for stuff that doesn't want
873 to implement getting the texture from a bigger texture
876 if(part_of_name == "[forcesingle")
881 Adds a cracking texture
883 else if(part_of_name.substr(0,6) == "[crack")
887 infostream<<"generate_image(): baseimg==NULL "
888 <<"for part_of_name=\""<<part_of_name
889 <<"\", cancelling."<<std::endl;
893 // Crack image number
894 u16 progression = stoi(part_of_name.substr(6));
896 // Size of the base image
897 core::dimension2d<u32> dim_base = baseimg->getDimension();
902 It is an image with a number of cracking stages
905 video::IImage *img_crack = driver->createImageFromFile(
906 getTexturePath("crack.png").c_str());
910 // Dimension of original image
911 core::dimension2d<u32> dim_crack
912 = img_crack->getDimension();
913 // Count of crack stages
914 u32 crack_count = dim_crack.Height / dim_crack.Width;
916 if(progression > crack_count-1)
917 progression = crack_count-1;
918 // Dimension of a single scaled crack stage
919 core::dimension2d<u32> dim_crack_scaled_single(
923 // Dimension of scaled size
924 core::dimension2d<u32> dim_crack_scaled(
925 dim_crack_scaled_single.Width,
926 dim_crack_scaled_single.Height * crack_count
928 // Create scaled crack image
929 video::IImage *img_crack_scaled = driver->createImage(
930 video::ECF_A8R8G8B8, dim_crack_scaled);
933 // Scale crack image by copying
934 img_crack->copyToScaling(img_crack_scaled);
936 // Position to copy the crack from
937 core::position2d<s32> pos_crack_scaled(
939 dim_crack_scaled_single.Height * progression
942 // This tiling does nothing currently but is useful
943 for(u32 y0=0; y0<dim_base.Height
944 / dim_crack_scaled_single.Height; y0++)
945 for(u32 x0=0; x0<dim_base.Width
946 / dim_crack_scaled_single.Width; x0++)
948 // Position to copy the crack to in the base image
949 core::position2d<s32> pos_base(
950 x0*dim_crack_scaled_single.Width,
951 y0*dim_crack_scaled_single.Height
953 // Rectangle to copy the crack from on the scaled image
954 core::rect<s32> rect_crack_scaled(
956 dim_crack_scaled_single
959 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
961 video::SColor(255,255,255,255),
965 img_crack_scaled->drop();
972 [combine:WxH:X,Y=filename:X,Y=filename2
973 Creates a bigger texture from an amount of smaller ones
975 else if(part_of_name.substr(0,8) == "[combine")
977 Strfnd sf(part_of_name);
979 u32 w0 = stoi(sf.next("x"));
980 u32 h0 = stoi(sf.next(":"));
981 infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
982 core::dimension2d<u32> dim(w0,h0);
983 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
984 while(sf.atend() == false)
986 u32 x = stoi(sf.next(","));
987 u32 y = stoi(sf.next("="));
988 std::string filename = sf.next(":");
989 infostream<<"Adding \""<<filename
990 <<"\" to combined ("<<x<<","<<y<<")"
992 video::IImage *img = driver->createImageFromFile(
993 getTexturePath(filename.c_str()).c_str());
996 core::dimension2d<u32> dim = img->getDimension();
997 infostream<<"Size "<<dim.Width
998 <<"x"<<dim.Height<<std::endl;
999 core::position2d<s32> pos_base(x, y);
1000 video::IImage *img2 =
1001 driver->createImage(video::ECF_A8R8G8B8, dim);
1004 img2->copyToWithAlpha(baseimg, pos_base,
1005 core::rect<s32>(v2s32(0,0), dim),
1006 video::SColor(255,255,255,255),
1012 infostream<<"img==NULL"<<std::endl;
1018 Adds a progress bar, 0.0 <= N <= 1.0
1020 else if(part_of_name.substr(0,12) == "[progressbar")
1024 infostream<<"generate_image(): baseimg==NULL "
1025 <<"for part_of_name=\""<<part_of_name
1026 <<"\", cancelling."<<std::endl;
1030 float value = stof(part_of_name.substr(12));
1031 make_progressbar(value, baseimg);
1034 "[noalpha:filename.png"
1035 Use an image without it's alpha channel.
1036 Used for the leaves texture when in old leaves mode, so
1037 that the transparent parts don't look completely black
1038 when simple alpha channel is used for rendering.
1040 else if(part_of_name.substr(0,8) == "[noalpha")
1044 infostream<<"generate_image(): baseimg!=NULL "
1045 <<"for part_of_name=\""<<part_of_name
1046 <<"\", cancelling."<<std::endl;
1050 std::string filename = part_of_name.substr(9);
1052 std::string path = getTexturePath(filename.c_str());
1054 infostream<<"generate_image(): Loading path \""<<path
1057 video::IImage *image = driver->createImageFromFile(path.c_str());
1061 infostream<<"generate_image(): Loading path \""
1062 <<path<<"\" failed"<<std::endl;
1066 core::dimension2d<u32> dim = image->getDimension();
1067 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1069 // Set alpha to full
1070 for(u32 y=0; y<dim.Height; y++)
1071 for(u32 x=0; x<dim.Width; x++)
1073 video::SColor c = image->getPixel(x,y);
1075 image->setPixel(x,y,c);
1078 image->copyTo(baseimg);
1084 "[makealpha:R,G,B:filename.png"
1085 Use an image with converting one color to transparent.
1087 else if(part_of_name.substr(0,11) == "[makealpha:")
1091 infostream<<"generate_image(): baseimg!=NULL "
1092 <<"for part_of_name=\""<<part_of_name
1093 <<"\", cancelling."<<std::endl;
1097 Strfnd sf(part_of_name.substr(11));
1098 u32 r1 = stoi(sf.next(","));
1099 u32 g1 = stoi(sf.next(","));
1100 u32 b1 = stoi(sf.next(":"));
1101 std::string filename = sf.next("");
1103 std::string path = getTexturePath(filename.c_str());
1105 infostream<<"generate_image(): Loading path \""<<path
1108 video::IImage *image = driver->createImageFromFile(path.c_str());
1112 infostream<<"generate_image(): Loading path \""
1113 <<path<<"\" failed"<<std::endl;
1117 core::dimension2d<u32> dim = image->getDimension();
1118 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1121 image->copyTo(baseimg);
1125 for(u32 y=0; y<dim.Height; y++)
1126 for(u32 x=0; x<dim.Width; x++)
1128 video::SColor c = baseimg->getPixel(x,y);
1130 u32 g = c.getGreen();
1131 u32 b = c.getBlue();
1132 if(!(r == r1 && g == g1 && b == b1))
1135 baseimg->setPixel(x,y,c);
1140 "[makealpha2:R,G,B;R2,G2,B2:filename.png"
1141 Use an image with converting two colors to transparent.
1143 else if(part_of_name.substr(0,12) == "[makealpha2:")
1147 infostream<<"generate_image(): baseimg!=NULL "
1148 <<"for part_of_name=\""<<part_of_name
1149 <<"\", cancelling."<<std::endl;
1153 Strfnd sf(part_of_name.substr(12));
1154 u32 r1 = stoi(sf.next(","));
1155 u32 g1 = stoi(sf.next(","));
1156 u32 b1 = stoi(sf.next(";"));
1157 u32 r2 = stoi(sf.next(","));
1158 u32 g2 = stoi(sf.next(","));
1159 u32 b2 = stoi(sf.next(":"));
1160 std::string filename = sf.next("");
1162 std::string path = getTexturePath(filename.c_str());
1164 infostream<<"generate_image(): Loading path \""<<path
1167 video::IImage *image = driver->createImageFromFile(path.c_str());
1171 infostream<<"generate_image(): Loading path \""
1172 <<path<<"\" failed"<<std::endl;
1176 core::dimension2d<u32> dim = image->getDimension();
1177 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1180 image->copyTo(baseimg);
1184 for(u32 y=0; y<dim.Height; y++)
1185 for(u32 x=0; x<dim.Width; x++)
1187 video::SColor c = baseimg->getPixel(x,y);
1189 u32 g = c.getGreen();
1190 u32 b = c.getBlue();
1191 if(!(r == r1 && g == g1 && b == b1) &&
1192 !(r == r2 && g == g2 && b == b2))
1195 baseimg->setPixel(x,y,c);
1200 [inventorycube{topimage{leftimage{rightimage
1201 In every subimage, replace ^ with &.
1202 Create an "inventory cube".
1203 NOTE: This should be used only on its own.
1204 Example (a grass block (not actually used in game):
1205 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1207 else if(part_of_name.substr(0,14) == "[inventorycube")
1211 infostream<<"generate_image(): baseimg!=NULL "
1212 <<"for part_of_name=\""<<part_of_name
1213 <<"\", cancelling."<<std::endl;
1217 str_replace_char(part_of_name, '&', '^');
1218 Strfnd sf(part_of_name);
1220 std::string imagename_top = sf.next("{");
1221 std::string imagename_left = sf.next("{");
1222 std::string imagename_right = sf.next("{");
1227 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1229 infostream<<"generate_image(): EVDF_RENDER_TO_TARGET"
1230 " not supported. Creating fallback image"<<std::endl;
1231 baseimg = generate_image_from_scratch(
1232 imagename_top, device);
1238 //infostream<<"inventorycube w="<<w0<<" h="<<h0<<std::endl;
1239 core::dimension2d<u32> dim(w0,h0);
1241 // Generate images for the faces of the cube
1242 video::IImage *img_top = generate_image_from_scratch(
1243 imagename_top, device);
1244 video::IImage *img_left = generate_image_from_scratch(
1245 imagename_left, device);
1246 video::IImage *img_right = generate_image_from_scratch(
1247 imagename_right, device);
1248 assert(img_top && img_left && img_right);
1250 // TODO: Create textures from images
1251 video::ITexture *texture_top = driver->addTexture(
1252 (imagename_top + "__temp__").c_str(), img_top);
1253 assert(texture_top);
1260 // Create render target texture
1261 video::ITexture *rtt = NULL;
1262 std::string rtt_name = part_of_name + "_RTT";
1263 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1264 video::ECF_A8R8G8B8);
1267 // Set render target
1268 driver->setRenderTarget(rtt, true, true,
1269 video::SColor(0,0,0,0));
1271 // Get a scene manager
1272 scene::ISceneManager *smgr_main = device->getSceneManager();
1274 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1279 - An unit cube is centered at 0,0,0
1280 - Camera looks at cube from Y+, Z- towards Y-, Z+
1281 NOTE: Cube has to be changed to something else because
1282 the textures cannot be set individually (or can they?)
1285 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1286 v3f(0,0,0), v3f(0, 45, 0));
1287 // Set texture of cube
1288 cube->setMaterialTexture(0, texture_top);
1289 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1290 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1291 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1293 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1294 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1295 // Set orthogonal projection
1296 core::CMatrix4<f32> pm;
1297 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1298 camera->setProjectionMatrix(pm, true);
1300 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1301 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1303 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1306 driver->beginScene(true, true, video::SColor(0,0,0,0));
1310 // NOTE: The scene nodes should not be dropped, otherwise
1311 // smgr->drop() segfaults
1315 // Drop scene manager
1318 // Unset render target
1319 driver->setRenderTarget(0, true, true, 0);
1321 //TODO: Free textures of images
1322 driver->removeTexture(texture_top);
1324 // Create image of render target
1325 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1329 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1333 image->copyTo(baseimg);
1340 infostream<<"generate_image(): Invalid "
1341 " modification: \""<<part_of_name<<"\""<<std::endl;
1348 void make_progressbar(float value, video::IImage *image)
1353 core::dimension2d<u32> size = image->getDimension();
1355 u32 barheight = size.Height/16;
1356 u32 barpad_x = size.Width/16;
1357 u32 barpad_y = size.Height/16;
1358 u32 barwidth = size.Width - barpad_x*2;
1359 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1361 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1363 video::SColor active(255,255,0,0);
1364 video::SColor inactive(255,0,0,0);
1365 for(u32 x0=0; x0<barwidth; x0++)
1372 u32 x = x0 + barpos.X;
1373 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1375 image->setPixel(x,y, *c);