Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+GNU Lesser General Public License for more details.
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "debug.h"
#include "main.h" // for g_settings
#include "filesys.h"
-#include "utility.h"
#include "settings.h"
#include "mesh.h"
#include <ICameraSceneNode.h>
#include "mapnode.h" // For texture atlas making
#include "nodedef.h" // For texture atlas making
#include "gamedef.h"
-#include "utility_string.h"
+#include "util/string.h"
+#include "util/container.h"
+#include "util/thread.h"
+#include "util/numeric.h"
/*
A cache from texture name to texture path
// Update new texture pointer and texture coordinates to an
// AtlasPointer based on it's texture id
void updateAP(AtlasPointer &ap);
+
+ bool isKnownSourceImage(const std::string &name)
+ {
+ bool is_known = false;
+ bool cache_found = m_source_image_existence.get(name, &is_known);
+ if(cache_found)
+ return is_known;
+ // Not found in cache; find out if a local file exists
+ is_known = (getTexturePath(name) != "");
+ m_source_image_existence.set(name, is_known);
+ return is_known;
+ }
// Processes queued texture requests from other threads.
// Shall be called from the main thread.
// This should be only accessed from the main thread
SourceImageCache m_sourcecache;
+ // Thread-safe cache of what source images are known (true = known)
+ MutexedMap<std::string, bool> m_source_image_existence;
+
// A texture id is index in this array.
// The first position contains a NULL texture.
core::array<SourceAtlasPointer> m_atlaspointer_cache;
// Overlay image on top of another image (used for cracks)
void overlay(video::IImage *image, video::IImage *overlay);
+// Draw an image on top of an another one, using the alpha channel of the
+// source image
+static void blit_with_alpha(video::IImage *src, video::IImage *dst,
+ v2s32 src_pos, v2s32 dst_pos, v2u32 size);
+
// Brighten image
void brighten(video::IImage *image);
// Parse a transform name
assert(get_current_thread_id() == m_main_thread);
m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
+ m_source_image_existence.set(name, true);
}
void TextureSource::rebuildImagesAndTextures()
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
// Create an image of the right size
- core::dimension2d<u32> atlas_dim(1024,1024);
+ core::dimension2d<u32> max_dim = driver->getMaxTextureSize();
+ core::dimension2d<u32> atlas_dim(2048,2048);
+ atlas_dim.Width = MYMIN(atlas_dim.Width, max_dim.Width);
+ atlas_dim.Height = MYMIN(atlas_dim.Height, max_dim.Height);
video::IImage *atlas_img =
driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
//assert(atlas_img);
const ContentFeatures &f = ndef->get(j);
for(u32 i=0; i<6; i++)
{
- std::string name = f.tname_tiles[i];
+ std::string name = f.tiledef[i].name;
sourcelist[name] = true;
}
}
infostream<<std::endl;
// Padding to disallow texture bleeding
+ // (16 needed if mipmapping is used; otherwise less will work too)
s32 padding = 16;
-
- s32 column_width = 256;
s32 column_padding = 16;
+ s32 column_width = 256; // Space for 16 pieces of 16x16 textures
/*
First pass: generate almost everything
*/
core::position2d<s32> pos_in_atlas(0,0);
+ pos_in_atlas.X = column_padding;
pos_in_atlas.Y = padding;
for(core::map<std::string, bool>::Iterator
core::dimension2d<u32> dim = img2->getDimension();
- // Don't add to atlas if image is large
- core::dimension2d<u32> max_size_in_atlas(32,32);
+ // Don't add to atlas if image is too large
+ core::dimension2d<u32> max_size_in_atlas(64,64);
if(dim.Width > max_size_in_atlas.Width
|| dim.Height > max_size_in_atlas.Height)
{
// Wrap columns and stop making atlas if atlas is full
if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
{
- if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
+ if(pos_in_atlas.X > (s32)atlas_dim.Width - column_width - column_padding){
errorstream<<"TextureSource::buildMainAtlas(): "
<<"Atlas is full, not adding more textures."
<<std::endl;
break;
}
pos_in_atlas.Y = padding;
- pos_in_atlas.X += column_width + column_padding;
+ pos_in_atlas.X += column_width + column_padding*2;
}
/*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
atlas_img->setPixel(x,dst_y,c);
}
+ for(u32 side=0; side<2; side++) // left and right
+ for(s32 x0=0; x0<column_padding; x0++)
+ for(s32 y0=-padding; y0<(s32)dim.Height+padding; y0++)
+ {
+ s32 dst_x;
+ s32 src_x;
+ if(side==0)
+ {
+ dst_x = x0 + pos_in_atlas.X + dim.Width*xwise_tiling;
+ src_x = pos_in_atlas.X + dim.Width*xwise_tiling - 1;
+ }
+ else
+ {
+ dst_x = -x0 + pos_in_atlas.X-1;
+ src_x = pos_in_atlas.X;
+ }
+ s32 y = y0 + pos_in_atlas.Y;
+ s32 src_y = MYMAX((int)pos_in_atlas.Y, MYMIN((int)pos_in_atlas.Y + (int)dim.Height - 1, y));
+ s32 dst_y = y;
+ video::SColor c = atlas_img->getPixel(src_x, src_y);
+ atlas_img->setPixel(dst_x,dst_y,c);
+ }
+
img2->drop();
/*
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
- image->copyToWithAlpha(baseimg, pos_to,
+ /*image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
- NULL);
+ NULL);*/
+ blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
// Drop image
image->drop();
}
It is an image with a number of cracking stages
horizontally tiled.
*/
- video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
+ video::IImage *img_crack = sourcecache->getOrLoad(
+ "crack_anylength.png", device);
if(img_crack && progression >= 0)
{
}
else
{
- img_crack_scaled->copyToWithAlpha(
+ /*img_crack_scaled->copyToWithAlpha(
baseimg,
v2s32(0,0),
core::rect<s32>(v2s32(0,0), dim_base),
- video::SColor(255,255,255,255));
+ video::SColor(255,255,255,255));*/
+ blit_with_alpha(img_crack_scaled, baseimg,
+ v2s32(0,0), v2s32(0,0), dim_base);
}
}
u32 h0 = stoi(sf.next(":"));
infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0);
- baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+ if(baseimg == NULL)
+ {
+ baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+ baseimg->fill(video::SColor(0,0,0,0));
+ }
while(sf.atend() == false)
{
u32 x = stoi(sf.next(","));
driver->createImage(video::ECF_A8R8G8B8, dim);
img->copyTo(img2);
img->drop();
- img2->copyToWithAlpha(baseimg, pos_base,
+ /*img2->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255),
- NULL);
+ NULL);*/
+ blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
img2->drop();
}
else
image->drop();
}
}
+ /*
+ [lowpart:percent:filename
+ Adds the lower part of a texture
+ */
+ else if(part_of_name.substr(0,9) == "[lowpart:")
+ {
+ Strfnd sf(part_of_name);
+ sf.next(":");
+ u32 percent = stoi(sf.next(":"));
+ std::string filename = sf.next(":");
+ //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
+
+ if(baseimg == NULL)
+ baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
+ video::IImage *img = sourcecache->getOrLoad(filename, device);
+ if(img)
+ {
+ core::dimension2d<u32> dim = img->getDimension();
+ core::position2d<s32> pos_base(0, 0);
+ video::IImage *img2 =
+ driver->createImage(video::ECF_A8R8G8B8, dim);
+ img->copyTo(img2);
+ img->drop();
+ core::position2d<s32> clippos(0, 0);
+ clippos.Y = dim.Height * (100-percent) / 100;
+ core::dimension2d<u32> clipdim = dim;
+ clipdim.Height = clipdim.Height * percent / 100 + 1;
+ core::rect<s32> cliprect(clippos, clipdim);
+ img2->copyToWithAlpha(baseimg, pos_base,
+ core::rect<s32>(v2s32(0,0), dim),
+ video::SColor(255,255,255,255),
+ &cliprect);
+ img2->drop();
+ }
+ }
+ /*
+ [verticalframe:N:I
+ Crops a frame of a vertical animation.
+ N = frame count, I = frame index
+ */
+ else if(part_of_name.substr(0,15) == "[verticalframe:")
+ {
+ Strfnd sf(part_of_name);
+ sf.next(":");
+ u32 frame_count = stoi(sf.next(":"));
+ u32 frame_index = stoi(sf.next(":"));
+
+ if(baseimg == NULL){
+ errorstream<<"generate_image(): baseimg!=NULL "
+ <<"for part_of_name=\""<<part_of_name
+ <<"\", cancelling."<<std::endl;
+ return false;
+ }
+
+ v2u32 frame_size = baseimg->getDimension();
+ frame_size.Y /= frame_count;
+
+ video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
+ frame_size);
+ if(!img){
+ errorstream<<"generate_image(): Could not create image "
+ <<"for part_of_name=\""<<part_of_name
+ <<"\", cancelling."<<std::endl;
+ return false;
+ }
+
+ // Fill target image with transparency
+ img->fill(video::SColor(0,0,0,0));
+
+ core::dimension2d<u32> dim = frame_size;
+ core::position2d<s32> pos_dst(0, 0);
+ core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
+ baseimg->copyToWithAlpha(img, pos_dst,
+ core::rect<s32>(pos_src, dim),
+ video::SColor(255,255,255,255),
+ NULL);
+ // Replace baseimg
+ baseimg->drop();
+ baseimg = img;
+ }
else
{
errorstream<<"generate_image(): Invalid "
}
}
+/*
+ Draw an image on top of an another one, using the alpha channel of the
+ source image
+
+ This exists because IImage::copyToWithAlpha() doesn't seem to always
+ work.
+*/
+static void blit_with_alpha(video::IImage *src, video::IImage *dst,
+ v2s32 src_pos, v2s32 dst_pos, v2u32 size)
+{
+ for(u32 y0=0; y0<size.Y; y0++)
+ for(u32 x0=0; x0<size.X; x0++)
+ {
+ s32 src_x = src_pos.X + x0;
+ s32 src_y = src_pos.Y + y0;
+ s32 dst_x = dst_pos.X + x0;
+ s32 dst_y = dst_pos.Y + y0;
+ video::SColor src_c = src->getPixel(src_x, src_y);
+ video::SColor dst_c = dst->getPixel(dst_x, dst_y);
+ dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+ dst->setPixel(dst_x, dst_y, dst_c);
+ }
+}
+
void brighten(video::IImage *image)
{
if(image == NULL)