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
27 A cache from texture name to texture path
29 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
32 Replaces the filename extension.
34 std::string image = "a/image.png"
35 replace_ext(image, "jpg")
36 -> image = "a/image.jpg"
37 Returns true on success.
39 static bool replace_ext(std::string &path, const char *ext)
43 // Find place of last dot, fail if \ or / found.
45 for(s32 i=path.size()-1; i>=0; i--)
53 if(path[i] == '\\' || path[i] == '/')
56 // If not found, return an empty string
59 // Else make the new path
60 path = path.substr(0, last_dot_i+1) + ext;
65 Find out the full path of an image by trying different filename
70 static std::string getImagePath(std::string path)
72 // A NULL-ended list of possible image extensions
73 const char *extensions[] = {
74 "png", "jpg", "bmp", "tga",
75 "pcx", "ppm", "psd", "wal", "rgb",
79 const char **ext = extensions;
81 bool r = replace_ext(path, *ext);
84 if(fs::PathExists(path))
87 while((++ext) != NULL);
93 Gets the path to a texture by first checking if the texture exists
94 in texture_path and if not, using the data path.
96 Checks all supported extensions by replacing the original extension.
98 If not found, returns "".
100 Utilizes a thread-safe cache.
102 std::string getTexturePath(const std::string &filename)
104 std::string fullpath = "";
108 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
113 Check from texture_path
115 std::string texture_path = g_settings.get("texture_path");
116 if(texture_path != "")
118 std::string testpath = texture_path + '/' + filename;
119 // Check all filename extensions. Returns "" if not found.
120 fullpath = getImagePath(testpath);
124 Check from default data directory
128 std::string testpath = porting::getDataPath(filename.c_str());
129 // Check all filename extensions. Returns "" if not found.
130 fullpath = getImagePath(testpath);
133 // Add to cache (also an empty result is cached)
134 g_texturename_to_path_cache.set(filename, fullpath);
144 TextureSource::TextureSource(IrrlichtDevice *device):
146 m_main_atlas_image(NULL),
147 m_main_atlas_texture(NULL)
151 m_atlaspointer_cache_mutex.Init();
153 m_main_thread = get_current_thread_id();
155 // Add a NULL AtlasPointer as the first index, named ""
156 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
157 m_name_to_id[""] = 0;
159 // Build main texture atlas
160 if(g_settings.getBool("enable_texture_atlas"))
163 dstream<<"INFO: Not building texture atlas."<<std::endl;
166 TextureSource::~TextureSource()
170 void TextureSource::processQueue()
175 if(m_get_texture_queue.size() > 0)
177 GetRequest<std::string, u32, u8, u8>
178 request = m_get_texture_queue.pop();
180 dstream<<"INFO: TextureSource::processQueue(): "
181 <<"got texture request with "
182 <<"name=\""<<request.key<<"\""
185 GetResult<std::string, u32, u8, u8>
187 result.key = request.key;
188 result.callers = request.callers;
189 result.item = getTextureIdDirect(request.key);
191 request.dest->push_back(result);
195 u32 TextureSource::getTextureId(const std::string &name)
197 //dstream<<"INFO: getTextureId(): \""<<name<<"\""<<std::endl;
201 See if texture already exists
203 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
204 core::map<std::string, u32>::Node *n;
205 n = m_name_to_id.find(name);
208 return n->getValue();
215 if(get_current_thread_id() == m_main_thread)
217 return getTextureIdDirect(name);
221 dstream<<"INFO: getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
223 // We're gonna ask the result to be put into here
224 ResultQueue<std::string, u32, u8, u8> result_queue;
226 // Throw a request in
227 m_get_texture_queue.add(name, 0, 0, &result_queue);
229 dstream<<"INFO: Waiting for texture from main thread, name=\""
230 <<name<<"\""<<std::endl;
234 // Wait result for a second
235 GetResult<std::string, u32, u8, u8>
236 result = result_queue.pop_front(1000);
238 // Check that at least something worked OK
239 assert(result.key == name);
243 catch(ItemNotFoundException &e)
245 dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
250 dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
255 // Draw a progress bar on the image
256 void make_progressbar(float value, video::IImage *image);
259 Generate image based on a string like "stone.png" or "[crack0".
260 if baseimg is NULL, it is created. Otherwise stuff is made on it.
262 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
263 IrrlichtDevice *device);
266 Generates an image from a full string like
267 "stone.png^mineral_coal.png^[crack0".
269 This is used by buildMainAtlas().
271 video::IImage* generate_image_from_scratch(std::string name,
272 IrrlichtDevice *device);
275 This method generates all the textures
277 u32 TextureSource::getTextureIdDirect(const std::string &name)
279 //dstream<<"INFO: getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
281 // Empty name means texture 0
284 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
289 Calling only allowed from main thread
291 if(get_current_thread_id() != m_main_thread)
293 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
294 "called not from main thread"<<std::endl;
299 See if texture already exists
302 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
304 core::map<std::string, u32>::Node *n;
305 n = m_name_to_id.find(name);
308 dstream<<"INFO: getTextureIdDirect(): \""<<name
309 <<"\" found in cache"<<std::endl;
310 return n->getValue();
314 dstream<<"INFO: getTextureIdDirect(): \""<<name
315 <<"\" NOT found in cache. Creating it."<<std::endl;
321 char separator = '^';
324 This is set to the id of the base image.
325 If left 0, there is no base image and a completely new image
328 u32 base_image_id = 0;
330 // Find last meta separator in name
331 s32 last_separator_position = -1;
332 for(s32 i=name.size()-1; i>=0; i--)
334 if(name[i] == separator)
336 last_separator_position = i;
341 If separator was found, construct the base name and make the
342 base image using a recursive call
344 std::string base_image_name;
345 if(last_separator_position != -1)
347 // Construct base name
348 base_image_name = name.substr(0, last_separator_position);
349 /*dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
350 " to get base image of \""<<name<<"\" = \""
351 <<base_image_name<<"\""<<std::endl;*/
352 base_image_id = getTextureIdDirect(base_image_name);
355 //dstream<<"base_image_id="<<base_image_id<<std::endl;
357 video::IVideoDriver* driver = m_device->getVideoDriver();
360 video::ITexture *t = NULL;
363 An image will be built from files and then converted into a texture.
365 video::IImage *baseimg = NULL;
367 // If a base image was found, copy it to baseimg
368 if(base_image_id != 0)
370 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
372 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
374 video::IImage *image = ap.atlas_img;
378 dstream<<"WARNING: getTextureIdDirect(): NULL image in "
379 <<"cache: \""<<base_image_name<<"\""
384 core::dimension2d<u32> dim = ap.intsize;
386 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
388 core::position2d<s32> pos_to(0,0);
389 core::position2d<s32> pos_from = ap.intpos;
393 v2s32(0,0), // position in target
394 core::rect<s32>(pos_from, dim) // from
397 /*dstream<<"INFO: getTextureIdDirect(): Loaded \""
398 <<base_image_name<<"\" from image cache"
404 Parse out the last part of the name of the image and act
408 std::string last_part_of_name = name.substr(last_separator_position+1);
409 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
411 // Generate image according to part of name
412 if(generate_image(last_part_of_name, baseimg, m_device) == false)
414 dstream<<"INFO: getTextureIdDirect(): "
415 "failed to generate \""<<last_part_of_name<<"\""
419 // If no resulting image, print a warning
422 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
423 " create texture \""<<name<<"\""<<std::endl;
428 // Create texture from resulting image
429 t = driver->addTexture(name.c_str(), baseimg);
433 Add texture to caches (add NULL textures too)
436 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
438 u32 id = m_atlaspointer_cache.size();
444 core::dimension2d<u32> baseimg_dim(0,0);
446 baseimg_dim = baseimg->getDimension();
447 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
448 m_atlaspointer_cache.push_back(nap);
449 m_name_to_id.insert(name, id);
451 /*dstream<<"INFO: getTextureIdDirect(): "
452 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
457 std::string TextureSource::getTextureName(u32 id)
459 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
461 if(id >= m_atlaspointer_cache.size())
463 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
464 <<" >= m_atlaspointer_cache.size()="
465 <<m_atlaspointer_cache.size()<<std::endl;
469 return m_atlaspointer_cache[id].name;
473 AtlasPointer TextureSource::getTexture(u32 id)
475 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
477 if(id >= m_atlaspointer_cache.size())
478 return AtlasPointer(0, NULL);
480 return m_atlaspointer_cache[id].a;
483 void TextureSource::buildMainAtlas()
485 dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
487 //return; // Disable (for testing)
489 video::IVideoDriver* driver = m_device->getVideoDriver();
492 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
494 // Create an image of the right size
495 core::dimension2d<u32> atlas_dim(1024,1024);
496 video::IImage *atlas_img =
497 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
499 if(atlas_img == NULL)
501 dstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
502 "image; not building texture atlas."<<std::endl;
507 A list of stuff to include in the texture atlas.
509 It is a single-dimensional texture atlas due to the need to tile
512 It should contain as much of the stuff shown in game as possible,
513 to minimize texture changes.
515 It fills up quickly, so do not add anything that isn't contained
516 in most MapBlocks. E.g. mese isn't suitable but stone is.
519 core::array<std::string> sourcelist;
521 sourcelist.push_back("stone.png");
522 sourcelist.push_back("mud.png");
523 sourcelist.push_back("sand.png");
524 sourcelist.push_back("grass.png");
525 sourcelist.push_back("grass_footsteps.png");
526 sourcelist.push_back("tree.png");
527 sourcelist.push_back("tree_top.png");
528 sourcelist.push_back("water.png");
529 sourcelist.push_back("leaves.png");
530 sourcelist.push_back("glass.png");
531 sourcelist.push_back("mud.png^grass_side.png");
532 sourcelist.push_back("cobble.png");
533 sourcelist.push_back("mossycobble.png");
534 sourcelist.push_back("gravel.png");
535 sourcelist.push_back("jungletree.png");
537 sourcelist.push_back("stone.png^mineral_coal.png");
538 sourcelist.push_back("stone.png^mineral_iron.png");
540 // Padding to disallow texture bleeding
544 First pass: generate almost everything
546 core::position2d<s32> pos_in_atlas(0,0);
548 pos_in_atlas.Y += padding;
550 for(u32 i=0; i<sourcelist.size(); i++)
552 std::string name = sourcelist[i];
554 /*video::IImage *img = driver->createImageFromFile(
555 getTexturePath(name.c_str()).c_str());
559 core::dimension2d<u32> dim = img->getDimension();
560 // Make a copy with the right color format
561 video::IImage *img2 =
562 driver->createImage(video::ECF_A8R8G8B8, dim);
566 // Generate image by name
567 video::IImage *img2 = generate_image_from_scratch(name, m_device);
570 dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
574 core::dimension2d<u32> dim = img2->getDimension();
576 // Don't add to atlas if image is large
577 core::dimension2d<u32> max_size_in_atlas(32,32);
578 if(dim.Width > max_size_in_atlas.Width
579 || dim.Height > max_size_in_atlas.Height)
581 dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
582 <<"\""<<name<<"\" because image is large"<<std::endl;
586 // Stop making atlas if atlas is full
587 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
589 dstream<<"WARNING: TextureSource::buildMainAtlas(): "
590 <<"Atlas is full, not adding more textures."
595 dstream<<"INFO: TextureSource::buildMainAtlas(): Adding \""<<name
596 <<"\" to texture atlas"<<std::endl;
598 // Tile it a few times in the X direction
599 u16 xwise_tiling = 16;
600 for(u32 j=0; j<xwise_tiling; j++)
602 // Copy the copy to the atlas
603 img2->copyToWithAlpha(atlas_img,
604 pos_in_atlas + v2s32(j*dim.Width,0),
605 core::rect<s32>(v2s32(0,0), dim),
606 video::SColor(255,255,255,255),
610 // Copy the borders a few times to disallow texture bleeding
611 for(u32 side=0; side<2; side++) // top and bottom
612 for(s32 y0=0; y0<padding; y0++)
613 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
619 dst_y = y0 + pos_in_atlas.Y + dim.Height;
620 src_y = pos_in_atlas.Y + dim.Height - 1;
624 dst_y = -y0 + pos_in_atlas.Y-1;
625 src_y = pos_in_atlas.Y;
627 s32 x = x0 + pos_in_atlas.X * dim.Width;
628 video::SColor c = atlas_img->getPixel(x, src_y);
629 atlas_img->setPixel(x,dst_y,c);
635 Add texture to caches
639 u32 id = m_atlaspointer_cache.size();
641 // Create AtlasPointer
643 ap.atlas = NULL; // Set on the second pass
644 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
645 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
646 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
647 (float)dim.Width/(float)atlas_dim.Height);
648 ap.tiled = xwise_tiling;
650 // Create SourceAtlasPointer and add to containers
651 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
652 m_atlaspointer_cache.push_back(nap);
653 m_name_to_id.insert(name, id);
655 // Increment position
656 pos_in_atlas.Y += dim.Height + padding * 2;
662 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
666 Second pass: set texture pointer in generated AtlasPointers
668 for(u32 i=0; i<sourcelist.size(); i++)
670 std::string name = sourcelist[i];
671 if(m_name_to_id.find(name) == NULL)
673 u32 id = m_name_to_id[name];
674 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
675 m_atlaspointer_cache[id].a.atlas = t;
679 Write image to file so that it can be inspected
681 /*driver->writeImageToFile(atlas_img,
682 getTexturePath("main_atlas.png").c_str());*/
685 video::IImage* generate_image_from_scratch(std::string name,
686 IrrlichtDevice *device)
688 /*dstream<<"INFO: generate_image_from_scratch(): "
689 "\""<<name<<"\""<<std::endl;*/
691 video::IVideoDriver* driver = device->getVideoDriver();
698 video::IImage *baseimg = NULL;
700 char separator = '^';
702 // Find last meta separator in name
703 s32 last_separator_position = -1;
704 for(s32 i=name.size()-1; i>=0; i--)
706 if(name[i] == separator)
708 last_separator_position = i;
713 /*dstream<<"INFO: generate_image_from_scratch(): "
714 <<"last_separator_position="<<last_separator_position
718 If separator was found, construct the base name and make the
719 base image using a recursive call
721 std::string base_image_name;
722 if(last_separator_position != -1)
724 // Construct base name
725 base_image_name = name.substr(0, last_separator_position);
726 /*dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
727 " to get base image of \""<<name<<"\" = \""
728 <<base_image_name<<"\""<<std::endl;*/
729 baseimg = generate_image_from_scratch(base_image_name, device);
733 Parse out the last part of the name of the image and act
737 std::string last_part_of_name = name.substr(last_separator_position+1);
738 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
740 // Generate image according to part of name
741 if(generate_image(last_part_of_name, baseimg, device) == false)
743 dstream<<"INFO: generate_image_from_scratch(): "
744 "failed to generate \""<<last_part_of_name<<"\""
752 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
753 IrrlichtDevice *device)
755 video::IVideoDriver* driver = device->getVideoDriver();
758 // Stuff starting with [ are special commands
759 if(part_of_name[0] != '[')
761 // A normal texture; load it from a file
762 std::string path = getTexturePath(part_of_name.c_str());
763 /*dstream<<"INFO: generate_image(): Loading path \""<<path
766 video::IImage *image = driver->createImageFromFile(path.c_str());
770 dstream<<"WARNING: generate_image(): Could not load image \""
771 <<part_of_name<<"\" from path \""<<path<<"\""
772 <<" while building texture"<<std::endl;
776 dstream<<"WARNING: generate_image(): Creating a dummy"
777 <<" image for \""<<part_of_name<<"\""<<std::endl;
779 // Just create a dummy image
780 //core::dimension2d<u32> dim(2,2);
781 core::dimension2d<u32> dim(1,1);
782 image = driver->createImage(video::ECF_A8R8G8B8, dim);
784 /*image->setPixel(0,0, video::SColor(255,255,0,0));
785 image->setPixel(1,0, video::SColor(255,0,255,0));
786 image->setPixel(0,1, video::SColor(255,0,0,255));
787 image->setPixel(1,1, video::SColor(255,255,0,255));*/
788 image->setPixel(0,0, video::SColor(255,myrand()%256,
789 myrand()%256,myrand()%256));
790 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
791 myrand()%256,myrand()%256));
792 image->setPixel(0,1, video::SColor(255,myrand()%256,
793 myrand()%256,myrand()%256));
794 image->setPixel(1,1, video::SColor(255,myrand()%256,
795 myrand()%256,myrand()%256));*/
798 // If base image is NULL, load as base.
801 //dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
803 Copy it this way to get an alpha channel.
804 Otherwise images with alpha cannot be blitted on
805 images that don't have alpha in the original file.
807 core::dimension2d<u32> dim = image->getDimension();
808 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
809 image->copyTo(baseimg);
812 // Else blit on base.
815 //dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
816 // Size of the copied area
817 core::dimension2d<u32> dim = image->getDimension();
818 //core::dimension2d<u32> dim(16,16);
819 // Position to copy the blitted to in the base image
820 core::position2d<s32> pos_to(0,0);
821 // Position to copy the blitted from in the blitted image
822 core::position2d<s32> pos_from(0,0);
824 image->copyToWithAlpha(baseimg, pos_to,
825 core::rect<s32>(pos_from, dim),
826 video::SColor(255,255,255,255),
834 // A special texture modification
836 dstream<<"INFO: generate_image(): generating special "
837 <<"modification \""<<part_of_name<<"\""
841 This is the simplest of all; it just adds stuff to the
842 name so that a separate texture is created.
844 It is used to make textures for stuff that doesn't want
845 to implement getting the texture from a bigger texture
848 if(part_of_name == "[forcesingle")
853 Adds a cracking texture
855 else if(part_of_name.substr(0,6) == "[crack")
859 dstream<<"WARNING: generate_image(): baseimg==NULL "
860 <<"for part_of_name=\""<<part_of_name
861 <<"\", cancelling."<<std::endl;
865 // Crack image number
866 u16 progression = stoi(part_of_name.substr(6));
868 // Size of the base image
869 core::dimension2d<u32> dim_base = baseimg->getDimension();
874 It is an image with a number of cracking stages
877 video::IImage *img_crack = driver->createImageFromFile(
878 getTexturePath("crack.png").c_str());
882 // Dimension of original image
883 core::dimension2d<u32> dim_crack
884 = img_crack->getDimension();
885 // Count of crack stages
886 u32 crack_count = dim_crack.Height / dim_crack.Width;
888 if(progression > crack_count-1)
889 progression = crack_count-1;
890 // Dimension of a single scaled crack stage
891 core::dimension2d<u32> dim_crack_scaled_single(
895 // Dimension of scaled size
896 core::dimension2d<u32> dim_crack_scaled(
897 dim_crack_scaled_single.Width,
898 dim_crack_scaled_single.Height * crack_count
900 // Create scaled crack image
901 video::IImage *img_crack_scaled = driver->createImage(
902 video::ECF_A8R8G8B8, dim_crack_scaled);
905 // Scale crack image by copying
906 img_crack->copyToScaling(img_crack_scaled);
908 // Position to copy the crack from
909 core::position2d<s32> pos_crack_scaled(
911 dim_crack_scaled_single.Height * progression
914 // This tiling does nothing currently but is useful
915 for(u32 y0=0; y0<dim_base.Height
916 / dim_crack_scaled_single.Height; y0++)
917 for(u32 x0=0; x0<dim_base.Width
918 / dim_crack_scaled_single.Width; x0++)
920 // Position to copy the crack to in the base image
921 core::position2d<s32> pos_base(
922 x0*dim_crack_scaled_single.Width,
923 y0*dim_crack_scaled_single.Height
925 // Rectangle to copy the crack from on the scaled image
926 core::rect<s32> rect_crack_scaled(
928 dim_crack_scaled_single
931 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
933 video::SColor(255,255,255,255),
937 img_crack_scaled->drop();
944 [combine:WxH:X,Y=filename:X,Y=filename2
945 Creates a bigger texture from an amount of smaller ones
947 else if(part_of_name.substr(0,8) == "[combine")
949 Strfnd sf(part_of_name);
951 u32 w0 = stoi(sf.next("x"));
952 u32 h0 = stoi(sf.next(":"));
953 dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
954 core::dimension2d<u32> dim(w0,h0);
955 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
956 while(sf.atend() == false)
958 u32 x = stoi(sf.next(","));
959 u32 y = stoi(sf.next("="));
960 std::string filename = sf.next(":");
961 dstream<<"INFO: Adding \""<<filename
962 <<"\" to combined ("<<x<<","<<y<<")"
964 video::IImage *img = driver->createImageFromFile(
965 getTexturePath(filename.c_str()).c_str());
968 core::dimension2d<u32> dim = img->getDimension();
969 dstream<<"INFO: Size "<<dim.Width
970 <<"x"<<dim.Height<<std::endl;
971 core::position2d<s32> pos_base(x, y);
972 video::IImage *img2 =
973 driver->createImage(video::ECF_A8R8G8B8, dim);
976 img2->copyToWithAlpha(baseimg, pos_base,
977 core::rect<s32>(v2s32(0,0), dim),
978 video::SColor(255,255,255,255),
984 dstream<<"WARNING: img==NULL"<<std::endl;
990 Adds a progress bar, 0.0 <= N <= 1.0
992 else if(part_of_name.substr(0,12) == "[progressbar")
996 dstream<<"WARNING: generate_image(): baseimg==NULL "
997 <<"for part_of_name=\""<<part_of_name
998 <<"\", cancelling."<<std::endl;
1002 float value = stof(part_of_name.substr(12));
1003 make_progressbar(value, baseimg);
1006 "[noalpha:filename.png"
1007 Use an image without it's alpha channel.
1008 Used for the leaves texture when in old leaves mode, so
1009 that the transparent parts don't look completely black
1010 when simple alpha channel is used for rendering.
1012 else if(part_of_name.substr(0,8) == "[noalpha")
1016 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1017 <<"for part_of_name=\""<<part_of_name
1018 <<"\", cancelling."<<std::endl;
1022 std::string filename = part_of_name.substr(9);
1024 std::string path = getTexturePath(filename.c_str());
1026 dstream<<"INFO: generate_image(): Loading path \""<<path
1029 video::IImage *image = driver->createImageFromFile(path.c_str());
1033 dstream<<"WARNING: generate_image(): Loading path \""
1034 <<path<<"\" failed"<<std::endl;
1038 core::dimension2d<u32> dim = image->getDimension();
1039 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1041 // Set alpha to full
1042 for(u32 y=0; y<dim.Height; y++)
1043 for(u32 x=0; x<dim.Width; x++)
1045 video::SColor c = image->getPixel(x,y);
1047 image->setPixel(x,y,c);
1050 image->copyTo(baseimg);
1056 [inventorycube{topimage{leftimage{rightimage
1057 In every subimage, replace ^ with &.
1058 Create an "inventory cube".
1059 NOTE: This should be used only on its own.
1060 Example (a grass block (not actually used in game):
1061 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1063 else if(part_of_name.substr(0,14) == "[inventorycube")
1067 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1068 <<"for part_of_name=\""<<part_of_name
1069 <<"\", cancelling."<<std::endl;
1073 str_replace_char(part_of_name, '&', '^');
1074 Strfnd sf(part_of_name);
1076 std::string imagename_top = sf.next("{");
1077 std::string imagename_left = sf.next("{");
1078 std::string imagename_right = sf.next("{");
1083 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1085 dstream<<"WARNING: generate_image(): EVDF_RENDER_TO_TARGET"
1086 " not supported. Creating fallback image"<<std::endl;
1087 baseimg = generate_image_from_scratch(
1088 imagename_top, device);
1094 //dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1095 core::dimension2d<u32> dim(w0,h0);
1097 // Generate images for the faces of the cube
1098 video::IImage *img_top = generate_image_from_scratch(
1099 imagename_top, device);
1100 video::IImage *img_left = generate_image_from_scratch(
1101 imagename_left, device);
1102 video::IImage *img_right = generate_image_from_scratch(
1103 imagename_right, device);
1104 assert(img_top && img_left && img_right);
1106 // TODO: Create textures from images
1107 video::ITexture *texture_top = driver->addTexture(
1108 (imagename_top + "__temp__").c_str(), img_top);
1109 assert(texture_top);
1116 // Create render target texture
1117 video::ITexture *rtt = NULL;
1118 std::string rtt_name = part_of_name + "_RTT";
1119 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1120 video::ECF_A8R8G8B8);
1123 // Set render target
1124 driver->setRenderTarget(rtt, true, true,
1125 video::SColor(0,0,0,0));
1127 // Get a scene manager
1128 scene::ISceneManager *smgr_main = device->getSceneManager();
1130 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1135 - An unit cube is centered at 0,0,0
1136 - Camera looks at cube from Y+, Z- towards Y-, Z+
1137 NOTE: Cube has to be changed to something else because
1138 the textures cannot be set individually (or can they?)
1141 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1142 v3f(0,0,0), v3f(0, 45, 0));
1143 // Set texture of cube
1144 cube->setMaterialTexture(0, texture_top);
1145 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1146 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1147 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1149 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1150 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1151 // Set orthogonal projection
1152 core::CMatrix4<f32> pm;
1153 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1154 camera->setProjectionMatrix(pm, true);
1156 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1157 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1159 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1162 driver->beginScene(true, true, video::SColor(0,0,0,0));
1166 // NOTE: The scene nodes should not be dropped, otherwise
1167 // smgr->drop() segfaults
1171 // Drop scene manager
1174 // Unset render target
1175 driver->setRenderTarget(0, true, true, 0);
1177 //TODO: Free textures of images
1178 driver->removeTexture(texture_top);
1180 // Create image of render target
1181 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1185 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1189 image->copyTo(baseimg);
1196 dstream<<"WARNING: generate_image(): Invalid "
1197 " modification: \""<<part_of_name<<"\""<<std::endl;
1204 void make_progressbar(float value, video::IImage *image)
1209 core::dimension2d<u32> size = image->getDimension();
1211 u32 barheight = size.Height/16;
1212 u32 barpad_x = size.Width/16;
1213 u32 barpad_y = size.Height/16;
1214 u32 barwidth = size.Width - barpad_x*2;
1215 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1217 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1219 video::SColor active(255,255,0,0);
1220 video::SColor inactive(255,0,0,0);
1221 for(u32 x0=0; x0<barwidth; x0++)
1228 u32 x = x0 + barpos.X;
1229 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1231 image->setPixel(x,y, *c);