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="<<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="
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="<<name
309 <<" found in cache"<<std::endl;
310 return n->getValue();
314 dstream<<"INFO: getTextureIdDirect(): name="<<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, name="<<base_image_name<<std::endl;
351 base_image_id = getTextureIdDirect(base_image_name);
354 dstream<<"base_image_id="<<base_image_id<<std::endl;
356 video::IVideoDriver* driver = m_device->getVideoDriver();
359 video::ITexture *t = NULL;
362 An image will be built from files and then converted into a texture.
364 video::IImage *baseimg = NULL;
366 // If a base image was found, copy it to baseimg
367 if(base_image_id != 0)
369 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
371 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
373 video::IImage *image = ap.atlas_img;
377 dstream<<"WARNING: getTextureIdDirect(): NULL image in "
378 <<"cache: \""<<base_image_name<<"\""
383 core::dimension2d<u32> dim = ap.intsize;
385 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
387 core::position2d<s32> pos_to(0,0);
388 core::position2d<s32> pos_from = ap.intpos;
392 v2s32(0,0), // position in target
393 core::rect<s32>(pos_from, dim) // from
396 dstream<<"INFO: getTextureIdDirect(): Loaded \""
397 <<base_image_name<<"\" from image cache"
403 Parse out the last part of the name of the image and act
407 std::string last_part_of_name = name.substr(last_separator_position+1);
408 dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
410 // Generate image according to part of name
411 if(generate_image(last_part_of_name, baseimg, m_device) == false)
413 dstream<<"INFO: getTextureIdDirect(): "
414 "failed to generate \""<<last_part_of_name<<"\""
418 // If no resulting image, print a warning
421 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
422 " create texture \""<<name<<"\""<<std::endl;
427 // Create texture from resulting image
428 t = driver->addTexture(name.c_str(), baseimg);
432 Add texture to caches (add NULL textures too)
435 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
437 u32 id = m_atlaspointer_cache.size();
443 core::dimension2d<u32> baseimg_dim(0,0);
445 baseimg_dim = baseimg->getDimension();
446 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
447 m_atlaspointer_cache.push_back(nap);
448 m_name_to_id.insert(name, id);
450 dstream<<"INFO: getTextureIdDirect(): name="<<name
451 <<": succesfully returning id="<<id<<std::endl;
456 std::string TextureSource::getTextureName(u32 id)
458 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
460 if(id >= m_atlaspointer_cache.size())
462 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
463 <<" >= m_atlaspointer_cache.size()="
464 <<m_atlaspointer_cache.size()<<std::endl;
468 return m_atlaspointer_cache[id].name;
472 AtlasPointer TextureSource::getTexture(u32 id)
474 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
476 if(id >= m_atlaspointer_cache.size())
477 return AtlasPointer(0, NULL);
479 return m_atlaspointer_cache[id].a;
482 void TextureSource::buildMainAtlas()
484 dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
486 //return; // Disable (for testing)
488 video::IVideoDriver* driver = m_device->getVideoDriver();
491 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
493 // Create an image of the right size
494 core::dimension2d<u32> atlas_dim(1024,1024);
495 video::IImage *atlas_img =
496 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
500 A list of stuff to add. This should contain as much of the
501 stuff shown in game as possible, to minimize texture changes.
504 core::array<std::string> sourcelist;
506 sourcelist.push_back("stone.png");
507 sourcelist.push_back("mud.png");
508 sourcelist.push_back("sand.png");
509 sourcelist.push_back("grass.png");
510 sourcelist.push_back("grass_footsteps.png");
511 sourcelist.push_back("tree.png");
512 sourcelist.push_back("tree_top.png");
513 sourcelist.push_back("water.png");
514 sourcelist.push_back("leaves.png");
515 sourcelist.push_back("glass.png");
516 sourcelist.push_back("mud.png^grass_side.png");
517 sourcelist.push_back("cobble.png");
518 sourcelist.push_back("mossycobble.png");
519 sourcelist.push_back("gravel.png");
521 sourcelist.push_back("stone.png^mineral_coal.png");
522 sourcelist.push_back("stone.png^mineral_iron.png");
523 sourcelist.push_back("mud.png^mineral_coal.png");
524 sourcelist.push_back("mud.png^mineral_iron.png");
525 sourcelist.push_back("sand.png^mineral_coal.png");
526 sourcelist.push_back("sand.png^mineral_iron.png");
528 // Padding to disallow texture bleeding
532 First pass: generate almost everything
534 core::position2d<s32> pos_in_atlas(0,0);
536 pos_in_atlas.Y += padding;
538 for(u32 i=0; i<sourcelist.size(); i++)
540 std::string name = sourcelist[i];
542 /*video::IImage *img = driver->createImageFromFile(
543 getTexturePath(name.c_str()).c_str());
547 core::dimension2d<u32> dim = img->getDimension();
548 // Make a copy with the right color format
549 video::IImage *img2 =
550 driver->createImage(video::ECF_A8R8G8B8, dim);
554 // Generate image by name
555 video::IImage *img2 = generate_image_from_scratch(name, m_device);
558 dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
562 core::dimension2d<u32> dim = img2->getDimension();
564 // Don't add to atlas if image is large
565 core::dimension2d<u32> max_size_in_atlas(32,32);
566 if(dim.Width > max_size_in_atlas.Width
567 || dim.Height > max_size_in_atlas.Height)
569 dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
570 <<"\""<<name<<"\" because image is large"<<std::endl;
574 // Stop making atlas if atlas is full
575 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
577 dstream<<"WARNING: TextureSource::buildMainAtlas(): "
578 <<"Atlas is full, not adding more textures."
583 // Tile it a few times in the X direction
584 u16 xwise_tiling = 16;
585 for(u32 j=0; j<xwise_tiling; j++)
587 // Copy the copy to the atlas
588 img2->copyToWithAlpha(atlas_img,
589 pos_in_atlas + v2s32(j*dim.Width,0),
590 core::rect<s32>(v2s32(0,0), dim),
591 video::SColor(255,255,255,255),
595 // Copy the borders a few times to disallow texture bleeding
596 for(u32 side=0; side<2; side++) // top and bottom
597 for(s32 y0=0; y0<padding; y0++)
598 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
604 dst_y = y0 + pos_in_atlas.Y + dim.Height;
605 src_y = pos_in_atlas.Y + dim.Height - 1;
609 dst_y = -y0 + pos_in_atlas.Y-1;
610 src_y = pos_in_atlas.Y;
612 s32 x = x0 + pos_in_atlas.X * dim.Width;
613 video::SColor c = atlas_img->getPixel(x, src_y);
614 atlas_img->setPixel(x,dst_y,c);
620 Add texture to caches
624 u32 id = m_atlaspointer_cache.size();
626 // Create AtlasPointer
628 ap.atlas = NULL; // Set on the second pass
629 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
630 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
631 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
632 (float)dim.Width/(float)atlas_dim.Height);
633 ap.tiled = xwise_tiling;
635 // Create SourceAtlasPointer and add to containers
636 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
637 m_atlaspointer_cache.push_back(nap);
638 m_name_to_id.insert(name, id);
640 // Increment position
641 pos_in_atlas.Y += dim.Height + padding * 2;
647 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
651 Second pass: set texture pointer in generated AtlasPointers
653 for(u32 i=0; i<sourcelist.size(); i++)
655 std::string name = sourcelist[i];
656 if(m_name_to_id.find(name) == NULL)
658 u32 id = m_name_to_id[name];
659 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
660 m_atlaspointer_cache[id].a.atlas = t;
664 Write image to file so that it can be inspected
666 /*driver->writeImageToFile(atlas_img,
667 getTexturePath("main_atlas.png").c_str());*/
670 video::IImage* generate_image_from_scratch(std::string name,
671 IrrlichtDevice *device)
673 dstream<<"INFO: generate_image_from_scratch(): "
674 "name="<<name<<std::endl;
676 video::IVideoDriver* driver = device->getVideoDriver();
683 video::IImage *baseimg = NULL;
685 char separator = '^';
687 // Find last meta separator in name
688 s32 last_separator_position = -1;
689 for(s32 i=name.size()-1; i>=0; i--)
691 if(name[i] == separator)
693 last_separator_position = i;
698 /*dstream<<"INFO: generate_image_from_scratch(): "
699 <<"last_separator_position="<<last_separator_position
703 If separator was found, construct the base name and make the
704 base image using a recursive call
706 std::string base_image_name;
707 if(last_separator_position != -1)
709 // Construct base name
710 base_image_name = name.substr(0, last_separator_position);
711 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
712 " to get base image, name="<<base_image_name<<std::endl;
713 baseimg = generate_image_from_scratch(base_image_name, device);
717 Parse out the last part of the name of the image and act
721 std::string last_part_of_name = name.substr(last_separator_position+1);
722 dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
724 // Generate image according to part of name
725 if(generate_image(last_part_of_name, baseimg, device) == false)
727 dstream<<"INFO: generate_image_from_scratch(): "
728 "failed to generate \""<<last_part_of_name<<"\""
736 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
737 IrrlichtDevice *device)
739 video::IVideoDriver* driver = device->getVideoDriver();
742 // Stuff starting with [ are special commands
743 if(part_of_name[0] != '[')
745 // A normal texture; load it from a file
746 std::string path = getTexturePath(part_of_name.c_str());
747 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
750 video::IImage *image = driver->createImageFromFile(path.c_str());
754 dstream<<"WARNING: Could not load image \""<<part_of_name
755 <<"\" from path \""<<path<<"\""
756 <<" while building texture"<<std::endl;
760 dstream<<"WARNING: Creating a dummy"<<" image for \""
761 <<part_of_name<<"\""<<std::endl;
763 // Just create a dummy image
764 //core::dimension2d<u32> dim(2,2);
765 core::dimension2d<u32> dim(1,1);
766 image = driver->createImage(video::ECF_A8R8G8B8, dim);
768 /*image->setPixel(0,0, video::SColor(255,255,0,0));
769 image->setPixel(1,0, video::SColor(255,0,255,0));
770 image->setPixel(0,1, video::SColor(255,0,0,255));
771 image->setPixel(1,1, video::SColor(255,255,0,255));*/
772 image->setPixel(0,0, video::SColor(255,myrand()%256,
773 myrand()%256,myrand()%256));
774 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
775 myrand()%256,myrand()%256));
776 image->setPixel(0,1, video::SColor(255,myrand()%256,
777 myrand()%256,myrand()%256));
778 image->setPixel(1,1, video::SColor(255,myrand()%256,
779 myrand()%256,myrand()%256));*/
782 // If base image is NULL, load as base.
785 dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
787 Copy it this way to get an alpha channel.
788 Otherwise images with alpha cannot be blitted on
789 images that don't have alpha in the original file.
791 core::dimension2d<u32> dim = image->getDimension();
792 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
793 image->copyTo(baseimg);
796 // Else blit on base.
799 dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
800 // Size of the copied area
801 core::dimension2d<u32> dim = image->getDimension();
802 //core::dimension2d<u32> dim(16,16);
803 // Position to copy the blitted to in the base image
804 core::position2d<s32> pos_to(0,0);
805 // Position to copy the blitted from in the blitted image
806 core::position2d<s32> pos_from(0,0);
808 image->copyToWithAlpha(baseimg, pos_to,
809 core::rect<s32>(pos_from, dim),
810 video::SColor(255,255,255,255),
818 // A special texture modification
820 dstream<<"INFO: getTextureIdDirect(): generating special "
821 <<"modification \""<<part_of_name<<"\""
825 This is the simplest of all; it just adds stuff to the
826 name so that a separate texture is created.
828 It is used to make textures for stuff that doesn't want
829 to implement getting the texture from a bigger texture
832 if(part_of_name == "[forcesingle")
837 Adds a cracking texture
839 else if(part_of_name.substr(0,6) == "[crack")
843 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
844 <<"for part_of_name="<<part_of_name
845 <<", cancelling."<<std::endl;
849 // Crack image number
850 u16 progression = stoi(part_of_name.substr(6));
852 // Size of the base image
853 core::dimension2d<u32> dim_base = baseimg->getDimension();
858 It is an image with a number of cracking stages
861 video::IImage *img_crack = driver->createImageFromFile(
862 getTexturePath("crack.png").c_str());
866 // Dimension of original image
867 core::dimension2d<u32> dim_crack
868 = img_crack->getDimension();
869 // Count of crack stages
870 u32 crack_count = dim_crack.Height / dim_crack.Width;
872 if(progression > crack_count-1)
873 progression = crack_count-1;
874 // Dimension of a single scaled crack stage
875 core::dimension2d<u32> dim_crack_scaled_single(
879 // Dimension of scaled size
880 core::dimension2d<u32> dim_crack_scaled(
881 dim_crack_scaled_single.Width,
882 dim_crack_scaled_single.Height * crack_count
884 // Create scaled crack image
885 video::IImage *img_crack_scaled = driver->createImage(
886 video::ECF_A8R8G8B8, dim_crack_scaled);
889 // Scale crack image by copying
890 img_crack->copyToScaling(img_crack_scaled);
892 // Position to copy the crack from
893 core::position2d<s32> pos_crack_scaled(
895 dim_crack_scaled_single.Height * progression
898 // This tiling does nothing currently but is useful
899 for(u32 y0=0; y0<dim_base.Height
900 / dim_crack_scaled_single.Height; y0++)
901 for(u32 x0=0; x0<dim_base.Width
902 / dim_crack_scaled_single.Width; x0++)
904 // Position to copy the crack to in the base image
905 core::position2d<s32> pos_base(
906 x0*dim_crack_scaled_single.Width,
907 y0*dim_crack_scaled_single.Height
909 // Rectangle to copy the crack from on the scaled image
910 core::rect<s32> rect_crack_scaled(
912 dim_crack_scaled_single
915 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
917 video::SColor(255,255,255,255),
921 img_crack_scaled->drop();
928 [combine:WxH:X,Y=filename:X,Y=filename2
929 Creates a bigger texture from an amount of smaller ones
931 else if(part_of_name.substr(0,8) == "[combine")
933 Strfnd sf(part_of_name);
935 u32 w0 = stoi(sf.next("x"));
936 u32 h0 = stoi(sf.next(":"));
937 dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
938 core::dimension2d<u32> dim(w0,h0);
939 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
940 while(sf.atend() == false)
942 u32 x = stoi(sf.next(","));
943 u32 y = stoi(sf.next("="));
944 std::string filename = sf.next(":");
945 dstream<<"INFO: Adding \""<<filename
946 <<"\" to combined ("<<x<<","<<y<<")"
948 video::IImage *img = driver->createImageFromFile(
949 getTexturePath(filename.c_str()).c_str());
952 core::dimension2d<u32> dim = img->getDimension();
953 dstream<<"INFO: Size "<<dim.Width
954 <<"x"<<dim.Height<<std::endl;
955 core::position2d<s32> pos_base(x, y);
956 video::IImage *img2 =
957 driver->createImage(video::ECF_A8R8G8B8, dim);
960 img2->copyToWithAlpha(baseimg, pos_base,
961 core::rect<s32>(v2s32(0,0), dim),
962 video::SColor(255,255,255,255),
968 dstream<<"WARNING: img==NULL"<<std::endl;
974 Adds a progress bar, 0.0 <= N <= 1.0
976 else if(part_of_name.substr(0,12) == "[progressbar")
980 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
981 <<"for part_of_name="<<part_of_name
982 <<", cancelling."<<std::endl;
986 float value = stof(part_of_name.substr(12));
987 make_progressbar(value, baseimg);
990 "[noalpha:filename.png"
991 Use an image without it's alpha channel.
992 Used for the leaves texture when in old leaves mode, so
993 that the transparent parts don't look completely black
994 when simple alpha channel is used for rendering.
996 else if(part_of_name.substr(0,8) == "[noalpha")
1000 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
1001 <<"for part_of_name="<<part_of_name
1002 <<", cancelling."<<std::endl;
1006 std::string filename = part_of_name.substr(9);
1008 std::string path = getTexturePath(filename.c_str());
1010 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
1013 video::IImage *image = driver->createImageFromFile(path.c_str());
1017 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
1018 <<path<<"\" failed"<<std::endl;
1022 core::dimension2d<u32> dim = image->getDimension();
1023 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1025 // Set alpha to full
1026 for(u32 y=0; y<dim.Height; y++)
1027 for(u32 x=0; x<dim.Width; x++)
1029 video::SColor c = image->getPixel(x,y);
1031 image->setPixel(x,y,c);
1034 image->copyTo(baseimg);
1040 [inventorycube{topimage{leftimage{rightimage
1041 In every subimage, replace ^ with &.
1042 Create an "inventory cube".
1043 NOTE: This should be used only on its own.
1044 Example (a grass block (not actually used in game):
1045 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1047 else if(part_of_name.substr(0,14) == "[inventorycube")
1051 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
1052 <<"for part_of_name="<<part_of_name
1053 <<", cancelling."<<std::endl;
1057 str_replace_char(part_of_name, '&', '^');
1058 Strfnd sf(part_of_name);
1060 std::string imagename_top = sf.next("{");
1061 std::string imagename_left = sf.next("{");
1062 std::string imagename_right = sf.next("{");
1067 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1069 dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET"
1070 " not supported. Creating fallback image"<<std::endl;
1071 baseimg = generate_image_from_scratch(
1072 imagename_top, device);
1078 dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1079 core::dimension2d<u32> dim(w0,h0);
1081 // Generate images for the faces of the cube
1082 video::IImage *img_top = generate_image_from_scratch(
1083 imagename_top, device);
1084 video::IImage *img_left = generate_image_from_scratch(
1085 imagename_left, device);
1086 video::IImage *img_right = generate_image_from_scratch(
1087 imagename_right, device);
1088 assert(img_top && img_left && img_right);
1090 // TODO: Create textures from images
1091 video::ITexture *texture_top = driver->addTexture(
1092 (imagename_top + "__temp__").c_str(), img_top);
1093 assert(texture_top);
1100 // Create render target texture
1101 video::ITexture *rtt = NULL;
1102 std::string rtt_name = part_of_name + "_RTT";
1103 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1104 video::ECF_A8R8G8B8);
1107 // Set render target
1108 driver->setRenderTarget(rtt, true, true,
1109 video::SColor(0,0,0,0));
1111 // Get a scene manager
1112 scene::ISceneManager *smgr_main = device->getSceneManager();
1114 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1119 - An unit cube is centered at 0,0,0
1120 - Camera looks at cube from Y+, Z- towards Y-, Z+
1121 NOTE: Cube has to be changed to something else because
1122 the textures cannot be set individually (or can they?)
1125 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1126 v3f(0,0,0), v3f(0, 45, 0));
1127 // Set texture of cube
1128 cube->setMaterialTexture(0, texture_top);
1129 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1130 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1131 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1133 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1134 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1135 // Set orthogonal projection
1136 core::CMatrix4<f32> pm;
1137 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1138 camera->setProjectionMatrix(pm, true);
1140 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1141 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1143 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1146 driver->beginScene(true, true, video::SColor(0,0,0,0));
1150 // NOTE: The scene nodes should not be dropped, otherwise
1151 // smgr->drop() segfaults
1155 // Drop scene manager
1158 // Unset render target
1159 driver->setRenderTarget(0, true, true, 0);
1161 //TODO: Free textures of images
1162 driver->removeTexture(texture_top);
1164 // Create image of render target
1165 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1169 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1173 image->copyTo(baseimg);
1180 dstream<<"WARNING: getTextureIdDirect(): Invalid "
1181 " modification: \""<<part_of_name<<"\""<<std::endl;
1188 void make_progressbar(float value, video::IImage *image)
1193 core::dimension2d<u32> size = image->getDimension();
1195 u32 barheight = size.Height/16;
1196 u32 barpad_x = size.Width/16;
1197 u32 barpad_y = size.Height/16;
1198 u32 barwidth = size.Width - barpad_x*2;
1199 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1201 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1203 video::SColor active(255,255,0,0);
1204 video::SColor inactive(255,0,0,0);
1205 for(u32 x0=0; x0<barwidth; x0++)
1212 u32 x = x0 + barpos.X;
1213 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1215 image->setPixel(x,y, *c);